1. 程式人生 > >圖解:實戰最左字首原則

圖解:實戰最左字首原則

介紹

前文已經說了explain命令的大部分引數,圖解:實戰EXPLAIN這篇文章把explain的key_len引數分享完,接著分享最左字首原則,建立如下的表,其中name列和address列都建立了索引

CREATE TABLE `teacher` (
  `id` int(10) NOT NULL,
  `name` char(20) NOT NULL,
  `address` varchar(100) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `idx_name` (`name`),
  KEY `idx_addr` (`address`
) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

執行如下命令

explain select * from teacher where name = "張三"

在這裡插入圖片描述

explain select * from teacher where address = "北京"

在這裡插入圖片描述

問題來了,這些key_len是怎麼算出來的呢?

key_len

key_len表示索引使用的位元組數,根據這個值,就可以判斷索引使用情況,特別是在組合索引的時候,判斷所有的索引欄位是否都被查詢用到

字串型別
在這裡插入圖片描述

char和varchar跟字元編碼也有密切的聯絡
latin1佔用一個位元組,gbk佔用2個位元組,utf8佔用3個位元組,utf8mb4佔用4個位元組(不同字元編碼佔用的儲存空間不同)

字元型別-索引欄位為char型別+不可為Null時

char(n)=n*(utf8mb4=4,utf8=3,gbk=2,latin1=1)
所以上面第一個列子(查詢name=張三)的key_len為20*3=60
下文中為了描述方便,編碼型別預設為utf8

字元型別-索引欄位為char型別+允許為Null時

char(n)=n*3+1(允許null,是否為空的標記)

字元型別-索引欄位為varchar型別+不可為Null時

varchar(n)=n*3+2(變長列,記錄當前資料存了多少)

字元型別-索引欄位為varchar型別+允許為Null時

varchar(n)=n*3+1(允許null)+2(變長列)
所以上面第二個例子(查詢住址=北京)的key_len為100*3+1+2=303

在這裡插入圖片描述

在這裡插入圖片描述

datetime型別在5.6中欄位長度是5個位元組,datetime型別在5.5中欄位長度是8個位元組

整數/浮點數/時間型別的索引長度
Not Null=欄位本身的長度
Null=欄位本身的長度+1

最左字首原則

查詢

主要針對組合索引,滿足如下2個條件即可滿足左字首原則

  1. 需要查詢的列和組合索引的列順序一致
  2. 查詢不要跨列

構造資料如下,其中在name,address,country上建了聯合索引

CREATE TABLE `people` (
  `name` varchar(50) NOT NULL,
  `address` varchar(50) NOT NULL,
  `country` varchar(50) NOT NULL,
  KEY `idx_name_addr_country` (`name`,`address`,`country`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

先簡單舉個例子,然後總結一下

explain select * from people where name = "jack"
and address = "beijing" and country = "china"

在這裡插入圖片描述

type為ref,key_len為456=(50*3+2)*3,聯合索引的所有列都使用了

explain select * from people where name = "jack"

在這裡插入圖片描述
type為ref,key_len為152=50*3+2,聯合索引只使用了name列

explain select * from people where address = "beijing"

在這裡插入圖片描述
type為index,並沒有走索引,簡單說一下index和ref的區別

index:這種型別表示mysql會對整個該索引進行掃描。要想用到這種型別的索引,對這個索引並無特別要求,只要是索引,或者某個聯合索引的一部分,mysql都可能會採用index型別的方式掃描。但是呢,缺點是效率不高,mysql會從索引中的第一個資料一個個的查詢到最後一個數據,直到找到符合判斷條件的某個索引。所以,上述語句會觸發索引

ref:這種型別表示mysql會根據特定的演算法快速查詢到某個符合條件的索引,而不是會對索引中每一個數據都進行一一的掃描判斷,也就是所謂你平常理解的使用索引查詢會更快的取出資料。而要想實現這種查詢,索引卻是有要求的,要實現這種能快速查詢的演算法,索引就要特定的資料結構。簡單說,也就是索引欄位的資料必須是有序的,才能實現這種型別的查詢,才能利用到索引。

總結幾個典型的例子,聯合索引為
key idx_a_b_c(a,b,c)

sql 是否使用索引
where a = x and b = x and c = x
where a = x and b = x 是,部分索引
where a = x 是,部分索引
where b = x 否,不包含最左列name
where b = x and c = x 否,不包含最左列name

排序

最左字首原則不僅用在查詢中,還能用在排序中。MySQL中,有兩種方式生成有序結果集:

  1. 通過有序索引順序掃描直接返回有序資料
  2. Filesort排序,對返回的資料進行排序

因為索引的結構是B+樹,索引中的資料是按照一定順序進行排列的,所以在排序查詢中如果能利用索引,就能避免額外的排序操作。EXPLAIN分析查詢時,Extra顯示為Using index。

所有不是通過索引直接返回排序結果的操作都是Filesort排序,也就是說進行了額外的排序操作。EXPLAIN分析查詢時,Extra顯示為Using filesort,當出現Using filesort時對效能損耗較大,所以要儘量避免Using filesort

還是先舉2個例子,然後總結

explain select * from people order by name

在這裡插入圖片描述

Extra列只有Using index,即根據索引順序進行掃描

explain select * from people order by address

在這裡插入圖片描述

Extra列有Using filesort

總結:假如說有如下聯合索引,key idx_a_b_c(a,b,c)

order by 能使用索引排序

order by a
order by a,b
order by a,b,c
order by a desc, b desc, c desc
where a = const order by b,c
where a = const and b = const order by c
where a = const and b > const order by b,c

order by 不能使用索引進行排序

order by b
order by c
order by b, c
order by a asc, b desc, c desc //排序不一致
where g = const order by b,c //丟失a索引
where a = const order by c //丟失b索引
where a = const order by a,d //d不是索引的一部分
where a in (...) order by b,c //範圍查詢

有時間會單開一篇文章介紹order by優化,這裡只做個粗淺的介紹

聯合索引特性

增加開銷。建一個聯合索引(col1,col2,col3),實際相當於建了(col1),(col1,col2),(col1,col2,col3)三個索引。每多一個索引,都會增加寫操作的開銷和磁碟空間的開銷。對於大量資料的表,使用聯合索引會大大增加開銷!

覆蓋索引。對聯合索引(col1,col2,col3),如果有如下的sql: select col1,col2,col3 from test where col1=1 and col2=2。那麼MySQL可以直接通過遍歷索引取得資料,而無需回表,這了很多的隨機io操作。io操作,特別的隨機io其實是dba主要的優化策略。所以,在真正的實際應用中,覆蓋索引是主要的提升效能的優化手段之一。

效率高。索引列越多,通過索引篩選出的資料越少。有1000W條資料的表,有如下sql:select from table where col1=1 and col2=2 and col3=3,假設假設每個條件可以篩選出10%的資料,如果只有單值索引,那麼通過該索引能篩選出1000W10%=100w條資料,然後再回表從100w條資料中找到符合col2=2 and col3= 3的資料,然後再排序,再分頁;如果是聯合索引,通過索引篩選出1000w*10%*10% *10%=1w,效率提升可想而知!

參考部落格

[1]https://mp.weixin.qq.com/s/h4B84UmzAUJ81iBY_FXNOg
[2]https://mp.weixin.qq.com/s/RB6wxubRluybf0BxiENJNg
[3]https://blog.csdn.net/xifeijian/article/details/20557921
[4]https://blog.csdn.net/xlgen157387/article/details/44156679
[5]https://mp.weixin.qq.com/s/Qmlr-GFwbdjYgvCmVKYYtA
[6]https://blog.csdn.net/zly9923218/article/details/51007554
[7]https://www.wzxaini9.cn/article/100/4.html
[8]http://blog.720ui.com/2017/mysql_core_04_index_item/#複合索引的最左字首原則
[9]http://www.chongchonggou.com/g_21444022.html
[10]https://it.520mwx.com/view/7109
[11]https://www.cnblogs.com/weizhixiang/p/5914120.html
[12]https://blog.csdn.net/zzx125/article/details/79678770
[13]https://www.cnblogs.com/softidea/p/5977860.html
[14]http://www.lovean.com/mip/view-34-315489-0.html