MySQL系列(11)— 索引

索引

參考:web

MySQL 索引

索引 就是爲了提升數據查詢的效率,就像書的目錄同樣,咱們能夠藉助目錄快速找到某個知識點所在的頁。一樣,對於數據庫的表而言,索引其實就是表數據的「目錄」。算法

索引 是在 MySQL 存儲引擎層中實現的,因此每一種存儲引擎支持的索引不必定相同,即便多個存儲引擎支持同一種類型的索引,其底層的實現也可能不一樣。下面這張表格展現了不一樣的存儲引擎所支持的索引類型。數據庫

索引類型 InnoDB 引擎 MyISAM 引擎 Memory 引擎
B+Tree 索引 Y Y Y
Hash 索引 N N Y
R-Tree 索引 N Y N
Full-Text 索引 N Y N

B+Tree索引Hash索引是比較經常使用的兩個索引數據存儲結構:性能優化

  • B+Tree索引是經過B+樹實現的,是有序排列存儲,因此在排序和範圍查找方面都比較有優點。服務器

  • Hash索引適合 key-value 鍵值對查詢,不管表數據多大,查詢數據的複雜度都是O(1),且直接經過 Hash 索引查詢的性能比其它索引都要高。但缺點是,由於不是有序的,因此哈希索引作區間查詢的速度很慢。因此,哈希表結構適用於只有等值查詢的場景。markdown

B+Tree 索引

數據頁

B+Tree 索引是經過 B+ 樹實現的,能夠經過 平衡二叉樹、B樹、B+樹、B*樹 這篇文章瞭解 B+ 樹的數據結構原理。數據結構

InnoDB 磁盤管理的最小單位是,B+Tree 索引中的每一個節點就是一個數據頁。在研究 B+Tree 索引前,先回顧下前面學過的一些關於頁的知識。oop

頁結構的 File Header 部分記錄的信息以下表所示:post

這裏記住以下幾個比較重要的信息:性能

  • FIL_PAGE_OFFSET:當前頁的頁號,每一個頁都有一個惟一編號
  • FIL_PAGE_PREV:雙向鏈表中指向當前頁的上一個頁
  • FIL_PAGE_NEXT:雙向鏈表中指向當前頁的下一個頁
  • FIL_PAGE_TYPE:頁的類型,索引和數據都是存放在 FIL_PAGE_INDEX(0x45BF)這種類型的頁中,就是數據頁。

數據頁中存放的就是一行行記錄,如常使用的 Compact 行記錄格式以下圖所示:

其中記錄頭部分記錄的信息以下表所示:

這裏記住兩個比較重要的信息:

  • record_type:記錄的類型:
    • 0:普通的用戶記錄
    • 1:目錄項記錄
    • 2:最小記錄
    • 3:最大記錄
  • next_record:指向頁中下一條記錄

有了上面這些信息,就能夠造成一個簡單的雙向鏈表來存儲數據,頁與頁之間就經過 FIL_PAGE_PREVFIL_PAGE_NEXT 連成雙向鏈表。頁中存放的就是一行行記錄,每行記錄經過 next_record 鏈接起來造成一個單項鍊表,每一個頁中都會有一個最小記錄(Infimum,record_type=2),以及最大記錄(Supremum,record_type=3),而後就是普通的用戶記錄(record_type=0)。

image.png

B+Tree 索引的造成過程

仍是之前面測試使用的 account 表爲例,咱們先覺得 id 列建立主鍵索引爲例來講明。

