MySQL 索引優(yōu)化

一. 最左前綴原理

以 MySQL 官方示例數(shù)據(jù)庫(kù) employees 的 titles 表的 <emp_no, from_date> 主鍵索引來進(jìn)行說明。

(一). 全值匹配

當(dāng)按照索引中所有列進(jìn)行精確匹配 ("=" 或 "IN" 匹配) 時(shí)瑞躺,索引可以被用到惠勒。理論上索引對(duì)順序是敏感的,但是 MySQL 查詢優(yōu)化器會(huì)自動(dòng)調(diào)整 WHERE 子句的條件順序以使用適合的索引。

EXPLAIN SELECT * 
FROM employees.titles 
WHERE emp_no='10001' AND title='Senior Engineer' AND from_date='1986-06-26'\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: titles
         type: const
possible_keys: PRIMARY
          key: PRIMARY
      key_len: 59
          ref: const,const,const
         rows: 1
        Extra: 
1 row in set (0.00 sec)

(二). 最左前綴匹配

當(dāng)查詢條件精確匹配索引的左邊連續(xù)一個(gè)或幾個(gè)列時(shí),索引可以被用到,但是只能用到一部分片择,即條件所組成的最左前綴。

EXPLAIN SELECT * FROM employees.titles WHERE emp_no='10001'\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: titles
         type: ref
possible_keys: PRIMARY
          key: PRIMARY
      key_len: 4
          ref: const
         rows: 1
        Extra: 
1 row in set (0.00 sec)

(三). 全值匹配骚揍,但是中間某個(gè)條件未提供

此時(shí)索引使用情況和 "最左前綴匹配" 相同字管,因?yàn)?title 未提供,所以查詢只用到了索引的第一列信不,而后面的 from_date 雖然也在索引中嘲叔,但是由于 title 不存在而無法和左前綴連接,因此需要對(duì)結(jié)果進(jìn)行掃描過濾 from_date抽活。

EXPLAIN SELECT * 
FROM employees.titles WHERE emp_no='10001' AND from_date='1986-06-26'\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: titles
         type: ref
possible_keys: PRIMARY
          key: PRIMARY
      key_len: 4
          ref: const
         rows: 1
        Extra: Using where
1 row in set (0.00 sec

如果想讓 from_date 列也使用索引而不是使用 WHERE 過濾硫戈,可以增加一個(gè)輔助索引 <emp_no, from_date>。除此之外下硕,還可以使用一種被稱之為 "隔離列" 的優(yōu)化方法丁逝,將 emp_no 與 from_date 之間的 "坑" 填上。

SELECT DISTINCT(title) FROM employees.titles;
+--------------------+
| title              |
+--------------------+
| Senior Engineer    |
| Staff              |
| Engineer           |
| Senior Staff       |
| Assistant Engineer |
| Technique Leader   |
| Manager            |
+--------------------+
7 rows in set (0.20 sec)

EXPLAIN SELECT * 
FROM employees.titles 
WHERE emp_no='10001' 
  AND title IN (
    'Senior Engineer', 
    'Staff', 
    'Engineer', 
    'Senior Staff', 
    'Assistant Engineer', 
    'Technique Leader', 
    'Manager') 
  AND from_date='1986-06-26'\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: titles
         type: range
possible_keys: PRIMARY
          key: PRIMARY
      key_len: 59
          ref: NULL
         rows: 7
        Extra: Using where
1 row in set (0.00 sec)

SHOW PROFILES\G
*************************** 1. row ***************************
Query_ID: 1
Duration: 0.00032611
   Query: SELECT * FROM employees.titles 
          WHERE emp_no='10001' AND from_date='1986-06-26'
*************************** 2. row ***************************
Query_ID: 2
Duration: 0.00031092
   Query: SELECT * FROM employees.titles
          WHERE emp_no='10001'
            AND title IN (
                'Senior Engineer', 
                'Staff', 
                'Engineer', 
                'Senior Staff', 
                'Assistant Engineer', 
                'Technique Leader',
                 'Manager')
            AND from_date='1986-06-26'
2 rows in set (0.00 sec)

但只有在這種成為 "坑" 的列值比較少的情況下梭姓,才可以考慮用 "IN"來填補(bǔ)這個(gè) "坑" 從而形成最左前綴霜幼。

(四). 匹配某列的前綴字符串

此時(shí)可以用到索引。如果如果通配符 % 不出現(xiàn)在開頭糊昙,則可以用到索引辛掠,但根據(jù)具體情況不同可能只會(huì)用其中一個(gè)前綴谢谦。

EXPLAIN SELECT * 
FROM employees.titles WHERE emp_no='10001' AND title LIKE 'Senior%'\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: titles
         type: range
possible_keys: PRIMARY
          key: PRIMARY
      key_len: 56
          ref: NULL
         rows: 1
        Extra: Using where
1 row in set (0.00 sec)

(五). 范圍查詢

范圍列可以用到索引 (必須是最左前綴)释牺,但是范圍列后面的列無法用到索引。索引最多用于一個(gè)范圍列回挽,因此如果查詢條件中有兩個(gè)范圍列則無法全用到索引没咙。

EXPLAIN SELECT * 
FROM employees.titles WHERE emp_no < '10010' and title='Senior Engineer'\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: titles
         type: range
possible_keys: PRIMARY
          key: PRIMARY
      key_len: 4
          ref: NULL
         rows: 14
        Extra: Using where
1 row in set (0.00 sec)

MySQL 有一個(gè)非常有意思的地方:僅用 EXPLAIN 可能無法區(qū)分范圍索引和多值匹配,因?yàn)樵?type 中這兩者都顯示為 range千劈。同時(shí)用了 "between" 并不意味著就是范圍查詢祭刚。

