轉載:MySQL 多列索引優化小記

問題背景 因爲爬蟲抓取的數據不斷增多,這兩天在不斷對數據庫以及查詢語句進行優化,其中一個表結構以下:數據庫

CREATE TABLE newspaper_article ( id varchar(50) NOT NULL COMMENT '編號', title varchar(190) NOT NULL COMMENT '標題', author varchar(255) DEFAULT NULL COMMENT '做者', date date NULL DEFAULT NULL COMMENT '發表時間', content longtext COMMENT '正文', status tinyint(4) DEFAULT '0', PRIMARY KEY (id), KEY idx_status_date (status,date) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='文章表'; 根據業務須要,添加了 idx_status_date 索引,在執行下面這個 SQL 時特別耗時:服務器

SELECT id, title, status, date FROM article WHERE status > -2 AND date = '2016-01-07'; 根據觀察,天天新增的數據大概在2500條之內,本覺得這裏指定了具體某天的日期 '2016-01-07' ,實際須要掃描的數據量應該在2500條之內纔對,但實際並不是如此: EXPLAIN 實際共掃描了185589條數據,遠遠高於預估的2500條,且實際執行時間都將近3秒鐘:數據結構

EXPLAIN性能

這是爲何呢?優化

解決方案 將 idx_status_date (status, date) 改成 idx_status (status) 後,查看 MySQL 執行計劃:spa

EXPLAINcode

能夠看到將多列索引改成單列索引後,執行計劃要掃描的數據總量沒有任何變化。結合多列索引遵循最左前綴原則,推測上面的查詢語句只使用了 idx_status_date 最左邊的 status 的索引。索引

翻了下《高性能MySQL》找到了下面這段話,證明了個人想法:it

若是查詢中有某個列的範圍查詢,則其右邊全部列都沒法使用索引優化查找。例若有查詢 WHERE last_name = 'Smith' AND first_name LIKE 'J%' AND dob = '1976-12-23' ,這個查詢只能使用索引的前兩列,由於這裏 LIKE 是一個範圍條件(可是服務器能夠把其他列用於其餘目的)。若是範圍查詢列值的數量有限,那麼能夠經過使用多個等於條件來代替範圍條件。 所以,這裏解決思路有兩種:ast

能夠經過使用多個等於條件來代替範圍條件 修改 idx_status_date (status, date) 爲索引 idx_date_status (date, status) ,並新建一個 idx_status 索引,便可達到一樣的效果。 優化後的執行計劃:

EXPLAIN

實際執行結果:

EXPLAIN

總結 當人們談論索引的時候,若是沒有特別指明類型,那麼多半說的是 B-Tree 索引,它使用 B-Tree 數據結構來存儲數據。咱們使用術語「B-Tree」,是由於 MySQL 在 CREATE TABLE 和其餘語句中也使用該關鍵字。不過,底層的存儲引擎也可能使用不一樣的存儲結構。InnoDB使用的是B+Tree。 假若有以下數據表:

CREATE TABLE People ( last_name varchar(50) not null, first_name varchar(50) not null, dob date not null, gender enum('m', 'f') not null, key(last_name, first_name, dob) ); B-Tree 索引對以下類型的查詢有效 全值匹配 全值匹配指的是和索引中的全部列進行匹配,例如上表的索引可用於查找姓名爲 Cuba Allen 、出生於 1960-01-01 的人。 匹配最左前綴 上表中的索引可用於查找全部姓爲 Allen 的人,即只使用索引的第一列。 匹配列前綴 只匹配某一列的值的開頭部分。例如上表的索引可用於查找全部以 J 開頭的姓的人。這裏也只使用了索引的第一列。 匹配範圍值 例如上表中的索引可用於查找姓在 Allen 和 Barrymore 之間的人。這裏也只使用了索引的第一列。 精確匹配某一列並範圍匹配另一列 上表的索引也可用於查找全部姓爲 Allen ,而且名字是字母 K 開頭(好比 Kim 、 Karl 等)的人。即第一列 last_name 全匹配,第二列 first_name 範圍匹配。 只訪問索引的查詢 B-Tree 一般能夠支持「只訪問索引的查詢」,即查詢只須要訪問索引,而無須訪問數據行。 B-Tree 索引的一些限制 若是不是按照索引的最左列開始查找,則沒法使用索引。例如上表的索引沒法用於查找名字爲 Bill 的人,也沒法查找某個特定生日的人,由於這兩列都不是最左數據列。相似地,也沒法查找姓氏以某個字母結尾的人。 不能跳過索引中列。也就是說,上表的索引沒法用於查找姓氏爲 Smith 而且在某個特定日期出生的人。若是不指定名(first_name),則 MySQL 只能使用索引的第一列。 若是查詢中有某個列的範圍查詢,則其右邊全部列都沒法使用索引優化查找。例若有查詢 WHERE last_name = 'Smith' AND first_name LIKE 'J%' AND dob = '1976-12-23' ,這個查詢只能使用索引的前兩列,由於這裏 LIKE 是一個範圍條件(可是服務器能夠把其他列用於其餘目的)。若是範圍查詢列值的數量有限,那麼能夠經過使用多個等於條件來代替範圍條件。

相關文章
相關標籤/搜索