最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

MySQL選錯索引的原因以及解決方案

 更新時間:2020年10月13日 11:57:42   作者:以終為始  
這篇文章主要介紹了MySQL選錯索引的原因以及解決方案,幫助大家更好的理解和使用MySQL索引,感興趣的朋友可以了解下

MySQL 中,可以為某張表指定多個索引,但在語句具體執(zhí)行時,選用哪個索引是由 MySQL 中執(zhí)行器確定的。那么執(zhí)行器選擇索引的原則是什么,以及會不會出現(xiàn)選錯索引的情況呢?

先看這樣一個例子:

創(chuàng)建表 Y,設置兩個普通索引, 創(chuàng)建一個存儲過程用于插入數(shù)據(jù)。

MySQL: 5.7.27, 隔離級別: RR

CREATE TABLE `Y` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
 `a` int(11) DEFAULT NULL,
 `b` int(11) DEFAULT NULL,
 PRIMARY KEY (`id`),
 KEY `a` (`a`),
 KEY `b` (`b`)
) ENGINE=InnoDB;
delimiter ;;
create procedure idata()
begin
 declare i int;
 set i=1;
 while(i<=100000)do
   insert into Y (`a`,`b`) values(i, i);
  set i=i+1;
 end while;
end;;
delimiter ;
call idata();

查看如下事務:

Session A Session B
start transaction with consistent snapshot;
delete from t;
call idata();
explain select * from Y where a between 10000 and 20000;
explain select * from Y force index(a) where a between 10000 and 20000;
commit;

如果單獨執(zhí)行 Session B 中 select * from Y where a between 10000 and 20000;,毫無疑問會選擇 a 這個索引。

但如果安裝 Session A,Session B 的順序執(zhí)行,發(fā)現(xiàn)索引的選擇如下:

可以發(fā)現(xiàn),在 Session B 的場景下,執(zhí)行器卻沒有選擇 a 所在的索引,而是選擇基于主鍵索引的全表掃描。

set long_query_time=0;
--將慢查詢日志打開,并將闕值設為 0. 在記錄的日志中,可以發(fā)現(xiàn) MySQL 并沒有選擇 a 所在的索引,同時花費了更長的時間。

這樣看,MySQL 的優(yōu)化器不一定每次都能選擇合適的索引。想要理解出現(xiàn)該現(xiàn)象的原因,就要從優(yōu)化器的選擇邏輯說起。

優(yōu)化器

MySQL 中優(yōu)化器的目的就是找到一個最優(yōu)的執(zhí)行方案,從而用最小的代價去執(zhí)行語句。

優(yōu)化器在選擇索引時,主要會考慮如下的因素:

  • 掃描的行數(shù):掃描的行數(shù)越少,就證明訪問磁盤數(shù)據(jù)的次數(shù)越少,消耗的 CPU 資源就越少。
  • 有沒有涉及到臨時表
  • 排序

關于掃描行數(shù)的確定

計算索引的基數(shù)

MySQL 在執(zhí)行語句前,其實并不能準確的計算出掃描的行數(shù),而是通過數(shù)學統(tǒng)計信息來估算記錄數(shù)。這個統(tǒng)計信息被稱為索引的“區(qū)分度”,在索引上不同的值越多,區(qū)分度就越高。在一個索引上不同值的個數(shù),稱為“基數(shù)”?;鶖?shù)越大,索引的區(qū)分度越好。

這里的 Cardinality 就是索引的基數(shù),但基數(shù)并不是完全準確的。MySQL 是在獲取基數(shù)時,實際上是采用采樣統(tǒng)計的方式。

計算時,會選擇 N 個數(shù)據(jù)頁,并統(tǒng)計這些頁面上的不同值,得到一個平均值,然后乘以該索引的頁面數(shù),然后得到的就是索引的基數(shù)。

在 MySQL 中,有兩種存儲索引的方式,可通過設置 innodb_stats_persistent 來切換:

  • on 時:表示統(tǒng)計信息會持久化存儲,默認 N 為 20,M 為 10.
  • off 時,統(tǒng)計信息僅會存儲在內存中,默認 N 為 8,M 為 16.

由于表中數(shù)據(jù)是不斷變化的,所以當更新的值超過 1/M 時,會自動觸發(fā)索引統(tǒng)計。

但需要注意的是,由于是采樣統(tǒng)計,所以基數(shù)的值不是準確的。

預估掃描行數(shù)的錯誤

之前看到,執(zhí)行 Select * from Y where a between 10000 and 20000 預估的行數(shù)是 100015,這個是能理解的,因為走的是全表掃描。

