laravel 单库分表问题
关于laravel单库分表
laravel在使用关联关系的时候,对分表支持的并不友好。比如处理hasOne hasMany with等方法都不能使用,而且网上提供的解决方案,都是改造这几个方法,这就涉及到分表的业务代码也需要一定的更改。
我有个想法,在laravel最后生成sql语句的时候,发送到mysql执行前,进行拦截,并对其进行重写
举例
1.logs表是根据用户id分表的,表名为logs_1 logs_2
2.那么我业务逻辑中无需修改,按单表使用。只是查询的时候必须加上筛选条件user_id=xx
3.生成的语句是select * from logs where user_id=1;
4.此时只把user_id的值提取到表名上,转换为select * from logs_1; 再发送给mysql执行,那么就解决了laravel分表的问题
有点类似于简易版数据库中间件,仅仅是为了解决单库分表,laravel关联关系不好用的问题。
请问这样可行吗?
如果可行,在哪里监听,重写sql呢?
或者,你有啥优雅的方式解决单库分表吗?
ps:一些开源的数据库中间件mycat、dble等,对laravel支持的并不友好,比如不支持子查询等等,业务改造起来很麻烦。
———————————————-实践——————————————–
我发现通过设置模型提供的setTable()方法就可以了。with hasMany等方法都可以使用
1.使用静态方法,给模型设置分表标识
2.重写模型中的getTable()方法
3.使用with等方法调用分表的模型时,先用静态方法设置下表标识,代码如下
namespace App\Contracts\SubTable;
use Illuminate\Database\Eloquent\Model;
abstract class SubTableAbstract extends Model
{
/**
* 表名前缀
*/
public static $prefixTable;
/**
* 分表标识
*/
public static $identity;
public function getTable()
{
return static::$prefixTable . static::$identity;
}
/**
* @inheritdoc
*/
public static function setTableIdentity(string $identity): void
{
static::$identity = $identity;
}
}
要分表的模型继承上述父类,且在模型中设置分表前缀,如
public static $prefixTable = "crm_customers_";
使用时,需要先设置分表标识
//假设crms表与crm_customers_xxx是一对多的关系,模型名分别是Crm、CrmCustomer
$user_id='123456';
CrmCustomer::setTableIdentity($user_id);
Crm::query->with('crmCustomers')->first();
以上初步测试是可以的,不知是否有其他的未知问题? 欢迎评论
以下是查询的方式
- 首先将需要查询的表的分表标识列出来
- 然后拼接各个表查询的sql
- 最后使用union 将各个sql拼接起来,作为一张临时表,这样就可以分页了
$queries = collect();
$userIds = [1,2,3,4,5]
foreach($userIds as $userId){
$tableName = 'customers_' . $userId;
$query = DB::table($tableName)
->select(['name', 'email', 'phone']);
// 可以拼接一些查询
// $query->where('phone','xxxx');
// 加入集合
queries->push($$query);
}
// 取出第一个作为查询对象,其他的作为合并对象
$unionQuery = $queries->shift();
$queries->each(function ($item) use ($unionQuery) {
$unionQuery->unionAll($item);
});
// 设置临时表,执行查询
(new Customer)->setTable('union_customers')->from(DB::raw("({$unionQuery->toSql()}) as union_customers"))
->mergeBindings($unionQuery)
->paginate()
可以打印以下最终生成的sql