資料庫索引設計與優化
這是讀完了 <高效能SQL/">MySQL> , <資料庫索引設計與優化> 和 MySQL Manual 之後的總結。本文的所有知識都是與MySQL相關的,其他 資料庫也許有不同。
-
索引的本質是用寫入時間換取查詢時間
-
所有的優化都是有有效上限的,例如記憶體當然是越大越好,但是當你把所有的資料和快取等都裝下了之後,記憶體再往上增加就不會再 有明顯的所用。CPU當然是越快越好,但是如果你的查詢不是CPU bound,那麼也不會有明顯作用。磁碟隨機IO當然是越快越好,也許有一天 磁碟可以達到和記憶體相當的速度,那時候磁碟也許就不再是瓶頸。
-
通常情況下來說,MySQL只能使用一顆索引樹,但是5.0之後有索引合併的優化,這種情況下可以使用多個索引,例如:
mysql [email protected]:fd_test> EXPLAIN SELECT * FROM user WHERE mobile='12345678910' OR fullname='小白'; +------+---------------+---------+-------------+---------------------+---------------------+-----------+--------+--------+-----------------------------------------------+ | id| select_type| table| type| possible_keys| key| key_len| ref| rows| Extra| |------+---------------+---------+-------------+---------------------+---------------------+-----------+--------+--------+-----------------------------------------------| | 1| SIMPLE| user| index_merge | MOBILE_IDX,IDX_NAME | MOBILE_IDX,IDX_NAME | 767,768| <null> | 2| Using union(MOBILE_IDX,IDX_NAME); Using where | +------+---------------+---------+-------------+---------------------+---------------------+-----------+--------+--------+-----------------------------------------------+ 1 row in set Time: 0.010s
諸如AND, OR等語句,MySQL可能會使用多個索引檢索出結果,然後對多個結果進行AND,OR等操作。
-
三星索引
符合三星的查詢是最佳的查詢,但是實際過程我們往往難以做到,例如如果使用了ORM,一般都沒有可能拿到第三顆星。
-
我們來假裝自己是MySQL伺服器,模擬一下查詢的過程。首先收到請求校驗請求等這些過程我們就先忽略,直接看到利用索引的 這個過程。
- 優化器決定是否使用某個索引
-
使用某個索引之後,索引通常是B樹或者其變種。通常根節點一定在記憶體中,其下層的節點可能會在記憶體中也可能會在磁碟上,取決
於作業系統記憶體是否充足以及是否將其快取。
- 假設將其快取在記憶體中,則一步一步往下查詢,在記憶體中自然速度是非常快的,一直到找到葉子節點
- 假設沒有快取在記憶體中,則找到子節點所在的磁碟中的塊,讀取資料,然後繼續進行,一直找到葉子節點
-
此前的步驟中,資料往往是連續的。找到葉子節點之後,讀取葉子節點中的資料,找到資料真正所在的行,這一步往往是
隨機I/O。因為資料所儲存的地方可能是不同的。
- 但是主鍵通常會是一張表中有且僅有的聚族索引。聚族索引是連續儲存的索引。
-
聯合索引是個好東西,但是使用聯合索引查詢時,會遵循最左匹配原則,例如有個聯合索引是
(key1, key2, key3)
,那麼能用上 的查詢是(key1)
,(key1, key2)
,(key1, key2, key3)
。其實這裡我有一點不明白,其實索引可以特殊處理一下,然後可以跳 過其中某個值進行使用,例如(key1, key3)
,原因是無論是 int還是varchar都可以換算成大小固定的值,例如int是4 byte, varchar換成一個指標,這樣就可以進行固定大小的偏移從而跨值利用索引。也許是為了降低複雜度所以目前並沒有這樣做,也許是有 其他原因。
另外為什麼是最左匹配原則呢?因為比較索引的時候是從左往右比較。
-
JOIN
是怎麼使用索引的?最樸素的JOIN自然是for...for
巢狀,例如SELECT A.* FROM A JOIN B ON A.key1 = B.key1
,可能會 生成類似於
def query(): result = [] for row_in_b in B: for row_in_a in A: if row_in_b.key1 == row_in_a.key1: result.append(row_in_a) return result
這樣的程式碼,如果在連表的欄位上有索引,那麼可能會生成:
def query(): result = [] for row_in_b in B: if row_in_b.key1 in A.key1: row_in_a = [i for i in A where A.key1 = row_in_b.key1] result.extend(row_in_a) return result
從而把時間複雜度從O(n^2)
降低到O(n lgn)
-
IN
語句是怎麼使用索引的?MySQL會把IN
後面的條件先排序,然後進行範圍查詢。因為B樹是有序的,所以可以減少很多無關資料。
還有很多。。。不過不想寫了。。。還是看書吧,哈哈哈
- ofollow,noindex" target="_blank">ORDER BY如何使用索引
- 高效能MySQL
- 資料庫索引設計和優化
- MySQL manual