订单搜索优化方案

核心目标:解决订单搜索(多条件、大数据量)慢问题,兼顾「查询性能」「业务适配」「开发成本」,核心思路:索引优化+查询逻辑优化+存储/架构优化,按优先级逐步落地。

一、先做低成本高收益:索引优化(优先级最高)

订单搜索慢80%是索引缺失/不合理,优先针对性建索引,无代码侵入,见效最快。

  1. 核心搜索字段索引(必建)

订单搜索高频字段: order_no (订单号)、 user_id (用户ID)、 order_status (订单状态)、 create_time (创建时间),按「单字段索引+联合索引」组合设计:

  • 单字段索引: idx_order_no (订单号,精确匹配为主,如搜索某订单号)、 idx_user_id (用户ID,查用户所有订单)
  • 联合索引(覆盖高频多条件搜索,遵循「左前缀原则」):
  •  idx_status_create ( order_status ,  create_time ):适配「按状态+时间筛选」(如待发货+近7天)
  •  idx_user_status ( user_id ,  order_status ):适配「按用户+状态筛选」(如用户A的待付款订单)
  •  idx_user_create ( user_id ,  create_time ):适配「按用户+时间筛选」(如用户A近30天订单)
  • 索引语法(MySQL):
    -- 单字段索引
    CREATE INDEX idx_order_no ON order(order_no);
    CREATE INDEX idx_user_id ON order(user_id);
    -- 联合索引
    CREATE INDEX idx_status_create ON order(order_status, create_time);
    CREATE INDEX idx_user_status ON order(user_id, order_status);
     
  1. 索引避坑要点
  • 不建冗余索引:如已有 idx_user_status_create (user_id,order_status,create_time),无需再建 idx_user_status 
  • 高基数字段放联合索引左前:如 user_id (基数高)比 order_status (基数低,仅待付/待发/完成等)优先
  • 避免过度索引:单表索引≤6个,过多会拖慢订单创建/更新速度

二、查询逻辑优化(开发成本低,见效快)

  1. 过滤条件优先用索引字段
  • 搜索时先通过「status/create_time/user_id」等索引字段缩小数据范围,再过滤非索引字段(如 consignee 收件人)
  • 反例:先查所有订单,再筛选收件人;正例:先按「待发+近7天」(索引字段)过滤,再匹配收件人
  1. 精准匹配优先,模糊匹配优化
  • 订单号搜索:用 = 精准匹配(走 idx_order_no 索引),避免 like %xxx% (索引失效);若需前缀匹配(如订单号前6位),用 like 'xxx%' (可走索引)
  • 收件人/手机号搜索:
  • 手机号:存明文/脱敏后,用 like '138%' (前缀匹配,建 idx_consignee_mobile 索引)
  • 收件人:避免全模糊,可做「分词+冗余字段」(如 consignee_pinyin 存拼音首字母,搜“张三”匹配“ZS%”)
  1. 分页与字段裁剪
  • 分页防深页:用 create_time+id 游标分页(代替 limit 10000,20 ,深页时索引失效),示例:
    // 游标分页:上一页最后一条的create_time和id
    $query->where(['>', 'create_time', $lastCreateTime])

    ->orWhere(['=', 'create_time', $lastCreateTime])
    ->andWhere(['>', 'id', $lastId])
    ->limit(20);

     

  • 字段裁剪:只查需要的字段(避免 select * ),联合索引可覆盖查询(如 idx_user_status 覆盖 user_id,status,id ,无需回表查主表)
  1. 避免查询无效数据
  • 过滤已删除订单( is_delete=0 ,可加进联合索引,如 idx_status_delete_create )
  • 时间范围不超过90天:默认只查近90天订单,历史订单提供「按年月筛选」入口(缩小范围)
  1. 批量查询防N+1
  • 查订单时关联「用户/商品」,用Yii2 with() 预加载,避免循环查关联数据:
    Order::find()->with(['user', 'orderGoods'])->where(...); // 预加载,仅2次查询
     

三、中成本优化:存储与冗余设计

  1. 历史订单分表(订单量≥100万条必做)
  • 按时间分表:近3个月订单存在 order 主表,历史订单按「年月」分表(如 order_202501 )
  • 分表策略:
  • 写入:新订单写主表,每月底定时将上月订单迁移到历史分表
  • 读取:近3个月查主表,历史订单查对应分表;跨表搜索用「分表聚合」(如UNION ALL,仅历史数据用)
  1. 冗余高频搜索字段
  • 非索引字段(如 consignee 收件人、 mobile 手机号)若高频搜索,可冗余到订单表(避免关联地址表)
  • 示例:订单表冗余 consignee (收件人)、 mobile (脱敏手机号),建 idx_consignee_mobile 索引,适配收件人/手机号前缀搜索
  1. 大字段拆分
  • 订单表中 order_desc (订单备注)、 ext_info (扩展信息)等大字段,拆分到 order_ext 表(一对一关联),减少主表数据量,提升查询速度

四、高成本高收益:架构层优化(订单量≥500万条)

  1. 引入ES(Elasticsearch)做全文检索
  • 适用场景:多字段混合搜索(如“用户A+待付款+近7天+收件人张三”)、全模糊搜索(如收件人“%三%”)
  • 实现方案:
  • 数据同步:订单创建/更新时,同步数据到ES索引(用MQ解耦,避免影响订单主流程)
  • 搜索查询:复杂搜索走ES,简单查询(订单号/用户ID)仍走MySQL,ES返回订单ID,再从MySQL查详情(ES+MySQL混合查询,兼顾速度与数据一致性)
  1. 读写分离
  • 订单创建/更新写主库,搜索查询读从库,分摊主库压力
  • 适配场景:QPS≥1000,主库压力大时,读从库可提升查询响应速度
  1. 热点数据缓存
  • 高频搜索数据(如用户近30天订单、热门状态订单)缓存到Redis,过期时间5-15分钟(根据订单更新频率调整)
  • 示例:缓存用户1001近30天订单,键 order:user:1001:30d ,值存订单ID列表,查询时先查缓存,无则查DB再回写缓存

五、落地优先级&效果预期

优化层级 具体方案 开发成本 性能提升 适用订单量
1级(必做) 核心索引优化+查询逻辑优化 低 50%-200% <100万
2级(推荐) 历史订单分表+字段冗余 中 200%-500% 100万-500万
3级(按需) ES全文检索+读写分离 高 500%+ ≥500万

六、监控与迭代

1. 监控慢查询:开启MySQL慢查询日志(阈值≥500ms),定期分析订单搜索慢SQL,针对性优化
2. 监控索引使用率:用 sys.schema_unused_indexes 查看未使用索引,及时删除冗余
3. 迭代优化:先落地1-2级方案,观察1-2周,若仍不满足需求,再推进ES等3级方案

按此方案落地,可解决大部分订单搜索慢问题,兼顾成本与效果,适配不同订单量规模的业务场景。

标签: none

添加新评论