下面的查詢中,看起來是用了兩個(gè)范圍查詢墙牌,但作用于 emp_no 上的 "BETWEEN" 實(shí)際上相當(dāng)于 "IN"涡驮,也就是說 emp_no 實(shí)際是多值精確匹配∠脖酰可以看到這個(gè)查詢用到了索引全部三個(gè)列捉捅。因此在 MySQL 中要謹(jǐn)慎地區(qū)分多值匹配和范圍匹配,否則會(huì)對(duì) MySQ L的行為產(chǎn)生困惑虽风。

EXPLAIN SELECT * 
FROM employees.titles
WHERE emp_no BETWEEN '10001' AND '10010'
AND title='Senior Engineer'
AND from_date BETWEEN '1986-01-01' AND '1986-12-31'\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: titles
         type: range
possible_keys: PRIMARY
          key: PRIMARY
      key_len: 59
          ref: NULL
         rows: 15
        Extra: Using where
1 row in set (0.00 sec)

(六). 查詢條件中含有函數(shù)或表達(dá)式

如果查詢條件中含有函數(shù)或表達(dá)式棒口,則 MySQL 不會(huì)為這列使用索引寄月。

EXPLAIN SELECT * 
FROM employees.titles WHERE emp_no='10001' AND left(title, 6)='Senior'\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: titles
         type: ref
possible_keys: PRIMARY
          key: PRIMARY
      key_len: 4
          ref: const
         rows: 1
        Extra: Using where
1 row in set (0.00 sec)

二. 索引選擇性

索引雖然加快了查詢速度,但也是有代價(jià)的无牵。索引文件本身要消耗存儲(chǔ)空間漾肮,同時(shí)索引會(huì)增加插入、刪除和修改記錄時(shí)的開銷茎毁。另外 MySQL 在運(yùn)行時(shí)也要消耗資源維護(hù)索引克懊,因此索引并不是越多越好。

一般 3 種情況下不建議使用索引:

  • 第一種情況是表記錄比較少七蜘。沒必要建索引保檐,讓查詢做全表掃描就好。根據(jù)記錄數(shù)不超過 2000 可以考慮不建索引崔梗,超過 2000 條可以酌情考慮索引

  • 第二種情況是表記錄過多夜只,建立和使用索引的代價(jià)將會(huì)增加。這種情況下蒜魄,需要一種技術(shù)可以直接區(qū)分出查詢需要的一組數(shù)據(jù)扔亥,而不是一條一條記錄地匹配√肝可以使用分區(qū)技術(shù)

  • 最后一種情況是索引的選擇性較低旅挤。所謂索引的選擇性是指不重復(fù)的索引值與表記錄數(shù)的比值。顯然選擇性的取值范圍為(0, 1]伞鲫,選擇性越高的索引價(jià)值越大粘茄,這是由 B+Tree 的性質(zhì)決定的