之后執(zhí)行 select * from Y force index(a) where a between 10000 and 20000 預估的行數(shù)是 37116,這個就不能理解了,理想的情況下應該是 10001 行 (需要遍歷到 20001)。

而且更奇怪的是,雖然 37116 行的預估行數(shù)不太合理,但也遠小于全表掃描的 100015,為什么優(yōu)化器還是選擇全表掃描呢?

首先先看第二個問題,選擇 100015 的原因是因為如果使用索引 a 的話,除了需要在 a 索引掃描外,還需要回表,主鍵索引上的查詢代價,優(yōu)化器也需要算進去,所以選擇了全表掃描。

這時再看第一個問題,為什么沒有得到正確的行數(shù)。這個就和一致性視圖有關了,首先 Session A 中,開啟了一致性視圖,并沒有提交。之后的 Session 清空了 Y 表后,又重新創(chuàng)建了相同的數(shù)據(jù),這時每行數(shù)據(jù)都有兩個版本,舊版本是 delete 前的數(shù)據(jù),新版本是標記為刪除的數(shù)據(jù)。所以索引 a 上的數(shù)據(jù)其實有兩份。也就造成了行數(shù)的預估錯誤。

mysql 是通過標記刪除的方法來刪除記錄的,并不是在索引和數(shù)據(jù)文件中真正的刪除。而且由于一致性讀的保證,不能刪除 delete 的空間,再加上 insert 的空間。導致統(tǒng)計信息有誤。

選用錯誤索引的解決辦法

對于行數(shù)預估錯誤的情況, 可采用如下的方法:

如果遇到 EXPLAIN 和預估的行數(shù),數(shù)值相差較大時,可以通過analyze table 來重新統(tǒng)計索引信息。

直接通過 force index 強制指定需要使用的索引,不讓優(yōu)化器進行判斷。但使用 force 也可能帶來一些問題:

  • 遷移數(shù)據(jù)庫時,語法不支持
  • 不容易變更并且不太方便,因為選錯索引的情況一般不會經(jīng)常發(fā)生,在生產環(huán)境出現(xiàn)問題后,才需要改代碼,但還需要重新進行上線測試,部署。

優(yōu)化 SQL 語句,引導優(yōu)化器使用正確的索引

再看一個類似的例子:

先來看一下這句

SQL select * from Y where a between 1 and 1000 and b between5000 100000 order by b limit 1;

在執(zhí)行這句話時,可以選索引 a,也可以選索引 b. 我們知道,每個索引對應了一顆B+樹。這里由于取得是 a 和 b 的交集,如果選用索引 a 的話,需要遍歷 1 - 10001 行。選用索引 b 需要遍歷 50000 - 100001 行。理論上來說,應該選擇 a 作為索引,可以優(yōu)化器又偏偏選擇了 b 作為索引。

這里選擇 b 作為索引的原因,是因為優(yōu)化器看到了后面的 order by 語句,由于要排序,而 B+ 樹本身就是有序的,省去了排序的過程,所以選擇了 b 作為索引。

但從實際的執(zhí)行時間來看,索引 a 執(zhí)行時間更短,所以這里 MySQL 又選擇了錯誤的索引。

我們可以將上述語句中 order by b limit 改為 order by b,a limit 1 這時由于 a,b 索引都要排序,掃描的行數(shù)就成為執(zhí)行器主要參考的條件,引導選擇正確的索引。

這樣做的前提一定要保證執(zhí)行的邏輯結果是一致的,比如在 limit 1 的情況下,order by b,a order by b 的結果一致,如果換成 limit 100 就不一定了。

還有一種改發(fā)

select * from (select * from t where (a between 1 and 1000) and (b between 50000 and 100000) order by b limit 100)alias limit 1;

現(xiàn)在可以看到,優(yōu)化器選擇了合適的索引。原因在于 limit 100 讓優(yōu)化器認為,使用索引 b 的代價較高,進而選擇索引 a. 其實就是通過 limit 100 誘導優(yōu)化器做出選擇。

調整索引

能否找到更優(yōu),更合適的索引,或者利用索引的原則,刪除一些不必要的索引。

總結

現(xiàn)在我們知道,MySQL 在選擇索引時,是會出現(xiàn)錯誤的情況的。優(yōu)化器選擇索引的原則主要有三個,掃描的行數(shù),是否存在臨時表,以及排序。行數(shù)的掃描,主要和基數(shù)有關,而基數(shù)的統(tǒng)計則是通過統(tǒng)計抽樣決定的,進而預估的行數(shù)可能會是不準確的。

在遇到掃描的行數(shù)不正確時,可以通過 analyze table 來重新統(tǒng)計表的信息,通過 force index 強制指定索引,或通過手動改變 sql 的語義,誘導優(yōu)化器做出正確的選擇。

