程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 數據庫知識 >> DB2數據庫 >> DB2教程 >> 注意使用BTREE復合索引各字段的ASC/DESC以優化orderby查詢效率

注意使用BTREE復合索引各字段的ASC/DESC以優化orderby查詢效率

編輯:DB2教程

注意使用BTREE復合索引各字段的ASC/DESC以優化orderby查詢效率


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
# 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。這些關鍵字允許為未來擴展用於指定升序或降序索引值存儲。這個語法會被解析但卻被忽略。索引列總是以升序排列。——也就是說你寫了不會報錯,但寫了白寫。
這樣看來,我們的主鍵沒起排序作用,原因就在於我們的主鍵是各主鍵字段 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,搞定。

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved