1. 程式人生 > >索引使用策略及優化

索引使用策略及優化

MySQL的優化主要分為結構優化(Scheme optimization)和查詢優化(Query optimization)。本文討論的高效能索引策略主要屬於結構優化範疇。本章的內容完全基於上文B+樹在資料庫索引中的應用的理論基礎,一旦理解了索引背後的機制,那麼選擇高效能的策略就變成了純粹的推理,並且可以理解這些策略背後的邏輯。

示例資料庫
為了討論索引策略,需要一個數據量不算小的資料庫作為示例。本文選用MySQL官方文件中提供的示例資料庫之一:employees。這個資料庫關係複雜度適中,且資料量較大。下圖是這個資料庫的E-R關係圖(引用自MySQL官方手冊):
這裡寫圖片描述

最左字首原理與相關優化

高效使用索引的首要條件是知道什麼樣的查詢會使用到索引,這個問題和B+Tree中的“最左字首原理”有關,下面通過例子說明最左字首原理。

這裡先說一下聯合索引的概念。在上文中,我們都是假設索引只引用了單個的列,實際上,MySQL中的索引可以以一定順序引用多個列,這種索引叫做聯合索引,一般的,一個聯合索引是一個有序元組<a1, a2, …, an>,其中各個元素均為資料表的一列。

對於employees.titles查詢該表的索引結構:

這裡寫圖片描述

  • Table 表的名稱。
  • Non_unique 如果索引不能包括重複詞,則為0。如果可以,則為1。
  • Key_name 索引的名稱。(PRIMARY為主鍵索引,否則是普通索引)
  • Seq_in_index 索引中的列序列號,從1開始。
  • Column_name 列名稱。
  • Collation 列以什麼方式儲存在索引中。在MySQL中,有值‘A’(升序)或NULL(無分類)。
  • Cardinality 索引中唯一值的數目的估計值。通過執行ANALYZE TABLE或myisamchk -a可以更新。基數根據被儲存為整數的統計資料來計數,所以即使對於小型表,該值也沒有必要是精確的。基數越大,當進行聯合時,MySQL使用該索引的機會就越大。
  • Sub_part 如果列只是被部分地編入索引,則為被編入索引的字元的數目。如果整列被編入索引,則為NULL。
  • Packed 指示關鍵字如何被壓縮。如果沒有被壓縮,則為NULL。
  • Null 如果列含有NULL,則含有YES。如果沒有,則該列含有NO。
  • Index_type 用過的索引方法(BTREE, FULLTEXT, HASH, RTREE)。
  • Comment,Index_comment 評註。

在對資料進行分析前,先了解 Mysql 的 explain用法和效能分析語句。

EXPLAIN解釋命令是顯示mysql如何使用索引來處理select語句以及連線表。可以幫助選擇更好的索引和寫出更優化的查詢語句。

使用方法很簡單,就是在select語句前加上explain, 如下:

  1. 使用explain語句去檢視分析結果,如
  explain select * from test1 where id=1;

EXPLAIN 命令返回列說明:

select_type:

表示select的型別(取值有:simple,primary,union,subquery)

type:

在mysql中獲取所需行的方式,也可以叫訪問型別

取值: all -> index -> range -> ref -> eq_ref -> const, system -> null (效能由差到好)

all: 全表掃描

index: 索引全掃描

range: 索引範圍掃描 (<, <=, >, >=, between等)

ref: 使用非唯一索引掃描或唯一索引的字首掃描或使用join。來查詢某個單獨值的記錄行(where id = xxx)

eq_ref: 類似於ref,區別在於使用唯一索引,join primary key 或 unique index

const/system: 單表最多有一個匹配行,可以被優化器在當前查詢中當成常量使用,

null:不訪問表的查詢

possible_keys: 查詢中可能用到的索引

key: 實際使用的索引

key_len:使用到索引欄位的長度

rows: 掃描行的數量

Extra: 執行機會的額外說明

情況一:全列匹配。

EXPLAIN SELECT * FROM employees.titles WHERE emp_no='10001' AND title='Senior Engineer' AND from_date='1986-06-26';

