sql優化的方案記錄

前言

相信我們都碰見過sql優化的問題,而大部分人的做法就是建立索引,在InnoDB引擎中索引分爲 B+tree 和 Hash, 
Hash index : 
    Hash的應用場景不多,我們簡單的聊幾句帶過,Hash 顧名思義是通過hash算法將索引列進行計算確定存儲指針位置,優點就是查詢非常快,缺點就是不能進行範圍或者不等於查詢,而且不合適的hash算法會造成大量的hash衝突

B+tree index : 
    這裏涉及了一些數據結構的知識,需要我們去了解一下,B+tree也是由 二叉樹 --> 平衡二叉樹 --> B-tree(B槓tree) --> B+tree演化優化而來,這裏有一篇不錯的介紹,感興趣的可以看看裏面的具體演化過程和對InnoDB引擎的介紹
    文章地址: https://www.toutiao.com/i6709447784136704523/

方案

sql優化的方案記錄:
    1. 儘量索引全值匹配
    2. varchar類型的引號不可省略
    3. 組合索引,索引要遵守最左匹配原則
    4. 不在索引列上做任何操作[計算,函數,(自動 or 手動)類型轉換]會導致索引失效而進行全表掃描
    5. 範圍條件放到最後[存儲引擎不能使用索引中範圍條件右邊的列]
    6. 不等於要慎用[在使用不等於(!= 或者 <>)的時候無法使用索引會導致全表掃描]
    7. OR 改 UNION 效率高
    8. like百分號寫最右邊(爲了遵守最左匹配原則)

測試案例

建表DDL語句(測試數據量大概200萬):

CREATE TABLE `srcorder_item` (
  `order_item_no` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '訂單行號',
  `order_no` varchar(20) DEFAULT NULL COMMENT '訂單號',
  `cust_no` varchar(20) DEFAULT NULL COMMENT '會員編碼',
  `commdty_code` varchar(20) DEFAULT NULL COMMENT '商品編碼',
  `quantiy` int(11) DEFAULT NULL COMMENT '數量',
  `unit_price` decimal(28,6) DEFAULT NULL COMMENT '單價',
  `create_time` datetime DEFAULT NULL COMMENT '創建時間',
  `update_time` datetime DEFAULT NULL COMMENT '最後更新時間',
  `order_header_no` bigint(20) DEFAULT NULL,
  `status` varchar(30) DEFAULT NULL COMMENT '訂單行狀態',
  `gc_order_no` varchar(20) DEFAULT NULL COMMENT '訂單號',
  `gc_item_no` varchar(20) DEFAULT NULL COMMENT '訂單行號',
  `invoice_status` char(1) DEFAULT NULL COMMENT '發票是否已開狀態',
  PRIMARY KEY (`order_item_no`),
  KEY `srcorder_item_idx_1` (`order_no`) USING BTREE,
  KEY `srcorder_item_idx_2` (`cust_no`) USING BTREE,
  KEY `srcorder_item_idx5` (`create_time`,`status`) USING BTREE,
  KEY `srcorder_item_idx_7` (`gc_order_no`) USING BTREE,
  KEY `srcorder_item_idx_8` (`gc_item_no`) USING BTREE,
  KEY `srcorder_item_idx_10` (`update_time`) USING BTREE,
  KEY `srcorder_item_idx_3` (`order_header_no`,`commdty_code`) USING BTREE,
  KEY `srcorder_item_idx_11` (`update_time`,`cust_no`) USING BTREE,
  KEY `srcorder_item_idx_12` (`cust_no`,`gc_order_no`,`create_time`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1550000 DEFAULT CHARSET=utf8 COMMENT='訂單行信息';

   Test1. 儘量索引全值匹配

explain 
select * from srcorder_item where cust_no = '6022517106' and gc_order_no ='1002296352' and create_time = STR_TO_DATE('2015-01-28 16:10:35', '%Y-%m-%d %H:%i:%s');

執行計劃:

提示: 按索引全值匹配,應該走 srcorder_item_idx_12 這個索引,但實際 srcorder_item_idx_7 ,這裏有一點需要注意,搜索引擎會優化選擇合適的索引,但不一定是最合適的,我們可以強制讓sql走 srcorder_item_idx_12 這個索引,測試一下平均查詢時間

explain 
select * from srcorder_item force index(srcorder_item_idx_12) where cust_no = '6022517106' and gc_order_no ='1002296352' and create_time = STR_TO_DATE('2015-01-28 16:10:35', '%Y-%m-%d %H:%i:%s');


1)非強制執行5次的執行時間分別爲      [0.022, 0.038, 0.040, 0.039, 0.025]  平均 = 0.0328

2)強制索引執行5次的執行時間分別爲  [0.033, 0.010, 0.013, 0.021, 0.028]  平均 = 0.021

