在thinkphp5.1中可以使用partition方法进行水平分表功能,但其分表功能较简单,不适用某些特殊场景。其在TP中的实现逻辑如下:
文件路径:thinkphp\library\think\db\Query.php (555行)
/**
* 得到分表的的数据表名
* @access public
* @param array $data 操作的数据
* @param string $field 分表依据的字段
* @param array $rule 分表规则
* @return array
*/
public function getPartitionTableName($data, $field, $rule = [])
{
// 对数据表进行分区
if ($field && isset($data[$field])) {
$value = $data[$field];
$type = $rule['type'];
switch ($type) {
case 'id':
// 按照id范围分表
$step = $rule['expr'];
$seq = floor($value / $step) + 1;
break;
case 'year':
// 按照年份分表
if (!is_numeric($value)) {
$value = strtotime($value);
}
$seq = date('Y', $value) - $rule['expr'] + 1;
break;
case 'mod':
// 按照id的模数分表
$seq = ($value % $rule['num']) + 1;
break;
case 'md5':
// 按照md5的序列分表
$seq = (ord(substr(md5($value), 0, 1)) % $rule['num']) + 1;
break;
default:
if (function_exists($type)) {
// 支持指定函数哈希
$value = $type($value);
}
$seq = (ord(substr($value, 0, 1)) % $rule['num']) + 1;
}
return $this->getTable() . '_' . $seq;
}
// 当设置的分表字段不在查询条件或者数据中
// 进行联合查询,必须设定 partition['num']
$tableName = [];
for ($i = 0; $i < $rule['num']; $i++) {
$tableName[] = 'SELECT * FROM ' . $this->getTable() . '_' . ($i + 1);
}
return ['( ' . implode(" UNION ", $tableName) . ' )' => $this->name];
}
仿照上面的实现逻辑,我实现了一个demo:
1.创建测试数据表
-- 用户信息
CREATE TABLE `t_user` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(100) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- 聊天内容
CREATE TABLE `t_chat` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`uid` int(10) unsigned NOT NULL,
`content` varchar(100) NOT NULL,
`create_time` datetime NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4;
2.application目录下场景traits目录,并在该目录下创建Partition
<?php
namespace app\traits;
trait Partition
{
/**
* 判断数据表是否存在
* @param string $tableName 表名
*/
protected function tableExists($tableName)
{
$result = $this->query("SHOW TABLES LIKE '{$tableName}'");
return !empty($result);
}
/**
* 创建分表
* @param string $tableName 分表表名
*/
protected function createPartitionTable($tableName)
{
$table = $this->getTable();
$result = $this->query("SHOW CREATE TABLE {$table}");
$createSql = $result[0]['Create Table'];
$createSql = str_replace($table, $tableName, $createSql);
$this->execute($createSql);
}
/**
* 设置分表的的数据表名
* 按业务、年月设置分表信息,进行后续CURD操作
* @param string $businessType 业务类型,
* @param string $startTime 开始时间,只传开始时间时用以insert、update、delete操作;同时传结束时间用以select操作
* @param string $endTime 结束时间
* @param string $alias 表名别名,用以select操作时各表union后的别名,默认使用模型的name属性
*/
public function partitionYm($businessType, $startTime, $endTime = null, $alias = null)
{
// 开始和结束时间都不传时返回自身
if (empty($startTime) && empty($endTime)) {
return $this;
}
if (empty($endTime)) {
$seq = date('Ym', strtotime($startTime));
$tableName = $this->getTable() . '_' . $businessType . '_' . $seq;
if (!$this->tableExists($tableName)) {
$this->createPartitionTable($tableName);
}
} else {
$startDate = new \DateTime($startTime);
$endDate = new \DateTime($endTime);
$endDate->modify('last day of this month');
$interval = $startDate->diff($endDate, true);
$startTime = strtotime($startTime);
$startYM = date('Ym', $startTime);
$tables = [];
for ($i = 0; $i <= $interval->m; $i++) {
$table = $this->getTable() . '_' . $businessType . '_' . $startYM;
if (!$this->tableExists($table)) {
continue;
}
$tables[] = "SELECT * FROM {$table}";
$startTime = strtotime("+1 month", $startTime);
$startYM = date('Ym', $startTime);
}
// 时间区间内没有相应的数据表时返回自身
$tableName = '(' . (empty($tables) ? 'SELECT * FROM ' . $this->getTable() : implode(" UNION ", $tables)) . ') ' . ($alias ?: $this->name);
dump($tableName);
}
$this->table = $tableName;
return $this;
}
}
3.Chat模型里使用Partition
模型路径:application\model\Chat.php
<?php
namespace app\model;
use app\traits\Partition;
class Chat extends Base
{
use Partition;
}
4.控制器使用Chat模型
控制器路径:application\index\controller\Index.php
<?php
namespace app\index\controller;
use app\model\Chat as ChatModel;
class Index
{
public $businessType = 1;
public function index()
{
$model = new ChatModel;
$res = $model->partitionYm($this->businessType, '2024-06-25', '2024-09-01', 'chat')
->leftJoin('user u', 'chat.uid = u.id')
->field(['chat.*', 'u.name'])
->order(['chat.create_time' => 'desc', 'chat.id' => 'desc'])
->select();
dump($res);
return 'index';
}
public function save()
{
$time = date('Y-m-d H:i:s');
$data = [
'uid' => 2,
'content' => 'think',
'create_time' => $time,
];
$model = new ChatModel;
$model->partitionYm($this->businessType, $time)->insert($data);
return 'save';
}
}
标签:tableName,实践,value,rule,startTime,分表,thinkphp5.1,id From: https://www.cnblogs.com/tros/p/18303135