CREATE TABLE `account` (
  `id` bigint(11) NOT NULL AUTO_INCREMENT COMMENT '主鍵',
  `card` varchar(60) NOT NULL COMMENT '卡號',
  `name` varchar(60) DEFAULT NULL COMMENT '姓名',
  `balance` int(11) NOT NULL DEFAULT '0' COMMENT '餘額',
  PRIMARY KEY (`id`),
  UNIQUE KEY `account_u1` (`card`) USING BTREE,
  KEY `account_n1` (`name`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='帳戶表';
複製代碼

每當爲某個表建立一個 B+Tree 索引的時候,都會爲這個索引建立一個根節點頁面,這個根節點頁面建立後便不會再移動。這個根節點的頁號會被記錄起來,而後在訪問這個表須要用這個索引的時候,就會取出這個根節點頁面,從而來訪問這個索引。

例如建立主鍵索引時,建立了一個頁號爲30的根節點頁面,插入數據時,首先會插入到根節點頁面中。

須要注意的是,一行記錄不僅包含用戶記錄,還包含隱藏的事務ID(trx_id)、回滾指針(roll_pointer)等,這些就沒有展現在圖中了。

image.png

假設一個頁最多存儲 3 條記錄就滿了。

這時再插入一條 id=4 的記錄,根節點頁面已滿,就會新分配一個頁(頁35),而後將根節點中的全部記錄複製到這個新分配的頁中。因爲頁 35 已滿,因此會再分配一個新的頁(頁38),而後將新的記錄插入頁 38 中。

image.png

但這裏是有點問題的,索引上的記錄是順序排列的,並且要求 下一個數據頁中用戶記錄的主鍵值必須大於上一個頁中用戶記錄的主鍵值。頁 35 中最大的記錄是 id=5,若是插入一條 id > 5 的記錄,插入到頁 38 中就沒有問題,而這裏插入的是 id=4 這條記錄。因此插入 id=4 這條記錄時還伴隨着 頁分裂,就是把 id=5 這條記錄移動到頁 38 中,而後再把 id=4 這條記錄插入到頁 35 中。

image.png

這個過程代表了在對頁中的記錄進行增刪改操做的過程當中,會經過一些諸如記錄移動的操做來保證下一個數據頁中記錄的主鍵值始終大於上一個頁中記錄的主鍵值,這個過程也能夠稱爲頁分裂

存儲用戶記錄的頁在物理存儲上可能並不挨着(把這種頁稱爲用戶記錄頁),因此若是想從這麼多頁中根據主鍵值快速定位某些記錄所在的頁,就須要給它們作個目錄。

這時候根節點頁就會變成目錄頁,裏面的記錄的類型爲record_type=1,也就是目錄項記錄。目錄項記錄跟普通記錄的結構相似,只不過它的數據部分只存儲了主鍵值頁號,每一個用戶記錄頁都會對應一個目錄項記錄,這個目錄項記錄的主鍵值就是這個用戶記錄頁中主鍵值最小的記錄。

這時,跟節點頁中就會有兩條目錄項記錄,第一條記錄的頁號爲 35,id=1;第二條記錄的頁號爲 38,id=5。

image.png

隨着用戶記錄不斷插入,用戶記錄頁愈來愈多,目錄頁中的記錄也滿了,這時要再插入一個目錄項記錄就放不下了。例以下面最後一條記錄。

image.png

其實跟前面是相似的,也會伴隨着頁分裂的操做。根節點頁始終不動,它會把全部記錄複製到一個新分配的頁中。這時能夠看到目錄頁就有兩層了。

image.png

上面這幅圖如今看起來就像一個倒過來的樹,這其實就是 B+樹,B+ 樹就是一種用來組織數據的數據結構。

不管是存放用戶記錄的數據頁,仍是存放目錄項記錄的數據頁,都把它們存放到 B+ 樹這個數據結構中了。從圖中能夠看出來,用戶記錄頁都存放在B+樹的最底層的節點上,這些節點也被稱爲葉子節點葉節點,其他用來存放目錄項的節點稱爲非葉子節點或者內節點,其中B+樹最上邊的那個節點就稱爲根節點

在索引中查找記錄

上面已經造成一個 B+ 數索引了,假設如今要查找 id=11 這條記錄,這時就會按以下步驟來查找:

  • 首先IO讀取索引的根節點頁(頁30)到內存中,而後在內存中遍歷根節點頁中的記錄項,這些記錄能夠根據主鍵劃分幾個區間:(Infimum, 1),[1, 15),[15,Supremum)。id=11 落在 [1, 15) 這個區間,因此定位到 id=1 這條記錄,對應的頁號是 52。

  • 接着IO讀取頁 52 到內存中,一樣的遍歷頁中的記錄,這時 id=11 落在 [10, Supremum) 這個區間,所以定位到 id=10 這條記錄,對應的頁號是 45。

  • 接着IO讀取頁 45 到內存中,再遍歷頁中的記錄,就能夠定位到 id=11 這條用戶記錄了。

須要注意的是,無論是目錄頁仍是用戶記錄頁,頁中都會有一個 Page Directory,就是頁目錄,經過頁目錄就能夠經過二分法快速定位到頁中的一條記錄,而不是從左往右一條條遍歷。關於 Page Directory 請參考:MySQL系列(4)— InnoDB數據頁結構

image.png

注意B+樹索引並不能找到一個給定鍵值的具體行,能找到的只是被查找數據行所在的。而後數據庫把讀入到內存,再在內存中進行查找,最後獲得要查找的數據。因此上面的步驟中會有IO操做。

從上面查找記錄的過程能夠看出,磁盤IO的次數等於 B+ 樹的高度,也就是說IO的次數將取決於 B+ 樹的高度,而磁盤 IO 每每是數據庫性能的瓶頸。B+Tree 索引最高會有多少層呢?

前面咱們只是假設每一個頁最多存放3條記錄,其實一個 16KB 的頁存放的記錄數量是很是大的。假設存放用戶記錄的葉子節點數據頁能夠存放100條用戶記錄,而存放目錄項記錄的內節點數據頁能夠存放1000條目錄項記錄:

  • 若是 B+Tree 有1層,也就是隻有1個用於存放用戶記錄的節點,最多能存放 100 條記錄。

  • 若是 B+Tree 有2層,最多能存放 1000×100=10萬 條記錄。

  • 若是 B+Tree 有3層,最多能存放 1000×1000×100=1億 條記錄。

  • 若是 B+Tree 有4層,最多能存放 1000×1000×1000×100=1000億 條記錄。

一張表通常來講不多會超過1億條記錄,更不用說 1000億 了。因此通常狀況下,B+Tree 都不會超過4層

咱們經過主鍵值去查找某條記錄最多隻須要作4個頁面內的查找(查找3個目錄項頁和一個用戶記錄頁),又由於在每一個頁面內有 Page Directory(頁目錄),因此在頁面內又能夠經過二分法實現快速定位記錄。

因此有了索引以後,根據索引查找數據是很是快的。而沒有索引,就只能全表掃描,讀取每一個頁到內存中遍歷,就會有不少次的磁盤IO,這個性能就很是低下了。

聚簇索引

上面介紹建立的ID主鍵索引其實就是一種聚簇索引,最主要的特徵即是 B+樹的葉子節點存儲的是完整的用戶記錄,也就是存儲了一行記錄中全部列的值(包括隱藏列)。

除此以外,聚簇索引使用記錄主鍵值的大小進行記錄和頁的排序,主要表如今如下幾個方面:

  • 頁內的記錄是按照主鍵的大小順序排成一個單向鏈表。
  • 各個存放用戶記錄的頁也是根據頁中用戶記錄的主鍵大小順序排成一個雙向鏈表。
  • 存放目錄項記錄的頁分爲不一樣的層次,在同一層次中的頁也是根據頁中目錄項記錄的主鍵大小順序排成一個雙向鏈表。

InnoDB 存儲引擎表都會有主鍵,若是咱們沒有爲某個表顯式的定義主鍵,而且表中也沒有定義惟一索引,那麼InnoDB會自動爲表添加一個 row_id 的隱藏列做爲主鍵。所以,InnoDB 始終會自動建立聚簇索引,在 InnoDB 中,聚簇索引就是數據的存儲方式,全部的數據都是存儲在這顆 B+樹的葉子節點上,這也就是 索引即數據,數據即索引

輔助索引

InnoDB 在建立表時,默認會建立一個主鍵的聚簇索引,而除此以外的其它索引都屬於輔助索引,也被稱爲二級索引非聚簇索引

聚簇索引只能在搜索條件是主鍵值時才能發揮做用,由於目錄頁中存儲的都是主鍵,B+樹中的數據都是按照主鍵進行排序的。若是咱們要根據其它的非主鍵列來查詢,好比前面 account 表中的 name 列,這時就能夠再建一個 name 列的輔助索引。

輔助索引與聚簇索引最大的區別在於葉子節點存儲的就再也不是完整的用戶記錄了,而是索引列+主鍵值,目錄頁中存儲的也是索引列的值,同時,輔助索引使用索引列的大小進行記錄和頁的排序。

例以下面就是爲 name 列建立的一個輔助索引。能夠看到最底層的葉子節點就只包含 name + id 列的數據,同時數據是按照 name 列的大小排序的,目錄頁中存儲的也是 name 列的值。

image.png

這時,再根據 name 列查找數據時,就會用上這個輔助索引了,查找過程跟聚簇索引的查找過程是相似的。最主要的區別在於利用輔助索引查找到的數據不是完整的用戶記錄,因此找到葉子節點上的記錄後,還會根據對應的主鍵值回到主鍵索引上再根據主鍵值找到對應的完整記錄,這個過程也稱爲回表

例如查找 name=H 的記錄,就會定位到頁69,name=H 對應的主鍵 id=11,而後就會回表在聚簇索引上查找 id=11 這條完整記錄。

利用輔助索引查找的時候,也並不是必定須要回表,若是咱們查找的數據在輔助索引上都已經存在了,就不會回表了。例如SQL select name, id where name='H' 只查詢 name、id 的值,就不會回表了,由於這個輔助索引上已經包含了要查找的全部列,只有索引上不包含要查找的列時,纔會回表再查一遍。

聯合索引

咱們也能夠同時以多個列的大小做爲排序規則,同時爲多個列創建索引,多個列創建的索引稱爲聯合索引,其本質上也是一個輔助索引或二級索引。

多個列創建的聯合索引,葉子節點中存儲的就是這幾個索引列+主鍵值,例如爲 name、balance 列創建索引,那葉子節點上存儲的就是 name、balance、id 這幾列,目錄頁存儲的就是 name、balance + 頁號。

聯合索引會先根據第一列排序,第一列相同的再根據第二列排序,以此類推。例如 name、balance 的聯合索引,會先以 name 列排序存儲,name 列值相同的再按 balance 列排序。

MyISAM 中的索引

InnoDB 引擎表中聚簇索引既包含了索引目錄又包含了完整數據,索引和數據是一塊兒存在一顆B+樹上的。

MyISAM 引擎表則是將索引和數據分開存儲:

  • 用戶數據按照記錄的插入順序單獨存儲在一個文件中,稱之爲數據文件,也就是 .MYD 爲後綴的文件。這個文件並不劃分數據頁,全部記錄都按照插入順序插入就好了,而後經過每行數據的物理地址來快速訪問到一條記錄。

  • 索引信息則另外存儲到一個單獨的索引文件中,就是 .MYI 爲後綴的文件。MyISAM 會單獨爲表的主鍵建立一個索引,只不過在索引的葉子節點中存儲的不是完整的用戶記錄,而是主鍵值 + 物理地址的組合。也就是先經過索引找到行對應的物理地址,再經過物理地址去找對應的記錄。

也就是說,MyISAM 引擎中創建的索引至關於所有都是二級索引,不管是爲主鍵仍是其它列建立的索引,都須要根據物理地址 回表,到數據文件中查找完整的用戶記錄。

image.png

索引總結

索引規則

根據前面的學習,咱們先總結熟悉下 InnoDB 引擎的 B+ 樹索引規則。

  • 每一個索引都對應一棵 B+樹,B+ 樹通常最多不超過4層,最底層的是葉子節點,其他的是內節點。全部用戶記錄都存儲在B+樹的葉子節點,全部目錄項記錄都存儲在內節點。

  • InnoDB 存儲引擎會自動爲主鍵創建聚簇索引,聚簇索引的葉子節點包含完整的用戶記錄。

  • 能夠根據實際需求建立 二級索引,二級索引的葉子節點僅包含索引列 + 主鍵,因此若是想經過二級索引來查找完整的用戶記錄,會有 回表 操做,也就是在經過二級索引找到主鍵值以後再到聚簇索引中查找完整的用戶記錄。

  • B+樹索引中,每層數據頁節點都是按照索引列值從小到大的順序排序而組成了雙向鏈表,每一個頁內的記錄(不管是用戶記錄仍是目錄項記錄)都是按照索引列值從小到大的順序而造成了一個單向鏈表。

  • 聯合索引 的頁面和記錄先按照聯合索引前邊的列排序,若是該列值相同,再按照聯合索引後邊的列排序。

  • 經過索引查找記錄是從B+樹的根節點開始,一層一層向下搜索。因爲每一個頁面都按照索引列的值創建了 Page Directory,因此在這些頁面中的查找也很是快。

記住索引列的順序性是很是重要的,索引自己的特徵以及不少查詢的性能優化和限制都和索引的順序有關係。

索引優勢

索引主要就是爲了提高數據庫的查詢性能,總結下來主要有以下幾個優勢:

  • 索引大大減小了服務器須要掃描的數據量,經過索引能夠快速定位到一條記錄。並且由於索引列存儲了實際的值,因此有些查詢只使用索引就可以完成所有查詢,而無需回表。

  • 索引能夠幫助服務器避免排序和臨時表,由於索引是按照索引列排序的,數據已經排好序了,因此對於範圍查詢、排序 ORDER BY、分組 GROUP BY 是很是有用的。

  • 索引能夠將隨機 I/O 變爲順序 I/O,由於數據就是按索引列排序的。

索引代價

首先要明確,索引並非越多越好,索引的使用是有必定代價的。

  • 空間上的代價

每建立一個索引都要爲它創建一棵 B+樹,每一棵 B+樹 的每個節點都是一個數據頁,一個頁默認會佔用16KB的存儲空間。一張表數據越多,這顆 B+樹就會越大,佔用的空間就會越多。

  • 時間上的代價

每次對錶中的數據進行增、刪、改操做時,都須要去修改各個B+樹索引。B+樹 每層節點都是按照索引列的值從小到大排序連成的雙向鏈表,節點中的記錄也是按照索引列的值從小到大排序而造成的一個單向鏈表。而增、刪、改操做可能會對節點和記錄的排序形成破壞,因此存儲引擎須要額外的時間進行一些記錄移位,頁面分裂、頁面回收等操做來維護好節點和記錄的排序。因此索引建的越多,每次增、刪、改操做索引所耗費的時間就會越多。

因此,一個表上索引建的越多,就會佔用越多的存儲空間,在增刪改記錄的時候性能就越差。只有正確使用和建立索引,才能總體提高性能,不然只會拔苗助長。

只有當索引對查找到記錄帶來的好處大於其帶來的額外工做時,索引纔是有效的。對於很是小的表,大部分狀況下簡單的全表掃描更高效。對於中到大型的表,索引就很是有效。

索引應用

全值匹配

全值匹配就是查詢條件中的列和索引中的列一致。

例如爲 (card, name, balance) 建立的一個聯合索引 idx_cnb,假設一個查詢語句把這三列都用上了:

SELECT * FROM account WHERE card = 'A' AND name = 'A' and balance = 0;
複製代碼

首先要知道 idx_cnb 這個索引是一個聯合索引,這個索引首先按照 card 列排序,card 列相同的再按照 name 列排序,name 列相同的再按照 balance 列排序。

因此這個查詢語句會先查找 card = 'A' 的記錄,再從這些記錄中快速找出 name='A' 的記錄,若是 card 和 name 都相同,還會用上 balance 列。

WHERE 子句中的幾個搜索條件的順序對查詢結果是沒有什麼影響的,MySQL查詢優化器會自動優化SQL語句,而後根據要使用的索引,來決定先使用哪一個查詢條件,後使用哪一個查詢條件。

匹配最左前綴

對於聯合索引,能夠只使用左邊的部分列,能夠不用包含所有聯合索引中的列,但只能是左邊連續的列。

例以下面的查詢語句就會使用 idx_cnb 這個聯合索引,但只使用了索引中的前兩個列。

SELECT * FROM account WHERE card = 'A' AND name = 'A';
複製代碼

若是隻使用了中間的列,則用不上這個聯合索引。例以下面的SQL根據 name 查詢,由於 idx_cnb 索引是先安裝 card 列排序的,在 card 列相同的狀況下才會使用 name 列排序。因此沒法跳過 card 列直接根據 name 列查找數據。

SELECT * FROM account WHERE name = 'A';
複製代碼

再好比下面的SQL,則只會使用到 idx_cnb 索引的第一列,由於 balance 是先根據 name 列排序後再排序的,因此對於 card=A 的數據,balance 可能並非有序的。因此要將全部 card=A 的數據查詢到內存後再篩選出 balance=0 的數據 。

SELECT * FROM account WHERE card = 'A' AND balance = 0;
複製代碼

匹配列前綴

匹配列前綴就是隻匹配某一列的值的開頭部分。

例以下面的查詢,只匹配 card 爲 A 開頭的記錄,也能夠用 idx_cnb 索引來快速定位記錄。

SELECT * FROM account WHERE card LIKE 'A%';
複製代碼

但若是隻給出後綴或者中間的某個字符串,則沒法使用索引。例以下面的查詢,查找 card 爲 A 結尾的記錄,由於並不知道 A 結尾以前的順序,因此就沒辦法使用索引。

SELECT * FROM account WHERE card LIKE '%A';
複製代碼

匹配範圍值

匹配範圍值就是利用索引的有序性,能夠很是方便的查找在某個範圍內的記錄。

例以下面的SQL語句,根據 card 列進行範圍查找,就可使用上 idx_cnb 索引。

SELECT * FROM account WHERE card > 'A' AND card < 'H';
複製代碼

但須要注意的是,若是對聯合索引多個列同時進行範圍查找的話,只有對索引最左邊的那個列進行範圍查找的時候才能用上索引。由於第一列使用範圍查詢後,第二列並非有序的,要知道是在第一列值相同的狀況下,才用第二列排序。

例以下面的查詢,先查詢了 card 在 (A,B) 之間的記錄,此時可能會有多條 card 不一樣的記錄,因此這些記錄中的 name 並非有序的。因此須要先找到 card 在 (A, B) 之間的記錄,再一條條過濾出 name > A 的記錄。因此這個查詢只用到了 idx_cnb 索引的 card 列,沒用到 name 列。

SELECT * FROM account WHERE card > 'A' AND card < 'H' AND name > 'A';
複製代碼

精確匹配某一列並範圍匹配另一列

上一小節說的是若是第一列是範圍查詢,第二列也是範圍查詢時,第二列不會走索引。

但若是左邊的列是精確匹配的,後面的列是範圍查詢則能夠用上索引,由於左邊的列精確匹配後,後邊的列就是排好序的。

例以下面的查詢,card 列是精確匹配,以後對 name 列進行範圍查找,這個查詢會用上 idx_cnb 索引的 card、name 兩列。

SELECT * FROM account WHERE card = 'A' AND name > 'A';
複製代碼

排序和分組

咱們常常會使用 ORDER BY 子句來對記錄排序,通常狀況下,數據庫只能把記錄都加載到內存中,再用一些排序算法在內存中對這些記錄進行排序。有的時候可能查詢的結果集太大以致於不能在內存中進行排序的話,還可能要使用磁盤空間來存放中間結果,排序操做完成後再把排好序的結果集返回到客戶端。在MySQL中,把這種在內存中或者磁盤上進行排序的方式統稱爲文件排序(filesort),文件排序的性能通常就比較低了。

可是若是 ORDER BY 子句裏使用到了索引列,就有可能省去在內存或文件中排序的步驟。

例以下面的查詢就會使用到 idx_cnb 索引,由於 card,name 已經排好序了,這個查詢就能夠直接從 idx_cnb 索引中提取數據,而後回表查詢。(固然了,idx_cnb 索引已經包含了整張表的數據,因此不會有回表這一步了)

SELECT * FROM account ORDER BY card, name;
複製代碼

一樣的,ORDER BY 也能夠只使用部分的B+樹索引列,當聯合索引左邊列的值爲精確匹配時,也可使用後邊的列進行排序。例以下面的查詢:

SELECT * FROM account ORDER BY card, balance;

SELECT * FROM account WHERE card='A' ORDER BY name;
複製代碼

須要注意的是,ORDER BY 子句後邊的列的順序必須按照索引列的順序來,不然也是用不了索引的。例以下面的查詢:

SELECT * FROM account ORDER BY name, card;
複製代碼

使用聯合索引進行排序時,要求各個排序列的排序順序是一致的,要麼各個列都是ASC升序,要麼都是DESC降序。

由於若是一個按 ASC 升序,一個按 DESC 降序,這與索引中的順序始終都是反的,並且若是加上 LIMIT 之類的限制條件,只能排好序以後才能肯定具體的記錄。因此 MySQL 認爲這種狀況還不如文件排序來的快,就不會使用索引。

例以下面的查詢語句:

SELECT * FROM account ORDER BY name ASC, card DESC;
複製代碼

若是用來排序的多個列不是一個索引裏的,這種狀況也不能使用索引進行排序,緣由跟上面的是相似的。假設 acount 表還有其它列,例以下面的查詢,country 列不屬於 idx_cnb ,因此這個查詢排序也用不上 idx_cnb 這個索引。

SELECT * FROM account ORDER BY name, country;
複製代碼

分組和排序在使用索引的方式上是相似的,就不在贅述了。

相關文章
相關標籤/搜索