注意使用 BTREE 复合索引各字段的 ASC/DESC 以优化 order by 查询效率
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
# User@Host: 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。搞定。
參考资料
注意使用 BTREE 复合索引各字段的 ASC/DESC 以优化 order by 查询效率的更多相关文章
- sql复合索引使用和注意事项
1.定义: 单一索引: 单一索引是指索引列为一列的情况,即新建索引的语句只实施在一列上; 复合索引: 复合索引也叫组合索引: 用户可以在多个列上建立索引,这种索引叫做复合索引(组合索引). 复合索引在 ...
- 二十、oracle通过复合索引优化查询及不走索引的8种情况
1. 理解ROWID ROWID是由Oracle自动加在表中每行最后的一列伪列,既然是伪列,就说明表中并不会物理存储ROWID的值:你可以像使用其它列一样使用它,只是不能对该列的值进行增.删.改操作: ...
- 索引优化、Sql查询语句优化
工作中我们经常会遇到系统查询慢的情况,一般我们会采取好多方法进行优化,如建立索引,优化查询Sql,分表,规范数据表结构设计,调整数据库参数(内存分配.缓存等),增加硬件配置,优化网络环境等.下面介绍两 ...
- mysql索引之四:复合索引之最左前缀原理,索引选择性,索引优化策略之前缀索引
高效使用索引的首要条件是知道什么样的查询会使用到索引,这个问题和B+Tree中的“最左前缀原理”有关,下面通过例子说明最左前缀原理. 一.最左前缀索引 这里先说一下联合索引的概念.MySQL中的索引可 ...
- 关于mysql建立索引 复合索引 索引类型
这两天有个非常强烈的感觉就是自己在一些特别的情况下还是hold不住,脑子easy放空或者说一下子不知道怎么去分析问题了,比方,问"hash和btree索引的差别",这非常难吗.仅仅 ...
- MySQL复合索引探究
复合索引(又称为联合索引),是在多个列上创建的索引.创建复合索引最重要的是列顺序的选择,这关系到索引能否使用上,或者影响多少个谓词条件能使用上索引.复合索引的使用遵循最左匹配原则,只有索引左边的列匹配 ...
- SQL Server 执行计划利用统计信息对数据行的预估原理二(为什么复合索引列顺序会影响到执行计划对数据行的预估)
本文出处:http://www.cnblogs.com/wy123/p/6008477.html 关于统计信息对数据行数做预估,之前写过对非相关列(单独或者单独的索引列)进行预估时候的算法,参考这里. ...
- [慢查优化]建索引时注意字段选择性 & 范围查询注意组合索引的字段顺序
文章转自:http://www.cnblogs.com/zhengyun_ustc/p/slowquery2.html 写在前面的话: 之前曾说过"不要求每个人一定理解 联表查询(join/ ...
- Mysql复合索引
当Mysql使用索引字段作为条件时,如果该索引是复合索引,必须使用该索引中的第一个字段作为条件才能保证系统使用该索引,否则该索引不会被使用,并且应尽可能地让索引顺序和字段顺序一致
随机推荐
- POJ22230 Watchcow (欧拉回路)
Watchcow Time Limit: 3000MS Memory Limit: 65536K Total Submissions: 6477 Accepted: 2823 Specia ...
- canvas.js | CLiPS
canvas.js | CLiPS canvas.js The canvas.js module is a simple and robust JavaScript API for the HTML5 ...
- JS封装cookie操作函数实例(设置、读取、删除)
本文实例讲述了JS封装cookie操作函数.分享给大家供大家参考,具体如下: ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 ...
- java面试题集1
一:单选题 下列哪一种叙述是正确的(D )A. abstract修饰符可修饰字段.方法和类B. 抽象方法的body部分必须用一对大括号{ }包住C. 声明抽象方法,大括号可有可无D. 声明抽象方法不可 ...
- ArcSDE for Oracle表空间管理——暂时(TEMP)表空间
Oracle暂时表空间主要用来做查询和存放一些缓冲区数据.暂时表空间消耗的主要原因是须要对查询的中间结果进行排序. 重新启动数据库能够释放暂时表空间,假设不能重新启动实例,而一直保持问题sql语句的运 ...
- NTP-ntpdate:no server suitable for synchronization found
NTP-ntpdate 问题处理 解决ntp的错误 no server suitable for synchronization found 当用ntpdate -d 来查询时会发现导致 no ser ...
- Maven 工程下 Spring MVC 站点配置 (三) C3P0连接池与@Autowired的应用
Maven 工程下 Spring MVC 站点配置 (一) Maven 工程下 Spring MVC 站点配置 (二) Mybatis数据操作 前两篇文章主要是对站点和数据库操作配置进行了演示,如果单 ...
- Mongoose的使用
最近想做一个练手的App小项目.考虑到数据接口的问题,因为关系型数据库用的比较多,也有一定经验了,所以打算使用比较火的MongoDB作为数据库,下面就介绍一下Mongoose的使用方法吧. 概念:Mo ...
- [?]Unity快捷键
Super=Win键. Rererence: What are Unity's keyboard and mouse shortcuts?
- java poi 导出excel
poi的jar下载地址:http://poi.apache.org/ 下载后会有很多jar,但是如果只是简单的excel报表的话,导入一个poi-版本号-日期.jar就可以了. 导出代码: priva ...