三. 前綴索引

前綴索引是用列的前綴代替整個(gè)列作為索引 key,當(dāng)前綴長(zhǎng)度合適時(shí)秕脓,可以做到既使得前綴索引的選擇性接近全列索引柒瓣,同時(shí)因?yàn)樗饕?key 變短而減少了索引文件的大小和維護(hù)開銷。

對(duì)于 BLOB吠架、TEXT 或者很長(zhǎng)的 VARCHAR 的列芙贫,必須使用前綴索引。MySQL 不允許索引這些列的完整長(zhǎng)度傍药。

前綴索引兼顧索引大小和查詢速度磺平,缺點(diǎn)是不能用于 ORDER BY 和 GROUP BY 操作,也不能用于索引覆蓋掃描 (即當(dāng)索引本身包含查詢所需全部數(shù)據(jù)時(shí)拐辽,不再訪問數(shù)據(jù)文件本身)拣挪。

EXPLAIN SELECT * 
FROM employees.employees WHERE first_name='Eric' AND last_name='Anido'\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: employees
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 300252
        Extra: Using where

SELECT count(DISTINCT(first_name))/count(*) AS Selectivity FROM employees.employees;
+-------------+
| Selectivity |
+-------------+
|      0.0042 |
+-------------+
1 row in set (0.13 sec)

SELECT count(DISTINCT(last_name))/count(*) AS Selectivity FROM employees.employees;
+-------------+
| Selectivity |
+-------------+
|      0.0055 |
+-------------+

SELECT count(DISTINCT(concat(first_name, last_name)))/count(*) AS Selectivity 
FROM employees.employees;
+-------------+
| Selectivity |
+-------------+
|      0.9313 |
+-------------+
1 row in set (0.36 sec)

SELECT count(DISTINCT(concat(first_name, left(last_name, 4))))/count(*) 
  AS Selectivity 
FROM employees.employees;
+-------------+
| Selectivity |
+-------------+
|      0.9007 |
+-------------+
1 row in set (0.36 sec)

ALTER TABLE employees.employees 
  ADD INDEX `first_name_last_name4` (first_name, last_name(4));

EXPLAIN SELECT * 
FROM employees.employees WHERE first_name='Eric' AND last_name='Anido'\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: employees
         type: ref
possible_keys: first_name_last_name4
          key: first_name_last_name4
      key_len: 22
          ref: const,const
         rows: 1
        Extra: Using index condition; Using where
1 row in set (0.00 sec)

SHOW PROFILES\G
*************************** 1. row ***************************
Query_ID: 1
Duration: 0.13175585
   Query: SELECT * 
          FROM employees.employees WHERE first_name='Eric' AND last_name='Anido'
*************************** 15. row ***************************
Query_ID: 2
Duration: 0.00034243
   Query: SELECT * 
          FROM employees.employees WHERE first_name='Eric' AND last_name='Anido'

四. 覆蓋索引

如果一個(gè)索引包含 (或者覆蓋) 所有要查詢的字段的值,就稱其為 "覆蓋索引"俱诸。當(dāng)發(fā)起一個(gè)被索引覆蓋的查詢時(shí)菠劝,在 EXPLAIN 的 Extra 列可以看到 "Using index" 的信息。