由此可見,有時候走不到自己的全值匹配索引時,可以考慮一下強制索引的寫法

   Test2. varchar類型的引號不可省略

select * from srcorder_item where order_no = 1002296352
select * from srcorder_item where order_no = '1002296352'


    Test3. 組合索引,索引要遵守最左匹配原則

explain select * from srcorder_item where create_time = STR_TO_DATE('2015-01-28 16:10:35', '%Y-%m-%d %H:%i:%s') and status='00'

explain select * from srcorder_item force index(srcorder_item_idx_12) where  gc_order_no ='1002296352' and create_time >= '2015-01-01 16:10:35' and create_time < '2015-01-31 16:10:35';


    Test4. 不在索引列上做任何操作[計算,函數,(自動 or 手動)類型轉換]會導致索引失效而進行全表掃描

explain select * from srcorder_item  where DATE_FORMAT(update_time, '%Y-%m-%d') = CURDATE()


   Test5. 範圍條件放到最後[存儲引擎不能使用索引中範圍條件右邊的列]

explain select * from srcorder_item where create_time > STR_TO_DATE('2015-01-28 16:10:35', '%Y-%m-%d %H:%i:%s') and status='00' 


    Test6. 不等於要慎用[在使用不等於(!= 或者 <>)的時候無法使用索引會導致全表掃描]

explain select * from srcorder_item where create_time != STR_TO_DATE('2015-01-28 16:10:35', '%Y-%m-%d %H:%i:%s') and status='00' 


    Test7. OR 改 UNION 效率高

select * from srcorder_item where order_no = '1002296352' or order_no='1418580809770'
修改後
select * from srcorder_item where order_no = '1002296352'
UNION
select * from srcorder_item where order_no = '1418580809770'

前者5次平均:[0.034,0.036,0.273,0.038,0.037] 平均 = 0.0836

後者5次平均[0.035,0.024,0.010,0.021,0.021] 平均 = 0.0222


    Test8. like百分號寫最右邊(爲了遵守最左匹配原則)

select * from srcorder_item where order_no like '1418580809770%'
非特殊需求,儘量不要用下面的寫法(執行計劃來看,會走全表掃描)
select * from srcorder_item where order_no like '%1418580809770%'

小結

全值匹配我最愛,最左前綴要遵守

帶頭大哥不能死,中間兄弟不能斷

索引列上少計算,範圍之後全失效

LIKE百分寫最右,覆蓋索引不寫*[意思是索引(a,b),你查詢的時候 select a,b from t where a=? and b=?,索引直接有值,避免回表]

不等空值還有OR,索引影響要注意

VAR引號不能丟,SQL優化有訣竅

重要重要重要(引用某大牛說的): 數據庫的優化,遠遠比不上應用層面上的優化,有可能是設計的問題,也值得考慮,而不是直指SQL

例外

查詢是否用到索引,有時候並不是我們建立了索引,就一定會走索引,搜索引擎還會做優化

1.例如走全表掃描比索引快時,執行計劃中就走不到我們建立的索引

2.通過索引掃描的記錄超過20%~30%,可能會變成全表掃描,常見於數據量大,根據時間範圍查詢的時候

發佈了11 篇原創文章 · 獲贊 1 · 訪問量 4929
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章