You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Laravel查询优化:时间过滤时UUIDv7与created_at的对比

使用UUIDv7主键筛选近30天文章的性能优化方案

一、用UUIDv7的id字段过滤是否更高效?

是的,用id字段过滤通常比created_at更高效,核心原因有两点:

  • UUIDv7的前48位是毫秒级时间戳,生成的ID天然按时间有序,完全可以替代created_at实现时间范围筛选。
  • 主键字段默认带有聚簇索引(以InnoDB为例),聚簇索引的叶子节点直接存储整行数据,查询时无需额外回表;而created_at即使添加了普通索引,查询时大概率需要回表获取完整数据,性能不如主键索引。

如果created_at未建立索引,id过滤的性能优势会更显著;即便created_at有索引,主键聚簇索引的查询效率依然更优。

二、你的方案是否可行?

你提出的实现方案是可行的,但需要注意两个关键细节:

  1. 确认Laravel版本支持传入时间参数:Laravel 10及以上版本的Illuminate\Support\Uuid::uuid7()可以接收DateTimeInterface类型参数,生成对应时间点的UUIDv7,这样生成的oldestAllowedId就是30天前时刻的最小UUIDv7,后续生成的所有UUIDv7都会大于等于它,能准确筛选出近30天的文章。
  2. 保证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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 16:03:13