CPU 成本:將數據讀入內存後,還要檢測數據是否知足條件和排序等 CPU 操做的成本,顯然它與行數有關,默認狀況下,檢測記錄的成本是 0.2。
實例說明
爲了根據以上兩個成原本算出使用索引的最終成本,咱們先準備一個表(如下操做基於 MySQL 5.7.18)code
CREATE TABLE person ( id bigint(20) NOT NULL AUTO_INCREMENT, name varchar(255) NOT NULL, score int(11) NOT NULL, create_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY name_score (name(191),score), KEY create_time (create_time) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; personidnamescorecreate_timeidname_scorenamescorecreate_timecreate_time
這個表除了主鍵索引以外,還有另外兩個索引, name_score 及 create_time。而後咱們在此表中插入 10 w 行數據,只要寫一個存儲過程調用便可,以下:
CREATE PROCEDURE insert_person() begin declare c_id integer default 1; while c_id<=100000 do insert into person values(c_id, concat('name',c_id), c_id+100, date_sub(NOW(), interval c_id second)); set c_id=c_id+1; end while; end
插入以後咱們如今使用 EXPLAIN 來計算下統計總行數到底使用的是哪一個索引
EXPLAIN SELECT COUNT(*) FROM person
從結果上看它選擇了 create_time 輔助索引,顯然 MySQL 認爲使用此索引進行查詢成本最小,這也是符合咱們的預期,使用輔助索引來查詢確實是性能最高的!
咱們再來看如下 SQL 會使用哪一個索引
SELECT * FROM person WHERE NAME >'name84059' AND create_time>'2020-05-23 14:39:18'
用了全表掃描!理論上應該用 name_score 或者 create_time 索引纔對,從 WHERE 的查詢條件來看確實都能命中索引,那是不是使用 SELECT * 形成的回表代價太大所致呢,咱們改爲覆蓋索引的形式試一下
SELECT create_time FROM person WHERE NAME >'name84059' AND create_time > '2020-05-23 14:39:18'
結果 MySQL 依然選擇了全表掃描!這就比較有意思了,理論上採用了覆蓋索引的方式進行查找性能確定是比全表掃描更好的,爲啥 MySQL 選擇了全表掃描呢,既然它認爲全表掃描比使用覆蓋索引的形式性能更好,那咱們分別用這二者執行來比較下查詢時間吧
-- 全表掃描執行時間: 4.0 ms SELECT create_time FROM person WHERE NAME >'name84059' AND create_time>'2020-05-23 14:39:18'
-- 使用覆蓋索引執行時間: 2.0 ms SELECT create_time FROM person force index(create_time) WHERE NAME >'name84059' AND create_time>'2020-05-23 14:39:18'
從實際執行的效果看使用覆蓋索引查詢比使用全表掃描執行的時間快了一倍!說明 MySQL 在查詢前作的成本估算不許!咱們先來看看 MySQL 作全表掃描的成本有多少。
前面咱們說了成本主要 IO 成本和 CPU 成本有關,對於全表掃描來講也就是分別和聚簇索引佔用的頁面數和表中的記錄數。執行如下命令
SHOW TABLE STATUS LIKE 'person'
能夠發現
行數是 100264,咱們不是插入了 10 w 行的數據了嗎,怎麼算出的數據反而多了,其實這裏的計算是估算,也有可能這裏的行數統計出來比 10 w 少了,估算方式有興趣你們去網上查找,這裏不是本文重點,就不展開了。得知行數,那咱們知道 CPU 成本是 100264 * 0.2 = 20052.8。
這個結果對不對呢,咱們能夠用一個工具驗證一下。在 MySQL 5.6 及以後的版本中,咱們能夠用 optimizer trace 功能來查看優化器生成計劃的整個過程 ,它列出了選擇每一個索引的執行計劃成本以及最終的選擇結果,咱們能夠依賴這些信息來進一步優化咱們的 SQL。
optimizer_trace 功能使用以下
SET optimizer_trace="enabled=on";
SELECT create_time FROM person WHERE NAME >'name84059' AND create_time > '2020-05-23 14:39:18';
SELECT * FROM information_schema.OPTIMIZER_TRACE;
SET optimizer_trace="enabled=off";