允许一个付费用户插入10万条任务,如果用户很多,那我的这两个表就会造成数据量过大,我想的是根据user_id来进行分表存储,16个表,user_id对16取余这样去存储更新和查询
<?php
namespace app\model;
use support\Model;
use support\Db;
/**
* 任务完成记录模型
*/
class TaskRecord extends Model
{
public function __construct(array $attributes = [])
{
$userId = session('user_id');
$data_table = $this->getTable();
$suffix = $userId % 16;
$this->table = 'ss_task_records_' . $suffix;
$result = Db::schema()->hasTable($this->table);
if (empty($result)) {
Db::update('create table ' . $this->table . ' like ' . $data_table);
}
parent::__construct($attributes);
}
实现方式基本思路是对的,即在模型的 __construct
方法里根据 user_id
动态切换表名,并自动建表。这种做法的优点是对调用方透明,无需修改大部分业务代码,确实可以极大简化分表迁移的工作量。
但这里有几点需要注意和优化,否则会有隐患:
1. session(‘user_id’) 的局限
- 你在
__construct
里用session('user_id')
,但模型实例化时不一定总有 session(如后台批量脚本、队列、定时任务、跨用户查询等)。 - 如果模型被用于跨用户查询(如统计、排行、批量操作),此时 session 里的 user_id 可能不是你想要的 user_id,甚至没有 session。
建议:
- 只在明确是“当前登录用户”操作时用 session,其他情况建议通过参数传递 user_id。
- 可以考虑提供一个静态工厂方法,如
TaskRecord::tableForUser($userId)
,让特殊场景下手动指定。
2. Eloquent 查询构造器的兼容性
- 你这样写后,
TaskRecord::where('user_id', $uid)->get()
这种代码会自动路由到分表,只要实例化时能拿到 user_id,确实无需大改。 - 但如果你有跨用户的查询,比如
TaskRecord::whereIn('user_id', [1,2,3])->get()
,这时只会查一张表,查不到其它分表的数据。
建议:
- 明确文档/注释,只支持单用户维度的查询,全局/跨用户聚合需特殊处理(如 union all)。
3. 自动建表的风险
- 你在
__construct
里自动建表,每次实例化模型都会检查和建表,这会带来性能损耗,且有并发建表的风险。 - 建议分表建表用脚本提前建好,不要在业务代码里自动建表。
4. 兼容性和扩展性
- 你现在的实现对 insert/update/delete/select 都有效,只要 user_id 能拿到,确实不用大改业务代码。
- 但如果后续要扩展为 32/64 张表,建议把分表数做成常量或配置项。
5. 推荐的改进写法
class TaskRecord extends Model
{
protected $table;
public function __construct(array $attributes = [], $userId = null)
{
$userId = $userId ?? session('user_id');
if (!$userId) {
throw new \Exception('user_id不能为空');
}
$suffix = $userId % 16;
$this->table = 'ss_task_records_' . $suffix;
parent::__construct($attributes);
}
// 静态工厂方法,适合批量/脚本/特殊场景
public static function tableForUser($userId)
{
$suffix = $userId % 16;
$instance = new static([], $userId);
$instance->setTable('ss_task_records_' . $suffix);
return $instance;
}
}
- 业务代码里大部分情况不用变,特殊场景用
TaskRecord::tableForUser($userId)
。
6. StarRecord 也建议同理实现
总结
- 你的实现对绝大多数“单用户”业务场景是没问题的,确实可以做到“对业务代码无感知”。
- 但要注意session依赖、自动建表性能、跨用户查询、分表数扩展等问题。
- 推荐提前建好分表,session获取不到时抛异常或用静态工厂方法传递 user_id。
如需分表建表脚本、分表助手Trait、批量查询方案等,可以随时补充。你的思路是对的,只需注意上述细节即可让系统更健壮!