探索索引的奧秘 有索引就一定會用麼?

2021-08-13 18:55:09 字數 3573 閱讀 3822

> 索引設定為unusable,會有以下特點,

1. 索引設定為unusable,此時會刪除索引段。

2. 索引處於unusable期間,對錶資料做dml操作,此時不維護索引。

3. 索引處於unusable期間,優化器會忽略此索引。

4. 索引處於unusable期間,由於不需要維護索引,因此可以提公升批量匯入效能。

5. 索引unusable變為usable,有兩種方法,一種是刪除-重建索引,一種是使用alter index ... rebuild,兩種方法,都相當於重新構建了索引。

> 索引設定為invisible,會有以下特點,

1. 索引設定為invisible,不會刪除索引段。

2. 索引處於invisible期間,對錶資料做dml操作,此時會維護索引。

3. 索引處於invisible期間,優化器會忽略此索引。

4. 索引invisible變為visible,直接使用alter index ... visible。

> unusable比invisible優先順序要高,同時設定,起作用的是unusable。

> 只有函式索引可以設定disable和enable,涉及函式索引維護的操作,會被禁止,且執行計畫,不會用這索引。

關於索引,還有一些存在模糊的知識點,這篇文章我們關注的是,是否有索引,就一定會用索引?

我們時常會碰見這種問題,

我們建立了索引,但為什麼sql未使用這個索引?

oracle 10g之前預設的優化器模式,是rbo,資料的訪問效率,會參考一些規則,說白了就是一些硬編碼,定義了優先順序,優先順序高的,認為效率就高,例如索引就比全表掃瞄效率高,如下是優先順序1-15的列表,最快的是基於rowid的訪問,最慢的則是全表掃瞄,

但oracle 10g開始,優化器預設模式就是cbo了,c表示的就是cost,即以成本為依據,結合物件的統計資訊,誰的成本值低,誰的效率就高,相比rbo,這樣更科學些,當然有些前提,例如統計資訊要準確。

我們對一張表建立了索引,但並不代表sql一定會用索引,究其原因可能有很多種情況,下面列舉出兩個場景,對於這樣的問題,嘗試提供一些思路和方法。

場景一:正確的有索引卻不用

建立測試表,插入一條資料,建立索引,採集表和索引的統計資訊,user_tables檢視顯示有1條記錄,平均行長為14位元組。

執行update語句,條件是索引欄位id,執行計畫顯示,對錶的掃瞄,用全表掃瞄而不是索引掃瞄,

如果各位對索引的結構,比較了解的話,就比較容易理解其原因了,我們此處用的是btree索引,即平衡二叉樹索引,他的結構類似一棵樹形,有根節點、分支節點,以及葉子結點,唯一索引和非唯一索引,葉子結點儲存的資訊會略有不同,我們此處建立的是非唯一索引,因此葉子結點中儲存的,則是索引字段鍵值,以及對應的rowid,rowid是乙個偽列,通過他可以快速定位,一條記錄對應的物理位置,因為他的資訊包括了,這條記錄對應的檔案號、塊號、行號等資訊,rowid的訪問cbo時代他的優先順序是最高的,關於rowid,內容其實還是很豐富的,有機會我們再聊。

再說索引結構,為什麼說索引快,主要就是因為索引的查詢,就是以這棵樹的根節點開始,找分支節點,如果等值查詢,則可以直接定位到具體的葉子結點,如果是範圍查詢,因為葉子結點是排序的,因此只要找出起始節點,按照葉子結點的指標,就可以找出對應結果集,無論何種用法,我們可以看出,他的執行路徑都是有限的,根節點-分支節點-葉子結點,而且即使表的資料量再增加,只要索引數層級不變,其消耗的代價就是穩定的,而全表掃瞄,則會隨著表資料量的增加,高水位不斷上公升,導致增加的成本消耗。

但一些情況下,索引掃瞄效率未必高,比如上面實驗,因為要是sql語句需要的資料,除了索引欄位外,還有其他字段,則首先使用索引掃瞄,定位葉子結點,根據其中儲存的rowid,回表找出對應的其他字段資訊,而且若是index range scan這種索引範圍掃瞄,會是單塊讀,而全表掃瞄則是多塊讀,相比之下,1次io讀的資料塊數量就不同,對應的資料量就不同,效率就會不同,如果使用全表掃瞄,由於只有1條記錄,則可以1次io就完成資料讀取。如果使用索引掃瞄,則先要消耗io掃瞄索引,再回表消耗io讀取資料,成本高於全表。