這裡寫圖片描述

很明顯,當按照索引中所有列進行精確匹配(這裡精確匹配指“=”或“IN”匹配)時,索引可以被用到。這裡有一點需要注意,理論上索引對順序是敏感的,但是由於MySQL的查詢優化器會自動調整where子句的條件順序以使用適合的索引,例如我們將where中的條件順序顛倒:

EXPLAIN SELECT * FROM employees.titles WHERE title='Senior Engineer' AND emp_no='10001' AND from_date='1986-06-26';

這裡寫圖片描述

效果是一樣的。

情況二:最左字首匹配。

EXPLAIN SELECT * FROM employees.titles WHERE emp_no='10001';

這裡寫圖片描述

當查詢條件精確匹配索引的左邊連續一個或幾個列時,如或

情況三:查詢條件用到了索引中列的精確匹配,但是中間某個條件未提供。

EXPLAIN SELECT * FROM employees.titles WHERE emp_no='10001' AND from_date='1986-06-26';

這裡寫圖片描述

此時索引使用情況和情況二相同,因為title未提供,所以查詢只用到了索引的第一列,而後面的from_date雖然也在索引中,但是由於title不存在而無法和左字首連線,因此需要對結果進行掃描過濾from_date(這裡由於emp_no唯一,所以不存在掃描)。如果想讓from_date也使用索引而不是where過濾,可以增加一個輔助索引<emp_no, from_date>,此時上面的查詢會使用這個索引。除此之外,還可以使用一種稱之為“隔離列”的優化方法,將emp_no與from_date之間的“坑”填上。

看下title一共有幾種不同的值:

SELECT DISTINCT(title) FROM employees.titles;

這裡寫圖片描述

只有7種。在這種成為“坑”的列值比較少的情況下,可以考慮用“IN”來填補這個“坑”從而形成最左字首:

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';

這裡寫圖片描述

這次key_len為59,說明索引被用全了,但是從type和rows看出IN實際上執行了一個range查詢,這裡檢查了7個key。當然,如果title的值很多,用”填坑”就不合適了,必須建立輔助索引。

情況四:查詢條件沒有指定索引第一列。

EXPLAIN SELECT * FROM employees.titles WHERE from_date='1986-06-26';

這裡寫圖片描述

由於不是最左字首,索引這樣的查詢顯然用不到索引。

情況五:匹配某列的字首字串。

EXPLAIN SELECT * FROM employees.titles WHERE emp_no='10001' AND title LIKE 'Senior%';

這裡寫圖片描述

如果萬用字元%不出現在開頭,則可以用到索引,但根據具體情況不同可能只會用其中一個字首。

情況六:範圍查詢。

EXPLAIN SELECT * FROM employees.titles WHERE emp_no < '10010' and title='Senior Engineer';

這裡寫圖片描述

範圍列可以用到索引(必須是最左字首),但是範圍列後面的列無法用到索引。同時,索引最多用於一個範圍列,因此如果查詢條件中有兩個範圍列則無法全用到索引。

EXPLAIN SELECT * FROM employees.titles
WHERE emp_no < '10010'
AND title='Senior Engineer'
AND from_date BETWEEN '1986-01-01' AND '1986-12-31';

這裡寫圖片描述

可以看到索引對第二個範圍索引無能為力。這裡特別要說明MySQL一個有意思的地方,那就是僅用explain可能無法區分範圍索引和多值匹配,因為在type中這兩者都顯示為range。同時,用了“between”並不意味著就是範圍查詢,例如下面的查詢:

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';

這裡寫圖片描述

看起來是用了兩個範圍查詢,但作用於emp_no上的“BETWEEN”實際上相當於“IN”,也就是說emp_no實際是多值精確匹配。可以看到這個查詢用到了索引全部三個列。因此在MySQL中要謹慎地區分多值匹配和範圍匹配,否則會對MySQL的行為產生困惑。

情況七:查詢條件中含有函式或表示式。

很不幸,如果查詢條件中含有函式或表示式,則MySQL不會為這列使用索引(雖然某些在數學意義上可以使用)。例如:

EXPLAIN SELECT * FROM employees.titles WHERE emp_no='10001' AND left(title, 6)='Senior';

這裡寫圖片描述

雖然這個查詢和情況五中功能相同,但是由於使用了函式left,則無法為title列應用索引,而情況五中用LIKE則可以。再如:

EXPLAIN SELECT * FROM employees.titles WHERE emp_no - 1='10000';

這裡寫圖片描述

顯然這個查詢等價於查詢emp_no為10001的函式,但是由於查詢條件是一個表示式,MySQL無法為其使用索引。看來MySQL還沒有智慧到自動優化常量表達式的程度,因此在寫查詢語句時儘量避免表示式出現在查詢中,而是先手工私下代數運算,轉換為無表示式的查詢語句。

索引選擇性與字首索引

既然索引可以加快查詢速度,那麼是不是隻要是查詢語句需要,就建上索引?答案是否定的。因為索引雖然加快了查詢速度,但索引也是有代價的:索引檔案本身要消耗儲存空間,同時索引會加重插入、刪除和修改記錄時的負擔,另外,MySQL在執行時也要消耗資源維護索引,因此索引並不是越多越好。一般兩種情況下不建議建索引。

第一種情況是表記錄比較少,例如一兩千條甚至只有幾百條記錄的表,沒必要建索引,讓查詢做全表掃描就好了。至於多少條記錄才算多,這個個人有個人的看法,我個人的經驗是以2000作為分界線,記錄數不超過 2000可以考慮不建索引,超過2000條可以酌情考慮索引。

