前綴索引和索引選擇性
有時候需要索引很長的字符,這會讓索引變得大且慢。一個策略是模擬哈希索引。
通常可以索引開始的部分字符,這樣可以大大解約索引空間,提高索引效率。但這樣會降低索引的選擇性。
索引的選擇性:不重復的索引值(也成為基數)和數據表的記錄總數比值。索引的選擇性越高則查詢效率越高,因為選擇性高的索引可以在查找時過濾更多的行。唯一索引的選擇性為1,是選擇性最好的。
前綴索引是一種能使索引更小更快的辦法,但也有缺點:
MySQL無法使用ORDER BY和GROUP BY,也無法使用覆蓋掃描。
聚簇索引
聚簇索引並不是一種單獨的索引類型,是一種數據存儲方式。
當表有聚簇索引時,它的數據行實際上存放在索引的葉子頁。
聚簇:數據行和相鄰的鍵值緊湊的存儲在一起。
如果沒有定義主鍵,InnoDB會選擇一個唯一的非空索引代替。
如果沒有這樣的索引,會隱式定義一個主鍵作為聚簇索引。
聚簇索引的缺點
-
插入速度嚴重依賴插入順序。按照主鍵的順序插入是加載數據到InnoDB表中速度最快的方式。但如果不是按照主鍵順序加載數據,最好使用OPYIMIZE TABLE重新組織表。
-
基於聚簇索引的表在插入新行,或者主鍵被遷移時,可能會“頁分裂”。當行的主鍵值要求必須將這一行插入到某個已滿的頁中時,存儲引擎會將該頁分裂成兩個頁面來容納該行,這是一次頁分裂操作。頁分裂會導致表占用更多的磁盤空間。
覆蓋索引
通常大家會根據查詢的WHERE條件創建合適的索引,設計優秀的索引也可以使用索引來直接獲取列的數據。
如果索引的葉子結點已經包含要查詢的數據,那還要什么必要再回表查詢呢?如果一個索引包含所有需要查詢的字段的值,我們稱之為“覆蓋索引”。

延遲關聯
使用inner join做子查詢。在查詢的第一個階段可以使用覆蓋索引。雖然無法使用索引覆蓋整個查詢,但比完全無法利用索引覆蓋的好。
冗余和重復索引
索引越大越多,插入數據越慢。
可以使用Percona Toolkit中的pt-duplicate-key-checker分析表結構找出冗余的索引。
單表建多少個索引才合適?
大表,主鍵有一個唯一索引。再有一到兩個組合索引,最多三個索引足夠用了。
索引數量不能超過4個/表。
一切服從應用需要。在一張表上創建多少索引,創建什么樣的索引,並無一定之規。不能說一張表上有了 7個索引,就不能再創建第 8個索引了。
索引的多少取決於具體的業務場景。
在oltp中,表經常需要insert等,那么索引不能過多,一般超過3個就會對性能有影響。
在olap中如果表只是用於查詢,那么建多個索引也無妨。
索引和鎖
索引可以讓查詢鎖定更少的行。但是,如果索引無法過濾掉無效的行,那么在InnoDB檢索到數據並返回給服務器層以后,MySQL才能用那個用WHERE子句,這時已經無法避免鎖定行了:InnoDB已經鎖定了這些行。
mysql> select actor_id from sakila.actor where actor_id < 5 and actor_id <> 1 for update;
雖然這條查詢返回的是2,3,4,但是實際上獲取了1-4的排他鎖。
話句話說,存儲引擎的操作是“找小於5的記錄”,服務器並沒有告訴InnoDB可以過濾第1行的WHERE條件。注意到EXPLAIN的Extra列出現了“Using where”,這表示MySQL將存儲引擎返回行以后再應用WHERE過濾條件。
using where 代表MYSQL服務器層在存儲引擎層返回行以后再應用WHERE過濾條件
查詢性能優化
對於性能低下的查詢,通過兩個步驟來分析非常有效:
1、確認應用程序是否在檢索大量超過需要的數據。這意味着訪問了過多的行或者是過多的列。
2、確認MySQL服務器層是否在分析大量超過需要的數據行。
-
比如使用 * 來返回全部列,其實有些列是用不到的,應該精簡,或者說重復查詢相同改的數據,這應該把這種數據放到緩存里,下次查先從緩存取,熱點數據每次取可以給加過期時間。
-
確認EXPLAIN中掃描的行數和訪問類型
在EXPLAIN中的type列是訪問類型,從全表掃描、索引掃描、范圍掃描、唯一索引查詢、常數引用(全索范唯),他們的查詢速度是從慢到快。
Re
《高性能MySQL》
