Laravel查询优化:时间过滤时UUIDv7与created_at的对比
使用UUIDv7主键筛选近30天文章的性能优化方案
一、用UUIDv7的id字段过滤是否更高效?
是的,用id字段过滤通常比created_at更高效,核心原因有两点:
- UUIDv7的前48位是毫秒级时间戳,生成的ID天然按时间有序,完全可以替代
created_at实现时间范围筛选。 - 主键字段默认带有聚簇索引(以InnoDB为例),聚簇索引的叶子节点直接存储整行数据,查询时无需额外回表;而
created_at即使添加了普通索引,查询时大概率需要回表获取完整数据,性能不如主键索引。
如果created_at未建立索引,id过滤的性能优势会更显著;即便created_at有索引,主键聚簇索引的查询效率依然更优。
二、你的方案是否可行?
你提出的实现方案是可行的,但需要注意两个关键细节:
- 确认Laravel版本支持传入时间参数:Laravel 10及以上版本的
Illuminate\Support\Uuid::uuid7()可以接收DateTimeInterface类型参数,生成对应时间点的UUIDv7,这样生成的oldestAllowedId就是30天前时刻的最小UUIDv7,后续生成的所有UUIDv7都会大于等于它,能准确筛选出近30天的文章。 - 保证UUIDv7与
created_at时间同步:如果模型中created_at是自动填充的系统时间,而UUIDv7在创建时生成,两者时间基本一致;但如果存在手动修改created_at的场景,这种筛选方式会出现偏差——需要确保UUIDv7的生成时间和created_at严格同步,比如在模型的creating事件中用UUIDv7的时间设置created_at。
你的代码可以直接使用,这里优化一下时间精度处理,避免秒级/毫秒级差异导致漏数据:
// 获取30天前的起始时间(精确到毫秒) $thirtyDaysAgo = now()->subDays(30)->startOfDay(); // 生成对应时间的UUIDv7 $oldestAllowedId = Uuid::uuid7($thirtyDaysAgo)->toString(); // 用主键筛选近30天的文章 $qry->where('id', '>=', $oldestAllowedId);
三、最佳实现建议
- 验证UUIDv7时间对应关系:手动生成一个指定时间的UUIDv7,查询数据库中该时间前后的ID,确认筛选逻辑的准确性。
- 强制时间同步:在模型的
creating事件中,用UUIDv7的生成时间设置created_at,确保两者完全同步:protected static function booted() { static::creating(function ($model) { $uuid = Uuid::uuid7(); $model->id = $uuid->toString(); // 用UUIDv7的毫秒时间戳转换为DateTime对象设置created_at $model->created_at = DateTime::createFromFormat('U.u', sprintf('%.6F', $uuid->getTimestamp() / 1000)); }); } - 对比执行计划:通过
EXPLAIN语句对比两种筛选方式的执行计划,确认id过滤是否用到主键索引,直观查看性能差异:EXPLAIN SELECT * FROM articles WHERE id >= '018f1d7b-xxxx-xxxx-xxxx-xxxxxxxxxxxx'; EXPLAIN SELECT * FROM articles WHERE created_at >= '2024-01-01 00:00:00';
内容的提问来源于stack exchange,提问作者Putra Fajar Hasanuddin
相关产品推荐
相关产品推荐

