注册
达梦数据库优化实战:如何让索引消除排序,提升查询性能
专栏/技术分享/ 文章详情 /

达梦数据库优化实战:如何让索引消除排序,提升查询性能

老罗 2025/11/28 107 0 0
摘要

在数据库查询优化中,ORDER BY 子句导致的排序操作往往是性能瓶颈之一。本文将通过一个实际案例,展示如何通过合理的索引设计来消除排序操作,显著提升查询性能。

场景介绍

我们有一个销售表 ITPUX_SALES,包含以下字段:

  • logtime:交易时间
  • cardid:卡号
  • amount:交易金额

业务需求:查询最近9年的销售记录,并按交易时间降序、卡号降序排列。

初始状态分析

初始查询语句:

select logtime, cardid, amount from ITPUX_SALES where logtime >= trunc(sysdate) - interval '9' year order by logtime desc, cardid desc;

此时表中只有一个单列索引 IDX_SALE_TIME(仅包含 logtime 字段),执行计划显示:

1 NSET2:[9, 2681->2677, 85]
2   PRJT2:[9, 2681->2677, 85];exp_num(4), is_atom(FALSE)
3     SORT3:[9, 2681->2677, 85];key_num(2), partition_key_num(1), is_distinct(FALSE), is_adaptive(0), MEM_USED(30720KB), DISK_USED(0KB)
4       BLKUP2:[8, 2681->2677, 85];IDX_SALE_TIME(ITPUX_SALES)
5         SSEK2:[8, 2681->2677, 85];scan_type(DESC), IDX_SALE_TIME(ITPUX_SALES), is_global(0), scan_range[exp11-exp12,max]

关键发现:执行计划中出现了 SORT3 操作符,说明数据库在进行显式排序,使用了30MB内存。

第一次优化:创建复合索引

我们创建包含两个排序字段的复合索引:

create index itpux_sales_log_card on ITPUX_SALES(logtime, cardid);

删除旧索引后重新执行查询,执行计划变为:

1 NSET2:[9, 2681->2677, 85]
2   PRJT2:[9, 2681->2677, 85];exp_num(4), is_atom(FALSE)
3     BLKUP2:[8, 2681->2677, 85];ITPUX_SALES_LOG_CARD(ITPUX_SALES)
4       SSEK2:[8, 2681->2677, 85];scan_type(DESC), ITPUX_SALES_LOG_CARD(ITPUX_SALES), is_global(0), scan_range[(exp11-exp12,min),(max,max))

优化效果SORT3 操作符消失了!数据库通过反向扫描索引直接获得了正确排序的结果。

问题再现:混合排序场景

现在修改查询,要求按交易时间降序、卡号升序排列:

select logtime, cardid, amount from ITPUX_SALES where logtime >= trunc(sysdate) - interval '9' year order by logtime desc, cardid; -- cardid 改为升序

执行计划显示:

1 NSET2:[9, 2681->2677, 85]
2   PRJT2:[9, 2681->2677, 85];exp_num(4), is_atom(FALSE)
3     SORT3:[9, 2681->2677, 85];key_num(2), partition_key_num(1), is_distinct(FALSE), is_adaptive(0), MEM_USED(30720KB), DISK_USED(0KB)
4       BLKUP2:[8, 2681->2677, 85];ITPUX_SALES_LOG_CARD(ITPUX_SALES)
5         SSEK2:[8, 2681->2677, 85];scan_type(DESC), ITPUX_SALES_LOG_CARD(ITPUX_SALES), is_global(0), scan_range[(exp11-exp12,min),(max,max))

问题再现SORT3 操作符再次出现!虽然使用了复合索引,但排序方向不匹配。

最终解决方案:匹配排序方向的索引

创建与 ORDER BY 子句完全一致的索引:

create index itpux_sales_logD_cardA on ITPUX_SALES(logtime desc, cardid asc);

执行计划:

1 NSET2:[9, 2681->2677, 85]
2   PRJT2:[9, 2681->2677, 85];exp_num(4), is_atom(FALSE)
3     BLKUP2:[8, 2681->2677, 85];ITPUX_SALES_LOGD_CARDA(ITPUX_SALES)
4       SSEK2:[8, 2681->2677, 85];scan_type(ASC), ITPUX_SALES_LOGD_CARDA(ITPUX_SALES), is_global(0), scan_range[(null2,min),(exp11-exp12,max))

最终效果:排序再次被消除!这次数据库通过正向扫描特殊设计的索引获得了正确结果。

核心原理总结

1. 索引消除排序的条件

  • ORDER BY 的顺序与索引扫描结果的顺序完全一致时,可消除排序
  • 数据库可以正向或反向扫描索引来匹配不同的排序需求

2. 不同场景下的匹配规则

ORDER BY 子句 可消除排序的索引 扫描方向
A DESC, B DESC (A ASC, B ASC) 反向扫描
A ASC, B ASC (A ASC, B ASC) 正向扫描
A DESC, B ASC (A DESC, B ASC) 正向扫描

3. 达梦数据库的执行计划关键操作符

  • SORT3:显式排序操作,消耗内存和CPU
  • SSEK2:索引范围扫描,scan_type 显示扫描方向
  • BLKUP2:通过索引回表取数据

实践建议

  1. 分析执行计划:关注 SORT 操作符的出现,这是优化的信号
  2. 创建复合索引:将 WHERE 条件和 ORDER BY 的列都考虑进索引
  3. 匹配排序方向:对于混合排序(ASC/DESC混合),创建对应方向的索引
  4. 权衡索引数量:虽然专用索引性能最佳,但要考虑维护成本
评论
后发表回复

作者

文章

阅读量

获赞

扫一扫
联系客服