注意使用 BTREE 复合索引各字段的 ASC/DESC 以优化 order by 查询效率

tbl_direct_pos_201506 表有 190 万数据。DDL:

CREATE TABLE `tbl_direct_pos_201506` (
  `acq_ins_code` char(13) NOT NULL DEFAULT ‘‘ COMMENT ‘机构代码‘,
  `trace_num` char(6) NOT NULL DEFAULT ‘‘ COMMENT ‘跟踪号‘,
  `trans_datetime` char(10) NOT NULL DEFAULT ‘‘ COMMENT ‘交易时间‘,
  `process_flag` char(1) DEFAULT NULL COMMENT ‘处理标识‘,
  `rev_flag` char(1) DEFAULT NULL COMMENT ‘接收标识‘,
  `before_trans_code` char(3) DEFAULT NULL COMMENT ‘交易类型‘,
  `trans_amt` decimal(15,3) DEFAULT NULL COMMENT ‘交易金额‘,
  `acct_num` char(21) DEFAULT NULL COMMENT ‘卡号‘,
  `mer_type` char(4) DEFAULT NULL COMMENT ‘商户类型‘,
  `recv_ins_code` char(13) DEFAULT NULL COMMENT ‘发卡行代码‘,
  `retrivl_ref_num` char(12) DEFAULT NULL COMMENT ‘检索參考号‘,
  `resp_auth_code` char(6) DEFAULT NULL COMMENT ‘授权码‘,
  `resp_code` char(2) DEFAULT NULL COMMENT ‘应答码‘,
  `term_id` char(8) DEFAULT NULL COMMENT ‘终端代码‘,
  `mer_code` char(15) DEFAULT NULL COMMENT ‘商户代码‘,
  `mer_addr_name` char(40) DEFAULT NULL COMMENT ‘商户名称和地址,前 25 字节是名称,后面是地址‘,
  `self_define` varchar(300) DEFAULT NULL COMMENT ‘第 259 字节是卡片类型‘,
  `sys_date` char(8) NOT NULL DEFAULT ‘‘ COMMENT ‘交易日期‘,
  `sa_sav2` varchar(300) DEFAULT NULL COMMENT ‘第 243 字节是 DCC 标识‘,
  `rec_create_time` datetime DEFAULT NULL COMMENT ‘联机入库时间‘,
  `rec_update_time` datetime DEFAULT NULL COMMENT ‘最后改动时间‘,
  PRIMARY KEY (`sys_date`,`trans_datetime`,`acq_ins_code`,`trace_num`),
  KEY `idx_direct_pos_create_time` (`rec_create_time`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT=‘交易月表模板‘;

关于该表的一个慢查询日志例如以下:
# Time: 150701 15:45:28
# [email protected]: test[test] @ localhost [127.0.0.1]  Id:     1
# Query_time: 2.478195  Lock_time: 0.010007 Rows_sent: 20  Rows_examined: 450612
SET timestamp=1435736728;
select substr(t.acq_ins_code, 3) merAcqInsCode, t.mer_code, t.term_id, substr(t.mer_addr_name, 1, 12) merName,
tt.trans_desc, t.rev_flag, t.trans_amt, concat(substr(t.sys_date, 1, 4), t.trans_datetime) transTime,t.before_trans_code,
t.acct_num, t.retrivl_ref_num,  t.resp_code, t.resp_auth_code,  r.recv_ins_name,t.acq_ins_code,t.trace_num,t.trans_datetime,
case substr(t.sa_sav2,259,1)  when 1 then ‘借记卡‘ when 2 then ‘贷记卡‘
when 3 then ‘准贷记卡‘ when 4 then ‘私有预付卡‘ else ‘‘ end  cardType,
case 
when locate(‘VIS‘,t.sa_sav2) > 0 then ‘VISA‘ 
when locate(‘JCB‘,t.sa_sav2) > 0 then ‘JCB‘ 
when locate(‘DNC‘,t.sa_sav2) > 0 then ‘大莱卡‘ 
when locate(‘CUP‘,t.sa_sav2) > 0 then ‘银联境内卡‘ 
when locate(‘UPI‘,t.sa_sav2) > 0 then ‘银联境外卡‘
else ‘‘ end cardBrand 
from tbl_direct_pos_201506 t
left join trans_recv_ins r on r.recv_ins_code = t.recv_ins_code
left join tbl_trans_type tt on tt.trans_code = t.before_trans_code
where t.sys_date between ‘20150622‘ and ‘20150628‘ 
order by
t.sys_date desc, t.trans_datetime desc, t.acq_ins_code, t.trace_num 
limit 0, 20;
日志中能够看出该 sql 的运行时间是 2.478 s。

我们来查看一下该 sql 的运行计划:

id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t range PRIMARY PRIMARY 24   299392 Using index condition; Using filesort
1 SIMPLE r eq_ref PRIMARY PRIMARY 41 msp.t.recv_ins_code 1 Using where
1 SIMPLE tt eq_ref PRIMARY PRIMARY 14 msp.t.before_trans_code 1 Using where

运行计划分析:
Using filesort。是的,看到它。说明我们的查询须要优化了:文件排序是通过对应的排序算法,将取得的数据在内存中进行排序。

MyISAM 存储引擎的主键索引和非主键索引区别非常小,仅仅只是是主键索引的索引键是一个唯一且非空的键而已。

MyISAM 的索引默觉得 B-TREE。也就是说,主键在这里相当于一个普通的 B-TREE。
该 sql 一个 where 字段,四个 order by 字段。都在主键里边呀,并且 order by 的顺序全然符合最左前缀原则,为什么还要 filesort?
MySql 索引创建手冊里如是说:
索引列的定义能够尾随 ASC 或者 DESC。这些keyword同意为未来扩展用于指定升序或降序索引值存储。这个语法会被解析但却被忽略。索引列总是以升序排列。

——也就是说你写了不会报错,但写了白写。

这样看来,我们的主键没起排序作用。原因就在于我们的主键是各主键字段 asc 存储。 order by 里 desc 和 asc(默认是 asc) 混用。为了验证这个说法。我们把该 order by 换为和主键一致的 asc:

select substr(t.acq_ins_code, 3) merAcqInsCode, t.mer_code, t.term_id, substr(t.mer_addr_name, 1, 12) merName,
				tt.trans_desc, t.rev_flag, t.trans_amt, concat(substr(t.sys_date, 1, 4), t.trans_datetime) transTime,t.before_trans_code,
				t.acct_num, t.retrivl_ref_num,  t.resp_code, t.resp_auth_code,  r.recv_ins_name,t.acq_ins_code,t.trace_num,t.trans_datetime,
				case substr(t.sa_sav2,259,1)  when 1 then ‘借记卡‘ when 2 then ‘贷记卡‘
				when 3 then ‘准贷记卡‘ when 4 then ‘私有预付卡‘ else ‘‘ end  cardType,
				case
					when locate(‘VIS‘,t.sa_sav2) > 0 then ‘VISA‘
					when locate(‘JCB‘,t.sa_sav2) > 0 then ‘JCB‘
					when locate(‘DNC‘,t.sa_sav2) > 0 then ‘大莱卡‘
					when locate(‘CUP‘,t.sa_sav2) > 0 then ‘银联境内卡‘
					when locate(‘UPI‘,t.sa_sav2) > 0 then ‘银联境外卡‘
					else ‘‘ end cardBrand
			from tbl_direct_pos_201506 t
			left join trans_recv_ins r on r.recv_ins_code = t.recv_ins_code
			left join tbl_trans_type tt on tt.trans_code = t.before_trans_code
			where t.sys_date between ‘20150622‘ and ‘20150628‘
			order by
			 t.sys_date, t.trans_datetime, t.acq_ins_code, t.trace_num
			limit 0, 20;

运行时间:0.023 s。

结果差强人意。查看其运行计划:

id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t range PRIMARY PRIMARY 24   299392 Using index condition
1 SIMPLE r eq_ref PRIMARY PRIMARY 41 msp.t.recv_ins_code 1 Using where
1 SIMPLE tt eq_ref PRIMARY PRIMARY 14 msp.t.before_trans_code 1 Using where

果然,我们利用到了主键索引,Using filesort 没有了。
既然找的了问题的症兆所在,接下来的事情似乎仅仅是走流程了。
问了下业务,分页结果里 sys_date 和 trans_datetime 两个字段必须降序排列。其余两个字段倒不是非常在意。
既然我们无法更改索引每一列的降序、升序(默觉得升序),那么我们能够在写 order by 的时候让索引各字段降序/升序一致。终于的 sql 改写为:

select substr(t.acq_ins_code, 3) merAcqInsCode, t.mer_code, t.term_id, substr(t.mer_addr_name, 1, 12) merName,
				tt.trans_desc, t.rev_flag, t.trans_amt, concat(substr(t.sys_date, 1, 4), t.trans_datetime) transTime,t.before_trans_code,
				t.acct_num, t.retrivl_ref_num,  t.resp_code, t.resp_auth_code,  r.recv_ins_name,t.acq_ins_code,t.trace_num,t.trans_datetime,
				case substr(t.sa_sav2,259,1)  when 1 then ‘借记卡‘ when 2 then ‘贷记卡‘
				when 3 then ‘准贷记卡‘ when 4 then ‘私有预付卡‘ else ‘‘ end  cardType,
				case
					when locate(‘VIS‘,t.sa_sav2) > 0 then ‘VISA‘
					when locate(‘JCB‘,t.sa_sav2) > 0 then ‘JCB‘
					when locate(‘DNC‘,t.sa_sav2) > 0 then ‘大莱卡‘
					when locate(‘CUP‘,t.sa_sav2) > 0 then ‘银联境内卡‘
					when locate(‘UPI‘,t.sa_sav2) > 0 then ‘银联境外卡‘
					else ‘‘ end cardBrand
			from tbl_direct_pos_201506 t
			left join trans_recv_ins r on r.recv_ins_code = t.recv_ins_code
			left join tbl_trans_type tt on tt.trans_code = t.before_trans_code
			where t.sys_date between ‘20150622‘ and ‘20150628‘
			order by
			 t.sys_date desc, t.trans_datetime desc, t.acq_ins_code desc, t.trace_num desc
			limit 0, 20;

运行之。0.029 s。搞定。

參考资料

时间: 2024-10-29 19:08:07

注意使用 BTREE 复合索引各字段的 ASC/DESC 以优化 order by 查询效率的相关文章

复合索引字段的排序对搜素的影响

索引是对数据库大数据的查询优化的一种有效的手段,索引又可分为唯一索引和复合索引 单一索引是指索引列为一列的情况,即新建索引的语句只实施在一列上面. 用户可以在多个列上建立索引,这种索引叫做复合索引(组合索引).复合索引的创建方法与创建单一索引的方法完全一样.但复合索引在数据库操作期间所需的开销更小,可以代替多个单一索引.而且在表的行数远远大于索引键的数目时,使用这种方式可以明显加快表的查询速度. (1)对一张表来说,如果有一个复合索引 on (col1,col2),就没有必要同时建立一个单索引

关于mysql建立索引 复合索引 索引类型

这两天有个非常强烈的感觉就是自己在一些特别的情况下还是hold不住,脑子easy放空或者说一下子不知道怎么去分析问题了,比方,问"hash和btree索引的差别",这非常难吗.仅仅要掌握了这两种数据结构稍加分析就能得出答案,结果是一下子不知道从何说起.进入正题吧.这两者有啥差别. 1. hash索引查找数据基本上能一次定位数据.当然有大量碰撞的话性能也会下降. 而btree索引就得在节点上挨着查找了,非常明显在数据精确查找方面hash索引的效率是要高于btree的. 2. 那么不精确查

复合索引的优点和注意事项

概念:     单一索引是指索引列为一列的情况,即新建索引的语句只实施在一列上;     用户可以在多个列上建立索引,这种索引叫做复合索引(组合索引);     复合索引在数据库操作期间所需的开销更小,可以代替多个单一索引;     同时有两个概念叫做窄索引和宽索引,窄索引是指索引列为1-2列的索引,宽索引也就是索引列超过2列的索引;     设计索引的一个重要原则就是能用窄索引不用宽索引,因为窄索引往往比组合索引更有效; 使用:     创建索引     create index idx1 o

mySql的普通索引和复合索引

有关普通索引和组合索引问题: 索引分单列索引和组合索引:单列索引,即一个索引只包含单个列,一个表可以有多个单列索引,但这不是组合索引:组合索引,即一个索包含多个列.   MySQL索引类型包括:   (1)普通索引是最基本的索引,它没有任何限制.它有以下几种创建方式:   ◆创建索引   CREATE INDEX indexName ON mytable(username(length));   如果是 CHAR,VARCHAR类型,length可以小于字段实际长度;如果是BLOB和TEXT类型

Sql Server之旅——第八站 复合索引和include索引到底有多大区别?

周末终于搬进出租房了,装了宽带....才发现没网的日子...那是一个怎样的与世隔绝呀...再也受不了那样的日子了....好了,既然网 安上去了,还得继续我的这个系列. 索引和锁,这两个主题对我们开发工程师来说,非常的重要...只有理解了这两个主题,我们才能写出高质量的sql语句,在之前的博客中,我所说的 索引都是单列索引...当然数据库不可能只认单列索引,还有我这篇的复合索引,说到复合索引,可能熟悉的人又会说到include索引,那这两个索引到底 有什么区别呢,当然我也是菜鸟一枚...所以下面的

Sql Server之旅——第九站 看公司这些DBA们设计的这些复合索引

这一篇再说下索引的最后一个主题,索引覆盖,当然学习比较好的捷径是看看那些大师们设计的索引,看从中能提取些什么营养的东西,下面我们看 看数据库中一个核心的Orders表. 一:查看表的架构 <1> 先查看这个表的大概架构信息 1 --查看表的架构信息 2 SELECT c.column_id,c.name,t.name FROM sys.columns AS c 3 JOIN sys.types t 4 ON c.system_type_id=t.system_type_id 5 WHERE c

复合索引介绍

什么是复合索引 1.1           复合索引定义 索引可以包含一个.两个或更多个列.两个或更多个列上的索引被称作复合索引. 利用索引中的附加列,您可以缩小搜索的范围,但使用一个具有两列的索引不同于使用两个单独的索引.复合索引的结构与电话簿类似,人名由姓和名构成,电话簿首先按姓氏对进行排序,然后按名字对有相同姓氏的人进行排序.如果您知道姓,电话簿将非常有用:如果您知道姓和名,电话簿则更为有用,但如果您只知道名不姓,电话簿将没有用处. 所以说创建复合索引时,应该仔细考虑列的顺序.对索引中的所

SQL Server创建复合索引时,复合索引列顺序对查询的性能影响

说说复合索引 写索引的博客太多了,一直不想动手写,有一下两个原因:一是觉得有炒剩饭的嫌疑,有兄弟曾说:索引吗,只要在查询条件上建索引就行了,真的可以这么暴力吗?二来觉得,索引是个非常大的话题,很难概括出所有的情况,你不整出点新意来,倒是有抄袭照搬的嫌疑 既然写了,就写一点稍微不一样的东西出来,好了,废话打住,开搞 /* 20160814备注:今天发现一个类似的文章:http://www.cnblogs.com/fly_zj/archive/2012/08/11/2633629.html : 可以

SQL SERVER大话存储结构(4)_复合索引与包含索引

索引这块从存储结构来分,有2大类,聚集索引和非聚集索引,而非聚集索引在堆表或者在聚集索引表都会对其 键值有所影响,这块可以详细查看本系列第二篇文章:SQL SERVER大话存储结构_(2)_非聚集索引如何查找到行记录. 非聚集索引内又分为多类:单列索引.复合索引.包含索引.过滤索引等.之前文章有具体分析过非聚集索引的存储情况,但是没有对复合索引及包含索引做过多说明,本文来讲讲这两个索引. 如果转载,请注明博文来源: www.cnblogs.com/xinysu/   ,版权归 博客园 苏家小萝卜