覆蓋索引具有如下優(yōu)點(diǎn):

  • 索引條目通常遠(yuǎn)小于數(shù)據(jù)行大小,所以如果只需要讀取索引掰伸,那么 MySQL 就會(huì)極大的減少數(shù)據(jù)訪問量有序。這對(duì)緩存的負(fù)載非常重要贸呢,因?yàn)檫@種情況下響應(yīng)時(shí)間大部分花在數(shù)據(jù)的拷貝上敞曹。覆蓋索引對(duì) I/O 密集型的應(yīng)用同樣有幫助爷恳,因?yàn)樗饕葦?shù)據(jù)更小颤诀,更容易全部放在內(nèi)存中

  • 因?yàn)樗饕前凑樟兄淀樞虼鎯?chǔ)的 (至少在單頁內(nèi)是如此)掂名,所以對(duì)于 I/O 密集型的范圍查詢會(huì)比隨機(jī)從磁盤讀取每一行數(shù)據(jù)的 I/O 要少得多辙喂。對(duì)于某些存儲(chǔ)引擎 捶牢,例如 MyISAM,甚至可以通過 OPTIMIZE 命令使得索引完全順序排列巍耗,這讓簡(jiǎn)單的范圍查詢能使用完全順序的索引訪問

  • 一些存儲(chǔ)引擎如 MyISAM 在內(nèi)存中只緩存索引秋麸,數(shù)據(jù)則依賴于操作系統(tǒng)來緩存,因此要訪問數(shù)據(jù)需要一次系統(tǒng)調(diào)用炬太。這可能會(huì)導(dǎo)致嚴(yán)重的性能問題灸蟆,尤其是那些系統(tǒng)調(diào)用占用 數(shù)據(jù)訪問中的最大開銷的場(chǎng)景

  • 由于 InnoDB 的聚簇索引,覆蓋索引對(duì) InnoDB 表特別有用亲族。InnoDB 的二級(jí)索引在葉子節(jié)點(diǎn)保存了行的主鍵值炒考,所以如果二級(jí)主鍵能夠覆蓋查詢,則可以避免對(duì)主鍵索引的二次查詢

索引覆蓋查詢有很多陷阱可能導(dǎo)致無法實(shí)現(xiàn)優(yōu)化霎迫。MySQL 查詢優(yōu)化器會(huì)在執(zhí)行查詢前判斷是否有一個(gè)索引能進(jìn)行覆蓋斋枢。假設(shè)所有覆蓋了 WHERE 條件中的字段,但不是整個(gè)查詢?cè)O(shè)計(jì)的字段知给。如果條件為假瓤帚,MySQL 5.5 和更早的版本也總是會(huì)回表獲取數(shù)據(jù)行,盡管并不需要這一行且最終會(huì)被過濾掉涩赢。

在大多數(shù)存儲(chǔ)引擎中戈次,覆蓋索引只能覆蓋那些只訪問索引中部分列的查詢。不過可以更進(jìn)一步優(yōu)化 InnoDB谒主。InnoDB 的二級(jí)索引的葉子節(jié)點(diǎn)都包含了主鍵的值朝扼,這意味著 InnoDB 的二級(jí)索可以有效的利用這些 "額外" 的主鍵列來覆蓋查詢。

五. 多列索引

在多個(gè)列上建立獨(dú)立的單列索引在大部分情況下并不能提高 MySQL 的查詢性能霎肯。MySQL5.0 和更新版本引入了 "索引合并" 策略,一定程度上可以使用表上的多個(gè)單列索引來定位指定的行榛斯。查詢能夠同時(shí)使用多個(gè)單列索引進(jìn)行掃描观游,并將結(jié)果進(jìn)行合并。這種算法有三個(gè)變種:OR 條件的聯(lián)合驮俗、AND 條件的相交懂缕、組合前兩種情況的聯(lián)合及相交。

索引合并策略有時(shí)候是一種優(yōu)化王凑,但更多的時(shí)候說明了表上的索引建的很糟糕:

  • 當(dāng)出現(xiàn)服務(wù)器對(duì)多個(gè)索引做相交操作時(shí)搪柑,通常意味著需要一個(gè)包含所有相關(guān)列的多列索引聋丝,而不是多個(gè)獨(dú)立的單列索引

  • 當(dāng)出現(xiàn)服務(wù)器對(duì)多個(gè)索引做聯(lián)合操作時(shí),通常需要耗費(fèi)大量的 CPU 和內(nèi)存資源在算法的緩存工碾、排序和合并操作上弱睦。特別是當(dāng)其中有些索引的選擇性不高,需要合并掃描返回的大量數(shù)據(jù)的時(shí)候

  • 優(yōu)化器不會(huì)把這些計(jì)算到 "查詢成本" 中渊额,優(yōu)化器只關(guān)心隨機(jī)頁面讀取况木。這會(huì)使得查詢成本被低估

