一. 最左前綴原理
以 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)化填充頁面爷辱。