以上就是MySQL選錯索引的原因以及解決方案的詳細內容,更多關于MySQL 索引的資料請關注腳本之家其它相關文章!

您可能感興趣的文章:

相關文章

  • MySQL數(shù)據(jù)表基本操作實例詳解

    MySQL數(shù)據(jù)表基本操作實例詳解

    這篇文章主要介紹了MySQL數(shù)據(jù)表基本操作,結合實例形式較為詳細的分析了MySQL針對數(shù)據(jù)表的基本創(chuàng)建、表結構查看、修改、刪除等相關操作技巧,需要的朋友可以參考下
    2018-06-06
  • 詳解數(shù)據(jù)庫多表連接查詢的實現(xiàn)方法

    詳解數(shù)據(jù)庫多表連接查詢的實現(xiàn)方法

    這篇文章主要介紹了詳解數(shù)據(jù)庫多表連接查詢的實現(xiàn)方法的相關資料,希望通過本文大家能夠掌握數(shù)據(jù)庫多表查詢的方法,需要的朋友可以參考下
    2017-09-09
  • 一鍵清空(重置)本地MySQL8.0密碼腳本

    一鍵清空(重置)本地MySQL8.0密碼腳本

    這篇文章主要介紹了一鍵清空本地MySQL8.0密碼腳本,再也不用擔心MySQL密碼忘記了,很容易的解決了忘記mysql密碼的煩惱,操作方法也非常簡單,需要的朋友可以參考下
    2023-01-01
  • MySQL批量更新的四種方式總結

    MySQL批量更新的四種方式總結

    最近需要批量更新大量數(shù)據(jù),習慣了寫sql,所以還是用sql來實現(xiàn),下面這篇文章主要給大家總結介紹了關于MySQL批量更新的四種方式,需要的朋友可以參考下
    2023-01-01
  • mysql的約束及實例分析

    mysql的約束及實例分析

    這篇文章主要介紹了mysql的約束及實例分析,真正約束字段的是數(shù)據(jù)類型,但是數(shù)據(jù)類型約束很單一,需要有一些額外的約束,更好的保證數(shù)據(jù)的合法性,從業(yè)務邏輯角度保證數(shù)據(jù)的正確性,需要的朋友可以參考下
    2023-07-07
  • mysql 8.0 Windows zip包版本安裝詳細過程

    mysql 8.0 Windows zip包版本安裝詳細過程

    這篇文章主要為大家詳細介紹了mysql 8.0 Windows zip包版本安裝詳細過程,以及密碼認證插件修改,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-05-05
  • mysql修改數(shù)據(jù)庫默認路徑無法啟動問題的解決

    mysql修改數(shù)據(jù)庫默認路徑無法啟動問題的解決

    這篇文章主要給大家介紹了關于mysql修改數(shù)據(jù)庫默認路徑無法啟動問題的解決方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2018-11-11
  • MySQL插入不了中文數(shù)據(jù)問題的原因及解決

    MySQL插入不了中文數(shù)據(jù)問題的原因及解決

    最近發(fā)現(xiàn)新安裝的MySQL數(shù)據(jù)庫不能插入中文字段,所以下面這篇文章主要給大家介紹了關于MySQL插入不了中文數(shù)據(jù)問題的原因及解決方法,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下
    2023-05-05
  • MySQL中查詢日志與慢查詢日志的基本學習教程

    MySQL中查詢日志與慢查詢日志的基本學習教程

    這篇文章主要介紹了MySQL中查詢日志與慢查詢日志的基本學習教程,文中還提到了MySQL自帶的Mysqldumpslow日志分析工具的使用,需要的朋友可以參考下
    2015-12-12
  • MySQL數(shù)據(jù)遷移使用MySQLdump命令

    MySQL數(shù)據(jù)遷移使用MySQLdump命令

    今天小編就為大家分享一篇關于MySQL數(shù)據(jù)遷移使用MySQLdump命令,小編覺得內容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧
    2018-10-10

最新評論

渑池县| 肇东市| 沙河市| 通州区| 循化| 章丘市| 泰州市| 西乌珠穆沁旗| 泸定县| 疏勒县| 雷山县| 永丰县| 温泉县| 贺州市| 黄浦区| 长顺县| 南昌县| 夹江县| 廊坊市| 韶关市| 会宁县| 乐平市| 宜阳县| 平阴县| 团风县| 玉屏| 绥滨县| 永修县| 阳春市| 镇沅| 镇宁| 社会| 柏乡县| 郎溪县| 远安县| 丰台区| 大邑县| 双流县| 武平县| 沙河市| 观塘区|