如果在 EXPLAIN 中看到有索引合并,應(yīng)該好好檢查下查詢和表的結(jié)構(gòu)是不是已經(jīng)是最優(yōu)的旬迹。也可以通過參數(shù) optimizer_switch 來關(guān)閉所有合并功能火惊。同時(shí)也可以使用 IGNORE INDEX 提示讓優(yōu)化器忽略掉某些索引。

五. 索引順序

索引列的正確順序依賴于使用該索引的查詢奔垦,并且同時(shí)需要考慮如何更好地滿足排序和分組的需要屹耐。在一個(gè)包含多列的索引中,索引列的順序意味著所有首先按照最左列進(jìn)行排序椿猎,其次是第二列张症,等等。索引可以按照升序或降序進(jìn)行掃描鸵贬,以滿足精確符合列順序的 ORDER BY俗他、GROUP BY 和 DISTINCT 等子句的查詢要求。

當(dāng)不需要考慮排序和分組時(shí)阔逼,將選擇性最高的列放在前面通常是很好的兆衅。這時(shí)候索引的作用只是用于優(yōu)化 WHERE 條件的查詢。在這種情況下嗜浮,這樣設(shè)計(jì)的索引確實(shí)能夠最快地過濾出需要的行羡亩,對(duì)于在 WHERE 子句中只使用了索引部分前綴列的查詢來說選擇性也更高。然而危融,性能并不只是依賴于所有索引列的選擇性畏铆,也和查詢條件的具體值有關(guān),也就是和值的分布有關(guān)吉殃〈蔷樱可能需要根據(jù)那些運(yùn)行頻率最高的查詢來調(diào)整索引列的順序,讓這種情況下索引的選擇性最高蛋勺。

六. 索引排序

MySQL 有兩種方式可以生成有序的結(jié)果:通過排序操作瓦灶、通過索引順序掃描。如果 EXPLAIN 的 type 列的值為 "index"抱完,則說明 MySQL 使用了索引掃描來做排序贼陶。

掃描索引本身是很快的,因?yàn)橹恍鑿囊粭l索引記錄移動(dòng)到緊接著的下一條記錄。但如果索引不能覆蓋查詢所需的全部列碉怔,那就不得不掃描一條索引記錄都回表查詢一次對(duì)應(yīng)的行烘贴。這基本上都是隨機(jī) I/O 操作,因此按索引順序讀取數(shù)據(jù)的速度通常要比順序地全表掃描慢撮胧,尤其是在 I/O 密集型的工作負(fù)載時(shí)桨踪。

只有當(dāng)索引的列順序和 ORDER BY 子句的順序完全一致,并且所有列的排序方向都一樣時(shí)趴樱,MySQL 才能夠使用索引來對(duì)結(jié)果進(jìn)行排序馒闷。如果查詢需要關(guān)聯(lián)多張表,則只有當(dāng) ORDER BY 子句引用的字段全部為第一個(gè)表時(shí)叁征,才能使用索引做排序纳账。ORDER BY 子句和查找型查詢的限制是一樣的:需要滿足索引的最左前綴的要求;否則捺疼,MySQL 都需要執(zhí)行排序操作疏虫,而無法利用索引排序。

有一種情況下的 ORDER BY 子句可以不滿足索引的最左前綴的要求啤呼,就是前導(dǎo)列為常量的時(shí)候卧秘。如果 WHERE 子句或者 JOIN 子句中對(duì)這些列指定了常量,就可以 "彌補(bǔ)" 索引的不足官扣。

CREATE TABLE rental(
    PRIMARY KEY (rental_id),
    UNIQUE KEY rental_data (rental_data, inventory_id, customer_id)
);

