当数据多到一定的程度时,分表可作为一种提升性能的方案。
这里以sku
表为列子,分表的后并不用修改任何业务逻辑:
商品表:goods
,sku表:sku
, 根据good_id
取余数来分表。
原始语句
GoodModel::with('sku')->limit(10)->findAll();
这里用的with关联查询。分库分表往往涉及到改动的可能就是这种关联的地方,直接操作sku表都比较简单,这不介绍了,通过orm前置事件修改表名就可以了。
上面这是一对多,一个商品有多个sku hasMany
,这里其实只需要把一对多改成多态一对多就好了,上面的查询语句不用任何改动。orm的完整配置
sku模型
class Sku extends Model
{
const TABLE = 'sku_';
// 分表的数量
const TABLE_COUNT = 7;
// 这通过get前置事件,设置表名
// 其他增加,更改,删除都有相应的前置事件 同理
public function onBeforeGet($result, $args)
{
foreach ($result->getModelWhere() as $val) {
if ($val[0] === 'good_id') {
$result->from(self::TABLE . ($val[2][0] % self::TABLE_COUNT));
return;
}
}
// 这里处理条件里面没有good_id的情况
// 抛出错误 条件里面必须要带 good_id
}
}
goods 模型
class Goods extends Model
{
const TABLE = 'goods';
// 要用多态的关系,这里没有type字段,创建type虚拟字段,根据id取余数
public function onAfterGet($result, $args)
{
if (is_array($result)) { // findAll 的情况
foreach ($result as $v) {
$v->type = $v->id % Sku::TABLE_COUNT;
}
}else{ // find 的情况
$result->type = $result->id % Sku::TABLE_COUNT;
}
}
// 把关联关系一对多 改成多态一对多
function sku()
{
return $this->morphMany(
array_fill(0, Sku::TABLE_COUNT, Sku::class),
array_fill(0, Sku::TABLE_COUNT, 'good_id'),
'type', 'id');
}
}
好了,完整的修改就完成了。这里只配置查询的前置事件onBeforeGet
,同样把onBeforeDelete
,onBeforeInsert
,onBeforeUpdate
配置完成。业务调用层无任何改动就支持分表了。分表的数量也可以随时调整。
这里使用的是one框架自带的orm模型 github.com/lizhichao/one
如果需要分库$result->from()
改成 $result->setConnection($key)->...
就好了