雖然此處用了1條記錄測試,有些極端,但即使有很多記錄,還是需要綜合考慮多塊讀、單塊讀、表的記錄數、平均行長、回表等各種因素,只要table access full的成本值低,無論是否有索引,都會選擇table access full。如果要用科學的資料,則可以做乙個10053事件,就可以看出全表掃瞄和索引掃瞄兩種方法對應的成本計算過程和結果,了解oracle自己的選擇。

場景二:錯誤的有索引卻不用

我們接著插入10000條記錄,但不執行統計資訊更新,user_tables檢視顯示表只有1條記錄,可實際此時應該有10001條記錄了。

我們執行comment表操作,讓oracle重新生成執行計畫,但發現還是採用了全表掃瞄,

其實我們就可以看出問題,table access full會掃瞄所有資料,但此處rows值是1,說明oracle認為表記錄只有1條,自然table access full是比較合適的選擇,無可厚非。

接下來我們用乙個11g推出的工具,sta(sql tuning advisor),來看看此時oracle可以給我們什麼建議,首先建立任務,其中sql_id是我們執行update語句對應的sqlid,

接著執行report_tuning_task輸出建議結果,請注意要是不設定開始的set,則可能結果顯示為空,

內容如下,表示oracle對這條sql有兩個建議,

第乙個建議是,手工採集表和索引的統計資訊,並且給出了sql語句,

第二個建議,則是使用sql profile,固定執行計畫,

並且給出了按照原始sql,以及使用了sql profile的sql,各執行10次的統計資訊平均值資料,原始sql用的table access full,

使用sql profile的sql,用的索引掃瞄index range scan,

可以看出,通過sql tuning advisor,可以讓oracle來提供一些優化建議,並且直接給出了一些方法sql,能輔助我們進行優化工作。

接下來針對實驗問題,我們採用手工收集統計資訊,再次執行,就會發現sql用了索引範圍掃瞄,相應地可以看10053事件,就會發現索引的成本,此時就會低於table access full,

總結:1. cbo時代,並不是有了索引,就一定會用索引,能不能用上,需要看誰的成本更低,影響成本值計算的因素很多,本文的問題,只有1條記錄的時候,不用索引是對的,因為多塊讀的全表掃瞄,成本低於單塊讀的索引掃瞄(需要回表),但當有10001條記錄的時候,不用索引就是錯誤的了,原因就是由於統計資訊不准,造成oracle計算成本值出現偏差,此時要麼手工採集統計資訊,要麼使用sql profile固化執行計畫,當然有索引但不用的場景,還有其他的因素,具體問題具體分析。

2. 像本文中,灌入大量資料,此時需要手工收集統計資訊,才能保證oracle估算成本值的正確,雖然oracle有自動收集統計資訊的job,但前提是要求這張表,當日的增刪改資料量超過表總量的10%(引數可以調整),或者執行過truncate操作,可以參考dbsnake的書,而且每晚定時才能執行,因此之前這段時間其實是有可能使用了錯誤的執行計畫,這就會有一些***。

3. sql tuning advisor工具,可以讓oracle為我們優化sql提出一些建議,自動化指出一些方向,還是比較有用的一種方法。

SQL IN 一定走索引嗎?

in 一定走索引嗎?那當然了,不走索引還能全部掃瞄嗎?好像之前有看到過什麼exist,in走不走索引的討論。但是好像看的太久了,又忘記了。哈哈,如果你也忘記了mysql中in是如何查詢的,就來複習下吧。問題要從之前的統計店鋪數關注人數說起 select shop id,count user id a...

MqSql一定一定會索引失效的幾種情況

使用explain查詢 檢視key是否命中索引 1.表層面,兩個表關聯時,如果兩個表關聯欄位的的編碼不同,不會走索引。如乙個表時 utf8,另外乙個表是utf8mb4 2.like查詢是以 開頭,索引不會命中,放後面會命中 3.如果查詢時,查詢的字段型別沒有寫對,底層會隱式處理加上函式,不會命中 4...

加了索引,mysql查詢就一定會用嗎?

一 概述 日常處理mysql問題中,往往通過增加索引來提高查詢速度,但在有些情況下,執行過程中並沒有按照我們的預期結果執行,也就是說,即使欄位加了索引,但現實也沒有使用到,到底是什麼地方出了差錯,以下我們將一 竟。二 實驗表結構宣告 我們將對以下表結構進行實際案例分析 createtable stu...