SELECT rental_id, staff_id 
FROM rental
WHERE rental_data = '2005-05-25'
ORDER BY inventory_id, customer_id

六. 哈希索引

如果存儲(chǔ)引擎不支持哈希索引翅敌,可以在 B-Tree 基礎(chǔ)上創(chuàng)建一個(gè)偽哈希索引。這和真正的哈希索引不是一回事惕蹄,因?yàn)檫€是使用 B-Tree 進(jìn)行查找蚯涮,但它使用哈希值而不是鍵值進(jìn)行索引查找。需要做的就是在查詢的 WHERE 子句中手動(dòng)指定哈希函數(shù)卖陵。這樣實(shí)現(xiàn)的缺陷是需要維護(hù)哈希值 (可以手動(dòng)維護(hù)遭顶,也可以用觸發(fā)器實(shí)現(xiàn))。

-- 創(chuàng)建如下表
CREATE TABLE pseudohash(
    id int unsigned NOT NULL auto_increment,
    url varchar(255) NOT NULL,
    url_crc int unsigned NOT NULL DEFAULT 0,
    PRIMARY KEY(id)
);

-- 創(chuàng)建觸發(fā)器 (先臨時(shí)修改一下語句分隔符泪蔫,這樣可以在觸發(fā)器定義中使用分號(hào))
DELIMITRE //

CREATE TRIGGER pseudohash_crc_ins BEFORE INSERT ON pseudohash FOR EACH ROW BEGIN
SET NEW.url_crc=crc32(NEW.url);
END;
//

CREATE TRIGGER pseudohash_crc_upd BEFORE UPDATE ON pseudohash FOR EACH ROW BEGIN
SET NEW.url_crc=crc32(NEW.url);
END;
//

DELIMITER ;

如果采用自定義哈希索引的方式棒旗,不要使用 SHA1() 和 MD5() 作為哈希函數(shù)。因?yàn)檫@兩個(gè)函數(shù)計(jì)算出來的哈希值是非常長(zhǎng)的字符串撩荣,會(huì)浪費(fèi)大量空間铣揉。如果數(shù)據(jù)表非常大,CRC32() 會(huì)出現(xiàn)大量的哈希沖突婿滓,則可以考慮自己實(shí)現(xiàn)一個(gè)簡(jiǎn)單的 64 位哈希函數(shù)老速。這個(gè)自定義的函數(shù)要返回整數(shù),而不是字符串凸主。一個(gè)簡(jiǎn)單的辦法可以使用 MD5() 函數(shù)返回值的一部分來作為自定義函數(shù)。

當(dāng)使用哈希索引進(jìn)行查詢時(shí)额湘,要避免哈希沖突的問題卿吐,必須在 WHERE 條件中帶入哈希值和對(duì)應(yīng)列值旁舰。如果不是想查詢具體值,例如只是統(tǒng)計(jì)記錄數(shù)則可以不帶入列值嗡官,直接使用 CRC32() 的哈希值查詢即可箭窜。

五. 主鍵選擇

使用 InnoDB 存儲(chǔ)引擎時(shí),如果沒有特別的需要衍腥,請(qǐng)永遠(yuǎn)使用一個(gè)與業(yè)務(wù)無關(guān)的自增字段作為主鍵磺樱。從數(shù)據(jù)庫(kù)索引優(yōu)化角度看,使用 InnoDB 引擎而不使用自增主鍵絕對(duì)是一個(gè)糟糕的主意婆咸。

InnoDB 使用聚簇索引竹捉,數(shù)據(jù)記錄被存放于主鍵索引的葉子節(jié)點(diǎn)上。這要求同一個(gè)葉子節(jié)點(diǎn)內(nèi)的各條數(shù)據(jù)記錄按主鍵順序存放尚骄。每當(dāng)有一條新的記錄插入時(shí)块差,MySQL 會(huì)根據(jù)其主鍵將其插入適當(dāng)?shù)墓?jié)點(diǎn)和位置。如果頁面達(dá)到裝載因子 (默認(rèn)為15/16)倔丈,則開辟一個(gè)新的頁 (節(jié)點(diǎn))憨闰。

