您好,登錄后才能下訂單哦!
這篇文章將為大家詳細(xì)講解有關(guān)MySQL索引優(yōu)化規(guī)則是什么,小編覺得挺實(shí)用的,因此分享給大家做個(gè)參考,希望大家閱讀完這篇文章后可以有所收獲。
今天mysql教程欄目為大家介紹MySQL的索引優(yōu)化規(guī)則。
select * from doc where title like '%XX'; --不能使用索引select * from doc where title like 'XX%'; --非前導(dǎo)模糊查詢,可以使用索引復(fù)制代碼
union
能夠命中索引,并且MySQL 耗費(fèi)的 CPU 最少。select * from doc where status=1union allselect * from doc where status=2;復(fù)制代碼
in
能夠命中索引,查詢優(yōu)化耗費(fèi)的 CPU 比 union all
多,但可以忽略不計(jì),一般情況下建議使用 in
。select * from doc where status in (1, 2);復(fù)制代碼
or
新版的 MySQL 能夠命中索引,查詢優(yōu)化耗費(fèi)的 CPU 比 in
多,不建議頻繁用or
。select * from doc where status = 1 or status = 2復(fù)制代碼
where
條件中使用or
,索引會失效,造成全表掃描,這是個(gè)誤區(qū):①要求where
子句使用的所有字段,都必須建立索引;
②如果數(shù)據(jù)量太少,mysql制定執(zhí)行計(jì)劃時(shí)發(fā)現(xiàn)全表掃描比索引查找更快,所以會不使用索引;
③確保mysql版本5.0
以上,且查詢優(yōu)化器開啟了index_merge_union=on
, 也就是變量optimizer_switch
里存在index_merge_union
且為on
。
負(fù)向條件有:!=
、<>
、not in
、not exists
、not like
等。
例如下面SQL語句:
select * from doc where status != 1 and status != 2;復(fù)制代碼
select * from doc where status in (0,3,4);復(fù)制代碼
如果在(a,b,c)
三個(gè)字段上建立聯(lián)合索引,那么他會自動建立 a
| (a,b)
| (a,b,c)
組索引。
登錄業(yè)務(wù)需求,SQL語句如下:
select uid, login_time from user where login_name=? andpasswd=?復(fù)制代碼
(login_name, passwd)
的聯(lián)合索引。因?yàn)闃I(yè)務(wù)上幾乎沒有passwd
的單條件查詢需求,而有很多login_name
的單條件查詢需求,所以可以建立(login_name, passwd)
的聯(lián)合索引,而不是(passwd, login_name
)。
- 建立聯(lián)合索引的時(shí)候,區(qū)分度最高的字段在最左邊
- 存在非等號和等號混合判斷條件時(shí),在建立索引時(shí),把等號條件的列前置。如
where a>? and b=?
,那么即使a
的區(qū)分度更高,也必須把b
放在索引的最前列。
- 最左前綴查詢時(shí),并不是指SQL語句的where順序要和聯(lián)合索引一致。
(login_name, passwd)
這個(gè)聯(lián)合索引:select uid, login_time from user where passwd=? andlogin_name=?復(fù)制代碼
where
后的順序和聯(lián)合索引一致,養(yǎng)成好習(xí)慣。
- 假如
index(a,b,c)
,where a=3 and b like 'abc%' and c=4
,a
能用,b
能用,c
不能用。
<、<=、>、>=、between
等。(empno、title、fromdate)
,那么下面的 SQL 中 emp_no
可以用到索引,而title
和 from_date
則使用不到索引。select * from employees.titles where emp_no < 10010' and title='Senior Engineer'and from_date between '1986-01-01' and '1986-12-31'復(fù)制代碼
date
上建立了索引,也會全表掃描:select * from doc where YEAR(create_time) <= '2016';復(fù)制代碼
select * from doc where create_time <= '2016-01-01';復(fù)制代碼
select * from order where date < = CURDATE();復(fù)制代碼
select * from order where date < = '2018-01-2412:00:00';復(fù)制代碼
phone
字段是 varchar
類型,則下面的 SQL 不能命中索引。select * from user where phone=13800001234復(fù)制代碼
select * from user where phone='13800001234';復(fù)制代碼
更新會變更 B+ 樹,更新頻繁的字段建立索引會大大降低數(shù)據(jù)庫性能。
“性別”這種區(qū)分度不大的屬性,建立索引是沒有什么意義的,不能有效過濾數(shù)據(jù),性能與全表掃描類似。
一般區(qū)分度在80%以上的時(shí)候就可以建立索引,區(qū)分度可以使用 count(distinct(列名))/count(*)
來計(jì)算。
Select uid, login_time from user where login_name=? and passwd=?復(fù)制代碼
(login_name, passwd, login_time)
的聯(lián)合索引,由于 login_time
已經(jīng)建立在索引中了,被查詢的 uid
和 login_time
就不用去 row
上獲取數(shù)據(jù)了,從而加速查詢。NULL
值,那么這一列對于此復(fù)合索引就是無效的。所以我們在數(shù)據(jù)庫設(shè)計(jì)時(shí),盡量使用not null
約束以及默認(rèn)值。order by
最后的字段是組合索引的一部分,并且放在索引組合順序的最后,避免出現(xiàn)file_sort 的情況,影響查詢性能。where a=? and b=? order by c
,可以建立聯(lián)合索引(a,b,c)
。 WHERE a>10 ORDER BY b;
,索引(a,b)
無法排序。對列進(jìn)行索引,如果可能應(yīng)該指定一個(gè)前綴長度。例如,如果有一個(gè)CHAR(255)
的列,如果該列在前10
個(gè)或20
個(gè)字符內(nèi),可以做到既使得前綴索引的區(qū)分度接近全列索引,那么就不要對整個(gè)列進(jìn)行索引。因?yàn)槎趟饕粌H可以提高查詢速度而且可以節(jié)省磁盤空間和I/O操作,減少索引文件的維護(hù)開銷??梢允褂?code>count(distinct leftIndex(列名, 索引長度))/count(*) 來計(jì)算前綴索引的區(qū)分度。
但缺點(diǎn)是不能用于 ORDER BY
和 GROUP BY
操作,也不能用于覆蓋索引。
不過很多時(shí)候沒必要對全字段建立索引,根據(jù)實(shí)際文本區(qū)分度決定索引長度即可。
offset
行,而是取 offset+N
行,然后返回放棄前 offset 行,返回 N 行,那當(dāng) offset 特別大的時(shí)候,效率就非常的低下,要么控制返回的總頁數(shù),要么對超過特定閾值的頁數(shù)進(jìn)行 SQL 改寫。id
段,然后再關(guān)聯(lián):selecta.* from 表1 a,(select id from 表1 where 條件 limit100000,20 ) b where a.id=b.id;復(fù)制代碼
select * from user where login_name=?;復(fù)制代碼
select * from user where login_name=? limit 1復(fù)制代碼
需要 join 的字段,數(shù)據(jù)類型必須一致,多表關(guān)聯(lián)查詢時(shí),保證被關(guān)聯(lián)的字段需要有索引。
例如:left join
是由左邊決定的,左邊的數(shù)據(jù)一定都有,所以右邊是我們的關(guān)鍵點(diǎn),建立索引要建右邊的。當(dāng)然如果索引在左邊,可以用right join
。
consts
:單表中最多只有一個(gè)匹配行(主鍵或者唯一索引),在優(yōu)化階段即可讀取到數(shù)據(jù)。
ref
:使用普通的索引(Normal Index)
。
range
:對索引進(jìn)行范圍檢索。
當(dāng) type=index
時(shí),索引物理文件全掃,速度非常慢。
insert
速度,這個(gè)速度損耗可以忽略,但提高查找速度是明顯的。另外,即使在應(yīng)用層做了非常完善的校驗(yàn)控制,只要沒有唯一索引,根據(jù)墨菲定律,必然有臟數(shù)據(jù)產(chǎn)生。既然索引可以加快查詢速度,那么是不是只要是查詢語句需要,就建上索引?答案是否定的。因?yàn)樗饕m然加快了查詢速度,但索引也是有代價(jià)的:索引文件本身要消耗存儲空間,同時(shí)索引會加重插入、刪除和修改記錄時(shí)的負(fù)擔(dān),另外,MySQL在運(yùn)行時(shí)也要消耗資源維護(hù)索引,因此索引并不是越多越好。一般兩種情況下不建議建索引。
第一種情況是表記錄比較少,例如一兩千條甚至只有幾百條記錄的表,沒必要建索引,讓查詢做全表掃描就好了。至于多少條記錄才算多,這個(gè)個(gè)人有個(gè)人的看法,我個(gè)人的經(jīng)驗(yàn)是以2000作為分界線,記錄數(shù)不超過 2000可以考慮不建索引,超過2000條可以酌情考慮索引。
另一種不建議建索引的情況是索引的選擇性較低。所謂索引的選擇性(Selectivity),是指不重復(fù)的索引值(也叫基數(shù),Cardinality)與表記錄數(shù)(#T)的比值:
Index Selectivity = Cardinality / #T復(fù)制代碼
(0, 1]``,選擇性越高的索引價(jià)值越大,這是由
B+Tree的性質(zhì)決定的。例如,
employees.titles表,如果
title`字段經(jīng)常被單獨(dú)查詢,是否需要建索引,我們看一下它的選擇性:SELECT count(DISTINCT(title))/count(*) AS Selectivity FROM employees.titles; +-------------+| Selectivity | +-------------+| 0.0000 | +-------------+復(fù)制代碼
title
的選擇性不足0.0001
(精確值為0.00001579),所以實(shí)在沒有什么必要為其單獨(dú)建索引。
有一種與索引選擇性有關(guān)的索引優(yōu)化策略叫做前綴索引,就是用列的前綴代替整個(gè)列作為索引key,當(dāng)前綴長度合適時(shí),可以做到既使得前綴索引的選擇性接近全列索引,同時(shí)因?yàn)樗饕齥ey變短而減少了索引文件的大小和維護(hù)開銷。下面以employees.employees
表為例介紹前綴索引的選擇和使用。
假設(shè)employees表只有一個(gè)索引<emp_no>,那么如果我們想按名字搜索一個(gè)人,就只能全表掃描了:
EXPLAIN SELECT * FROM employees.employees WHERE first_name='Eric' AND last_name='Anido'; +----+-------------+-----------+------+---------------+------+---------+------+--------+-------------+| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-----------+------+---------------+------+---------+------+--------+-------------+| 1 | SIMPLE | employees | ALL | NULL | NULL | NULL | NULL | 300024 | Using where | +----+-------------+-----------+------+---------------+------+---------+------+--------+-------------+復(fù)制代碼
<first_name>
或<first_name, last_name>
,看下兩個(gè)索引的選擇性:SELECT count(DISTINCT(first_name))/count(*) AS Selectivity FROM employees.employees; +-------------+| Selectivity | +-------------+| 0.0042 | +-------------+SELECT count(DISTINCT(concat(first_name, last_name)))/count(*) AS Selectivity FROM employees.employees; +-------------+| Selectivity | +-------------+| 0.9313 | +-------------+復(fù)制代碼
<first_name>
顯然選擇性太低,`<first_name, last_name>
選擇性很好,但是first_name
和last_name
加起來長度為30
,有沒有兼顧長度和選擇性的辦法?可以考慮用first_name和last_name的前幾個(gè)字符建立索引,例如<first_name, left(last_name, 3)>
,看看其選擇性:SELECT count(DISTINCT(concat(first_name, left(last_name, 3))))/count(*) AS Selectivity FROM employees.employees; +-------------+ | Selectivity | +-------------+ | 0.7879 | +-------------+復(fù)制代碼
SELECT count(DISTINCT(concat(first_name, left(last_name, 4))))/count(*) AS Selectivity FROM employees.employees; +-------------+| Selectivity | +-------------+| 0.9007 | +-------------+復(fù)制代碼
18
,比<first_name, last_name>
短了接近一半,我們把這個(gè)前綴索引建上:ALTER TABLE employees.employees ADD INDEX `first_name_last_name4` (first_name, last_name(4));復(fù)制代碼
SHOW PROFILES; +----------+------------+---------------------------------------------------------------------------------+ | Query_ID | Duration | Query | +----------+------------+---------------------------------------------------------------------------------+ | 87 | 0.11941700 | SELECT * FROM employees.employees WHERE first_name='Eric' AND last_name='Anido' | | 90 | 0.00092400 | SELECT * FROM employees.employees WHERE first_name='Eric' AND last_name='Anido' | +----------+------------+---------------------------------------------------------------------------------+復(fù)制代碼
性能的提升是顯著的,查詢速度提高了120多倍。
前綴索引兼顧索引大小和查詢速度,但是其缺點(diǎn)是不能用于ORDER BY
和GROUP BY
操作,也不能用于Covering index
(即當(dāng)索引本身包含查詢所需全部數(shù)據(jù)時(shí),不再訪問數(shù)據(jù)文件本身)。
本篇文章腦圖
和PDF文檔
已經(jīng)準(zhǔn)備好,有需要的伙伴可以回復(fù)關(guān)鍵詞索引優(yōu)化
獲取。
關(guān)于MySQL索引優(yōu)化規(guī)則是什么就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,可以學(xué)到更多知識。如果覺得文章不錯(cuò),可以把它分享出去讓更多的人看到。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。