另一種不建議建索引的情況是索引的選擇性較低。所謂索引的選擇性(Selectivity),是指不重複的索引值(也叫基數,Cardinality)與表記錄數(#T)的比值:

Index Selectivity = Cardinality / #T

顯然選擇性的取值範圍為(0, 1],選擇性越高的索引價值越大,這是由B+Tree的性質決定的。例如,上文用到的employees.titles表,如果title欄位經常被單獨查詢,是否需要建索引,我們看一下它的選擇性

SELECT count(DISTINCT(title))/count(*) AS Selectivity FROM employees.titles;

這裡寫圖片描述

title的選擇性不足0.0001(精確值為0.00001579),所以實在沒有什麼必要為其單獨建索引

有一種與索引選擇性有關的索引優化策略叫做字首索引,就是用列的字首代替整個列作為索引key,當前綴長度合適時,可以做到既使得字首索引的選擇性接近全列索引,同時因為索引key變短而減少了索引檔案的大小和維護開銷。下面以employees.employees表為例介紹字首索引的選擇和使用。

這裡寫圖片描述

該表僅有主鍵索引 emp_on,那麼如果我們想按名字搜尋一個人,就只能全表掃描了:

EXPLAIN SELECT * FROM employees.employees WHERE first_name='Eric' AND last_name='Anido';

這裡寫圖片描述

如果頻繁按名字搜尋員工,這樣顯然效率很低,因此我們可以考慮建索引。有兩種選擇,建<first_name><first_name, last_name>,看下兩個索引的選擇性:

SELECT count(DISTINCT(first_name))/count(*) AS Selectivity FROM employees.employees;

這裡寫圖片描述

SELECT count(DISTINCT(concat(first_name, last_name)))/count(*) AS Selectivity FROM employees.employees;

這裡寫圖片描述

<first_name>顯然選擇性太低,<first_name, last_name>選擇性很好,但是first_name和last_name加起來長度為30,有沒有兼顧長度和選擇性的辦法?可以考慮用first_name和last_name的前幾個字元建立索引,例如<first_name, left(last_name, 3)>,看看其選擇性:

SELECT count(DISTINCT(concat(first_name, left(last_name, 3))))/count(*) AS Selectivity FROM employees.employees;

這裡寫圖片描述

選擇性還不錯,但離0.9313還是有點距離,那麼把last_name字首加到4:

這裡寫圖片描述

這時選擇性已經很理想了,而這個索引的長度只有18,比<first_name, last_name>短了接近一半,我們把這個字首索引建上:

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

檢驗該索引的效能

SET profiling = 1;
SELECT * FROM employees.employees WHERE first_name='Eric' AND last_name='Anido'
show profiles

這裡寫圖片描述

drop index first_name_last_name4 on employees
SELECT * FROM employees.employees WHERE first_name='Eric' AND last_name='Anido'
show profiles

這裡寫圖片描述

效能的提升是顯著的

字首索引兼顧索引大小和查詢速度,但是其缺點是不能用於ORDER BY和GROUP BY操作,也不能用於Covering index(即當索引本身包含查詢所需全部資料時,不再訪問資料檔案本身)。

相關推薦

MySql 筆記 | 索引使用策略優化

MySQL的優化主要分為結構優化(Scheme optimization)和查詢優化(Query optimization)。本章討論的高效能索引策略主要屬於結構優化範疇。本章的內容完全基於上文的理論基礎,實際上一旦理解了索引背後的機制,那麼選擇高效能的策略就

索引使用策略優化

MySQL的優化主要分為結構優化(Scheme optimization)和查詢優化(Query optimization)。本文討論的高效能索引策略主要屬於結構優化範疇。本章的內容完全基於上文B+樹在資料庫索引中的應用的理論基礎,一旦理解了索引背後的機制,那麼

資料庫索引設計優化

1.表的索引數量不應有上限,只要所有的SQL都能夠流暢的執行。 一個頁從磁碟讀取到資料緩衝池中,一個頁包含多條記錄,我們可能需要該頁上的所有行或一部分行或一行,但花費的成本都相同,約10ms. 在計算領域中, 10ms是一個很長的時間。 10ms怎麼計算出來的:排隊時間 + 尋道時間 +

轉載:資料庫索引原理優化

轉自:https://www.cnblogs.com/wuchanming/p/6886020.html  摘要: 本文內容主要來源於網際網路上主流文章,只是按照個人理解稍作整合,後面附有參考連結。 本文內容主要來源於網際網路上主流文章,只是按照個人理解稍作整合,後面附有

資料庫索引原理優化

本文內容主要來源於網際網路上主流文章,只是按照個人理解稍作整合,後面附有參考連結。 一、摘要 本文以MySQL資料庫為研究物件,討論與資料庫索引相關的一些話題。特別需要說明的是,MySQL支援諸多儲存引擎,而各種儲存引擎對索引的支援也各不相同,因此MySQL

MySQL索引建立優化

聯合索引中 索引建立在離散度大的欄位上面離散度指利用COUNT(DISTINCT ..)獲取欄位的唯一值,唯一值越多的離散度越大索引的優化----重複及冗餘索引重複索引:指相同的列以相同的順序建立的同類型的索引,如下表CREATE TABLE IF NOT EXISTS te

Mysql(二)--索引使用優化

索引簡介 索引是什麼?索引是類似於書的目錄這樣的結構嗎?可以這麼講,但是到底索引是什麼,索引是排好序的快速查詢資料結構。索引最常用的型別是BTree型別,這種型別同樣可以體現出索引是排好序的快速查詢資料結構,沒有索引之前要想查詢符合某個條件的資料就需要全表掃描

索引列上計算引起的索引失效優化措施以及注意事項

兩個示例 例子一 表結構 DROP TABLE IF EXISTS `account`; CREATE TABLE IF NOT EXISTS `account` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `a

Mysql之索引選擇優化

## 索引模型 - 雜湊表 - 適用於只有等值查詢的場景,Memory引擎預設索引 - InnoDB支援自適應雜湊索引,不可干預,由引擎自行決定是否建立 - 有序陣列:在等值查詢和範圍查詢場景中的效能都非常優秀,但插入和刪除資料需要進行資料移動,成本太高。因此,只適用於靜態儲存引擎 - 二叉平衡樹:

MySQL索引原理慢查詢優化(轉)

範圍 很難 等於 right 事件 原理 插入 jpg 個人網站 轉自:美團點評技術團隊http://tech.meituan.com/mysql-index.html MySQL憑借著出色的性能、低廉的成本、豐富的資源,已經成為絕大多數互聯網公司的首選關系型數據庫。雖然性

MySQL索引原理慢查詢優化-zz

`` 原理 並不是 計劃 ora 實的 birt 總計 war https://tech.meituan.com/mysql-index.html MySQL憑借著出色的性能、低廉的成本、豐富的資源,已經成為絕大多數互聯網公司的首選關系型數據庫。雖然性能出色,但所謂“好馬配

【MySQL資料庫】效能優化索引優化(一)

一、Mysql效能優化之影響效能的因素 1.商業需求的影響 不合理的需求造成的資源投入產出,這裡就用一個看上去很簡單的功能分析。需求:一個論壇帖子的總量統計,附加要求:實時更新。從功能上看來是非常容易實現的,執行一條select count(*)from表名就可以得到結果,但是如果我們採

MySQL資料庫索引優化

索引 什麼是索引 索引是用來提高資料庫效能的常用工具,索引就像資料的目錄一樣,通過索引可以快速找到需要的內容。 索引的原理,加快查詢 索引是建立了針對資料內容的排序結果的指標,根據指標快速定位所要的資料。 索引的設計原則 1  索引列一般為where子句中的列或連線列子句中的列。 2&

資料庫索引底層原理優化

一、摘要 本文以MySQL資料庫為研究物件,討論與資料庫索引相關的一些話題。特別需要說明的是,MySQL支援諸多儲存引擎,而各種儲存引擎對索引的支援也各不相同,因此MySQL資料庫支援多種索引型別,如BTree索引,雜湊索引,全文索引等等。為了避免混亂,本文將只關注於BTr

MySQL索引分析?優化

索引類似大學圖書館建書目索引,可以提高資料檢索的效率,降低資料庫的IO成本。MySQL在300萬條記錄左右效能開始逐漸下降,雖然官方文件說500~800w記錄,所以大資料量建立索引是非常有必要的。MySQL提供了Explain,用於顯示SQL執行的詳細資訊,可以進行索引的優化。 一、導致SQL執

海量資料處理專題(七)——資料庫索引優化(轉)

索引是對資料庫表中一列或多列的值進行排序的一種結構,使用索引可快速訪問資料庫表中的特定資訊。資料庫索引什麼是索引資料庫索引好比是一本書前面的目錄,能加快資料庫的查詢速度。例如這樣一個查詢:select * from table1 where id=44。如果沒有索引,必須遍歷整個表,直到ID等於44的這一行被

MySQL索引原理慢查詢優化--美團分享

MySQL索引原理及慢查詢優化 http://tech.meituan.com/mysql-index.html MySQL憑藉著出色的效能、低廉的成本、豐富的資源,已經成為絕大多數網際網路公司的首選關係型資料庫。雖然效能出色,但所謂“好馬配好鞍”,如何能夠更好的使用

MySql索引原理慢查詢優化

MySQL憑藉著出色的效能、低廉的成本、豐富的資源,已經成為絕大多數網際網路公司的首選關係型資料庫。雖然效能出色,但所謂“好馬配好鞍”, 如何能夠更好的使用它,已經成為開發工程師的必修課,我們經常會從職位描述上看到諸如“精通MySQL”、“SQL語句優化”、“瞭解資料庫原

MySQL優化索引優化漢字模糊查詢語句

利用MySQL這種關係型資料庫來做索引,的確有些勉強了,也只能看情況來說了,有些簡單的功能還是可以考慮的。 對於模糊查詢語句,最不利的情況是要like '%key%'這樣的查詢,但是如果是like 'key%'這種情況,那麼mysql的索引在些查詢方式上還是可以優化的。 網

MySQL索引原理慢查詢優化--美團分享轉載

MySQL憑藉著出色的效能、低廉的成本、豐富的資源,已經成為絕大多數網際網路公司的首選關係型資料庫。雖然效能出色,但所謂“好馬配好鞍”,如何能夠更好的使用它,已經成為開發工程師的必修課,我們經常會從職位描述上看到諸如“精通MySQL”、“SQL語句優化”、“瞭解資料庫原理”等