如果表使用自增主鍵,那么每次插入新的記錄需五,記錄就會(huì)順序添加到當(dāng)前索引節(jié)點(diǎn)的后續(xù)位置鹉动,當(dāng)一頁寫滿,就會(huì)自動(dòng)開辟一個(gè)新的頁宏邮。這樣就會(huì)形成一個(gè)緊湊的索引結(jié)構(gòu)泽示,近似順序填滿。每次插入時(shí)也不需要移動(dòng)已有數(shù)據(jù)蜀铲,因此效率很高边琉,也不會(huì)增加很多開銷在維護(hù)索引上。

如果使用非自增主鍵记劝,每次插入主鍵的值近似于隨機(jī)变姨,因此新增的紀(jì)錄都要被插到現(xiàn)有索引頁得中間某個(gè)位置。此時(shí) MySQL 不得不為了將新記錄插到合適位置而移動(dòng)數(shù)據(jù)厌丑,甚至目標(biāo)頁可能已經(jīng)被回寫到磁盤上而從緩存中清掉定欧。此時(shí)又要從磁盤上讀回來,這增加了很多開銷怒竿。同時(shí)頻繁的移動(dòng)砍鸠、分頁操作造成了大量的碎片,使得索引結(jié)構(gòu)不夠緊湊耕驰,不得不通過 OPTIMIZE TABLE 來重建表并優(yōu)化填充頁面爷辱。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子饭弓,更是在濱河造成了極大的恐慌双饥,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,454評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件弟断,死亡現(xiàn)場(chǎng)離奇詭異咏花,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)阀趴,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,553評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門昏翰,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人刘急,你說我怎么就攤上這事棚菊。” “怎么了排霉?”我有些...
    開封第一講書人閱讀 157,921評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵窍株,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我攻柠,道長(zhǎng)球订,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,648評(píng)論 1 284
  • 正文 為了忘掉前任瑰钮,我火速辦了婚禮冒滩,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘浪谴。我一直安慰自己开睡,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,770評(píng)論 6 386
  • 文/花漫 我一把揭開白布苟耻。 她就那樣靜靜地躺著篇恒,像睡著了一般。 火紅的嫁衣襯著肌膚如雪凶杖。 梳的紋絲不亂的頭發(fā)上胁艰,一...
    開封第一講書人閱讀 49,950評(píng)論 1 291
  • 那天,我揣著相機(jī)與錄音智蝠,去河邊找鬼腾么。 笑死,一個(gè)胖子當(dāng)著我的面吹牛杈湾,可吹牛的內(nèi)容都是我干的解虱。 我是一名探鬼主播,決...
    沈念sama閱讀 39,090評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼漆撞,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼殴泰!你這毒婦竟也來了于宙?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,817評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤艰匙,失蹤者是張志新(化名)和其女友劉穎限煞,沒想到半個(gè)月后抹恳,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體员凝,經(jīng)...
    沈念sama閱讀 44,275評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,592評(píng)論 2 327
  • 正文 我和宋清朗相戀三年奋献,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了健霹。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,724評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡瓶蚂,死狀恐怖糖埋,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情窃这,我是刑警寧澤瞳别,帶...
    沈念sama閱讀 34,409評(píng)論 4 333
  • 正文 年R本政府宣布,位于F島的核電站杭攻,受9級(jí)特大地震影響祟敛,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜兆解,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,052評(píng)論 3 316
  • 文/蒙蒙 一馆铁、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧锅睛,春花似錦埠巨、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,815評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至印蔬,卻和暖如春勋桶,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背扛点。 一陣腳步聲響...
    開封第一講書人閱讀 32,043評(píng)論 1 266
  • 我被黑心中介騙來泰國(guó)打工哥遮, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人陵究。 一個(gè)月前我還...
    沈念sama閱讀 46,503評(píng)論 2 361
  • 正文 我出身青樓眠饮,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親铜邮。 傳聞我的和親對(duì)象是個(gè)殘疾皇子仪召,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,627評(píng)論 2 350

推薦閱讀更多精彩內(nèi)容