本文介紹一些優化 mysql 索引設計和查詢的建議。在進行優化工作前,請務必了解mysql explain命令: 檢視執行計畫
索引在邏輯上是指從索引列(關鍵字)到資料的對映,通過索引可以快速的由關鍵字查詢到資料記錄。順序查詢複雜度為o(n), 樹狀索引查詢複雜度為o(logn), 雜湊索引為o(1)。
mysql中的索引一般是指btree索引, innodb儲存引擎使用b+樹來實現btree索引。
btree索引保持資料之間的順序,可以極大的加快精確搜尋(=, in)、範圍搜尋(<,>), 去重(distinct), 排序(order by) 和 聚合(group by)。
總結來說使用索引有三個優點:
因為寫入資料時需要為新行建立索引,所以索引會減慢寫入速度。請盡量避免建立無用的索引。
示例:
select * from `user` where `id`=5; -- 可以使用索引
select * from `user` where `id` + 1 = 5; -- 索引列作為表示式一部分時無法使用索引
select * from `user` where md5(first_name)='md5'; -- 索引列作為函式引數時無法使用索引
btree索引具有最左匹配性質, 即只能按照索引列的順序自左向右搜尋,不能跳過索引列。
聯合索引中存在範圍查詢(<, >, like, between) 會導致後面的索引列失效。
定義表和索引:
create table `user` (
`id` int,
`first_name` varchar(16),
`middle_name` varchar(16),
`last_name` varchar(16),
primary key (`id`),
key `idx_name` (`first_name`, `middle_name`, `last_name`)
);
示例:
select * from `user` where `first_name`='a'; -- 可以使用 idx_name 索引
select * from `user` where `first_name`='a' and `middle_name`='b'; -- 可以使用 idx_name 索引
select * from `user` where `first_name`='a' and `middle_name`='b' and `last_name`='c'; -- 可以使用 idx_name 索引
select * from `user` where `middle_name`='b'; -- 不能使用 idx_name 索引
select * from `user` where `middle_name`='b' and `last_name`='c'; -- 不能使用 idx_name 索引
select * from `user` where `first_name`='a' and `last_name`='c'; -- 不能使用 idx_name 索引
上文中說的"可以使用索引"是指可以用ref
,eq_ref
或range
方式進行查詢。
使用 explain 命令檢視3個不能使用索引示例的執行計畫,可以發現 type 欄位為 index, 這是在索引樹上進行順序查詢。雖然效能優於全表掃瞄, 但比 ref 和 range 查詢來說要慢很多。
索引列為字串等型別時, 可以使用索引列的字首字串進行模糊查詢
select * from user where first_name = 'abc' and middle_name like 'de%';
這條語句的型別的為 range, 即在索引列上進行範圍查詢。
將聯合索引理解為: 將索引列(關鍵字)按順序拼接, 把拼接後的關鍵字與資料建立對映。最左匹配即是使用關鍵字字首縮小搜尋範圍。在進行多列搜尋時有一條經驗法則:首先使用選擇性高的列進行搜尋。
我們可以將選擇性定義為 count(distinct ) / count(*), 也就是說滿足條件的資料越少,則條件的選擇性越高。
假設使用者名稱name比性別gender選擇性高, 那麼查詢應該寫作where name='finley' and gender='m'
而不是where gender='m' and name='finley'
。
實際上兩條語句是等效的,當存在多個查詢條件時 mysql 優化器會根據索引和選擇性決定最優的過濾順序。
為每乙個列單獨建立索引,並不能有效支援多列查詢
create table `user` (
`id` int,
`first_name` varchar(16),
`middle_name` varchar(16),
`last_name` varchar(16),
primary key (`id`),
key `idx_first_name` (`first_name`),
key `idx_middle_name` (`middle_name`)
);
查詢語句:
select * from user where first_name = 'a' and middle_name = 'bc';
檢視查詢計畫:
idselect_type
table
type
possible_keys
keykey_len
refrows
filtered
extra
1******
user
allidx_first_name,idx_middle_name
null
null
null
4100.00
using where
根據最左匹配法則和優先使用高選擇性列的經驗法則,可以得出一條建議:
對於需要進行多列查詢的表,應建立包含所有參與查詢列的聯合索引, 索引的順序應按照列的選擇性從強到弱排列
通常在使用索引檢索到資料之後,需要訪問磁碟上資料表檔案讀取所需要的列,這種操作稱為"回表"。
若索引中包含查詢的所有列,則不需要回表操作直接從索引檔案中讀取資料即可, 這種索引稱為覆蓋索引。
在查詢時儘量減少"select *"只查詢需要的行, 條件允許時盡量建立覆蓋索引。
《資料庫索引設計與優化》一書中提出了判斷最佳索引的"三星索引"概念:
1星: 可以在索引上(用 ref 或 eq_ref 方式)完成等值查詢。需要取出等值謂詞涉及的列作為索引開頭的列以滿足最左匹配原則。
2星: 可以使用索引進行排序
3星: 索引中包含要查詢的所有列,不需要回表
mysql 在索引包含 null 的列時需要額外的開銷,盡量避免允許索引列上存在 null。
除非有非常嚴格的一致性要求,否則應避免使用外來鍵。
關於主鍵:
在查詢較多且業務允許的情況下, 推薦使用自增主鍵。
不知道放哪兒好的兩條建議:
mysql在執行多表查詢時可以採用nest loop join演算法,即選擇資料集較小的一張表(資料集)作為驅動表, 遍歷驅動表中所有記錄並連線另一張表中符合條件的記錄。
在使用 join 進行查詢時 mysql 會自動選擇資料集較小的一張表作為驅動表。
left join 強制左表作為驅動表, right join 則強制選擇右表作為驅動表。
mysql 的 straight_join 結果與 inner join 相同, 但強制使用左表作為驅動表, 可用來分析選擇不同驅動表的效果。
在業務允許的情況下, 讓 mysql 自行決定驅動表。
在使用 in 進行多表查詢時一般會把 in 內部的巢狀迴圈作為驅動表, 應儘量減少in資料集的大小。實際上, mysql 也會對 in 和 exists 查詢進行優化, 選擇最優的驅動表。
MySQL索引原理與慢查詢優化
索引的目的在於提高查詢效率,可以模擬字典,如果要查 mysql 這個單詞,我們肯定需要定位到m字母,然後從下往下找到y字母,再找到剩下的sql。如果沒有索引,那麼你可能需要把所有單詞看一遍才能找到你想要的,如果我想找到m開頭的單詞呢?或者w開頭的單詞呢?是不是覺得如果沒有索引,這個事情根本無法完成?...
MySQL索引原理與慢查詢優化
索引的目的在於提高查詢效率,可以模擬字典,如果要查 mysql 這個單詞,我們肯定需要定位到m字母,然後從下往下找到y字母,再找到剩下的sql。如果沒有索引,那麼你可能需要把所有單詞看一遍才能找到你想要的,如果我想找到m開頭的單詞呢?或者w開頭的單詞呢?是不是覺得如果沒有索引,這個事情根本無法完成?...
MySQL查詢優化之索引
mysql查詢優化之索引 什麼是索引 索引簡單來說就類似字典,想想我們小時候在中華字典怎麼查某個字。索引的作用就是快速找出在某個列中有一特定值的行。例如 有一張user表,其中有200萬條記錄,記錄著200萬個人的資訊。有乙個phone的字段記錄每個人的 號碼,現在想要查詢出 號碼為 x的人的資訊。...