mysql删除unionkey_MySQL索引如何优化?二十条铁则送给你
前言
索引的相信大家都聽(tīng)說(shuō)過(guò),但是真正會(huì)用的又有幾人?平時(shí)工作中寫SQL真的會(huì)考慮到這條SQL如何能夠用上索引,如何能夠提升執(zhí)行效率?
此篇文章詳細(xì)的講述了索引優(yōu)化的幾個(gè)原則,只要在工作中能夠隨時(shí)應(yīng)用到,相信你寫出的SQL一定是效率最高,最牛逼的。
文章的腦圖如下:
索引優(yōu)化規(guī)則 1、like語(yǔ)句的前導(dǎo)模糊查詢不能使用索引 select * from doc where title like '%XX'; --不能使用索引select * from doc where title like 'XX%'; --非前導(dǎo)模糊查詢,可以使用索引
因?yàn)轫?yè)面搜索嚴(yán)禁左模糊或者全模糊,如果需要可以使用搜索引擎來(lái)解決。
2、union、in、or 都能夠命中索引,建議使用 in
union能夠命中索引,并且MySQL 耗費(fèi)的 CPU 最少。
select * from doc where status=1union allselect * from doc where status=2;
in能夠命中索引,查詢優(yōu)化耗費(fèi)的 CPU 比 union all 多,但可以忽略不計(jì),一般情況下建議使用 in。
select * from doc where status in (1, 2);
or 新版的 MySQL 能夠命中索引,查詢優(yōu)化耗費(fèi)的 CPU 比 in多,不建議頻繁用or。
select * from doc where status = 1 or status = 2
補(bǔ)充:有些地方說(shuō)在where條件中使用or,索引會(huì)失效,造成全表掃描,這是個(gè)誤區(qū):
①要求where子句使用的所有字段,都必須建立索引;
②如果數(shù)據(jù)量太少,mysql制定執(zhí)行計(jì)劃時(shí)發(fā)現(xiàn)全表掃描比索引查找更快,所以會(huì)不使用索引;
③確保mysql版本5.0以上,且查詢優(yōu)化器開(kāi)啟了index_merge_union=on, 也就是變量optimizer_switch里存在index_merge_union且為on。
3、負(fù)向條件查詢不能使用索引
負(fù)向條件有:!=、<>、not in、not exists、not like 等。
例如下面SQL語(yǔ)句:
select * from doc where status != 1 and status != 2;
可以優(yōu)化為 in 查詢:
select * from doc where status in (0,3,4); 4、聯(lián)合索引最左前綴原則
如果在(a,b,c)三個(gè)字段上建立聯(lián)合索引,那么他會(huì)自動(dòng)建立 a| (a,b) | (a,b,c)組索引。
登錄業(yè)務(wù)需求,SQL語(yǔ)句如下:
select uid, login_time from user where login_name=? andpasswd=?
可以建立(login_name, passwd)的聯(lián)合索引。因?yàn)闃I(yè)務(wù)上幾乎沒(méi)有passwd 的單條件查詢需求,而有很多l(xiāng)ogin_name 的單條件查詢需求,所以可以建立(login_name, passwd)的聯(lián)合索引,而不是(passwd, login_name)。
建立聯(lián)合索引的時(shí)候,區(qū)分度最高的字段在最左邊存在非等號(hào)和等號(hào)混合判斷條件時(shí),在建立索引時(shí),把等號(hào)條件的列前置。如 where a>? and b=?,那么即使a 的區(qū)分度更高,也必須把 b 放在索引的最前列。最左前綴查詢時(shí),并不是指SQL語(yǔ)句的where順序要和聯(lián)合索引一致。
下面的 SQL 語(yǔ)句也可以命中 (login_name, passwd) 這個(gè)聯(lián)合索引:
select uid, login_time from user where passwd=? andlogin_name=?
但還是建議 where 后的順序和聯(lián)合索引一致,養(yǎng)成好習(xí)慣。
假如index(a,b,c), where a=3 and b like 'abc%' and c=4,a能用,b能用,c不能用。5、不能使用索引中范圍條件右邊的列(范圍列可以用到索引),范圍列之后列的索引全失效
范圍條件有:、>=、between等。
索引最多用于一個(gè)范圍列,如果查詢條件中有兩個(gè)范圍列則無(wú)法全用到索引。
假如有聯(lián)合索引 (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' 6、不要在索引列上面做任何操作(計(jì)算、函數(shù)),否則會(huì)導(dǎo)致索引失效而轉(zhuǎn)向全表掃描
例如下面的 SQL 語(yǔ)句,即使 date 上建立了索引,也會(huì)全表掃描:
select * from doc where YEAR(create_time) <= '2016';
可優(yōu)化為值計(jì)算,如下:
select * from doc where create_time <= '2016-01-01';
比如下面的 SQL 語(yǔ)句:
select * from order where date < = CURDATE();
可以優(yōu)化為:
select * from order where date < = '2018-01-2412:00:00'; 7、強(qiáng)制類型轉(zhuǎn)換會(huì)全表掃描
字符串類型不加單引號(hào)會(huì)導(dǎo)致索引失效,因?yàn)閙ysql會(huì)自己做類型轉(zhuǎn)換,相當(dāng)于在索引列上進(jìn)行了操作。
如果 phone 字段是 varchar 類型,則下面的 SQL 不能命中索引。
可以優(yōu)化為:
select * from user where phone='13800001234'; 8、更新十分頻繁、數(shù)據(jù)區(qū)分度不高的列不宜建立索引
更新會(huì)變更 B+ 樹(shù),更新頻繁的字段建立索引會(huì)大大降低數(shù)據(jù)庫(kù)性能。
“性別”這種區(qū)分度不大的屬性,建立索引是沒(méi)有什么意義的,不能有效過(guò)濾數(shù)據(jù),性能與全表掃描類似。
一般區(qū)分度在80%以上的時(shí)候就可以建立索引,區(qū)分度可以使用 count(distinct(列名))/count(*) 來(lái)計(jì)算。
9、利用覆蓋索引來(lái)進(jìn)行查詢操作,避免回表,減少select * 的使用
覆蓋索引:查詢的列和所建立的索引的列個(gè)數(shù)相同,字段相同。
被查詢的列,數(shù)據(jù)能從索引中取得,而不用通過(guò)行定位符 row-locator 再到 row 上獲取,即“被查詢列要被所建的索引覆蓋”,這能夠加速查詢速度。
例如登錄業(yè)務(wù)需求,SQL語(yǔ)句如下。
Select uid, login_time from user where login_name=? and passwd=?
可以建立(login_name, passwd, login_time)的聯(lián)合索引,由于 login_time 已經(jīng)建立在索引中了,被查詢的 uid 和 login_time 就不用去 row 上獲取數(shù)據(jù)了,從而加速查詢。
10、索引不會(huì)包含有NULL值的列
只要列中包含有NULL值都將不會(huì)被包含在索引中,復(fù)合索引中只要有一列含有NULL值,那么這一列對(duì)于此復(fù)合索引就是無(wú)效的。所以我們?cè)跀?shù)據(jù)庫(kù)設(shè)計(jì)時(shí),盡量使用not null 約束以及默認(rèn)值。
11、is null, is not null無(wú)法使用索引 12、如果有order by、group by的場(chǎng)景,請(qǐng)注意利用索引的有序性
order by 最后的字段是組合索引的一部分,并且放在索引組合順序的最后,避免出現(xiàn)file_sort 的情況,影響查詢性能。
例如對(duì)于語(yǔ)句 where a=? and b=? order by c,可以建立聯(lián)合索引(a,b,c)。
如果索引中有范圍查找,那么索引有序性無(wú)法利用,如 WHERE a>10 ORDER BY b;,索引(a,b)無(wú)法排序。
13、使用短索引(前綴索引)
對(duì)列進(jìn)行索引,如果可能應(yīng)該指定一個(gè)前綴長(zhǎng)度。例如,如果有一個(gè)CHAR(255)的列,如果該列在前10個(gè)或20個(gè)字符內(nèi),可以做到既使得前綴索引的區(qū)分度接近全列索引,那么就不要對(duì)整個(gè)列進(jìn)行索引。因?yàn)槎趟饕粌H可以提高查詢速度而且可以節(jié)省磁盤空間和I/O操作,減少索引文件的維護(hù)開(kāi)銷。可以使用count(distinct leftIndex(列名, 索引長(zhǎng)度))/count(*) 來(lái)計(jì)算前綴索引的區(qū)分度。
但缺點(diǎn)是不能用于 ORDER BY 和 GROUP BY 操作,也不能用于覆蓋索引。
不過(guò)很多時(shí)候沒(méi)必要對(duì)全字段建立索引,根據(jù)實(shí)際文本區(qū)分度決定索引長(zhǎng)度即可。
14、利用延遲關(guān)聯(lián)或者子查詢優(yōu)化超多分頁(yè)場(chǎng)景
MySQL 并不是跳過(guò) offset 行,而是取 offset+N 行,然后返回放棄前 offset 行,返回 N 行,那當(dāng) offset 特別大的時(shí)候,效率就非常的低下,要么控制返回的總頁(yè)數(shù),要么對(duì)超過(guò)特定閾值的頁(yè)數(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; 15、如果明確知道只有一條結(jié)果返回,limit 1 能夠提高效率
比如如下 SQL 語(yǔ)句:
select * from user where login_name=?;
可以優(yōu)化為:
select * from user where login_name=? limit 1
自己明確知道只有一條結(jié)果,但數(shù)據(jù)庫(kù)并不知道,明確告訴它,讓它主動(dòng)停止游標(biāo)移動(dòng)。
16、超過(guò)三個(gè)表最好不要 join
需要 join 的字段,數(shù)據(jù)類型必須一致,多表關(guān)聯(lián)查詢時(shí),保證被關(guān)聯(lián)的字段需要有索引。
例如:left join是由左邊決定的,左邊的數(shù)據(jù)一定都有,所以右邊是我們的關(guān)鍵點(diǎn),建立索引要建右邊的。當(dāng)然如果索引在左邊,可以用right join。
17、單表索引建議控制在5個(gè)以內(nèi) 18、SQL 性能優(yōu)化 explain 中的 type:至少要達(dá)到 range 級(jí)別,要求是 ref 級(jí)別,如果可以是 consts 最好
consts:單表中最多只有一個(gè)匹配行(主鍵或者唯一索引),在優(yōu)化階段即可讀取到數(shù)據(jù)。
ref:使用普通的索引(Normal Index)。
range:對(duì)索引進(jìn)行范圍檢索。
當(dāng) type=index 時(shí),索引物理文件全掃,速度非常慢。
19、業(yè)務(wù)上具有唯一特性的字段,即使是多個(gè)字段的組合,也必須建成唯一索引
不要以為唯一索引影響了 insert 速度,這個(gè)速度損耗可以忽略,但提高查找速度是明顯的。另外,即使在應(yīng)用層做了非常完善的校驗(yàn)控制,只要沒(méi)有唯一索引,根據(jù)墨菲定律,必然有臟數(shù)據(jù)產(chǎn)生。
20.創(chuàng)建索引時(shí)避免以下錯(cuò)誤觀念
索引越多越好,認(rèn)為需要一個(gè)查詢就建一個(gè)索引。
寧缺勿濫,認(rèn)為索引會(huì)消耗空間、嚴(yán)重拖慢更新和新增速度。
抵制惟一索引,認(rèn)為業(yè)務(wù)的惟一性一律需要在應(yīng)用層通過(guò)“先查后插”方式解決。
過(guò)早優(yōu)化,在不了解系統(tǒng)的情況下就開(kāi)始優(yōu)化。
索引選擇性與前綴索引
既然索引可以加快查詢速度,那么是不是只要是查詢語(yǔ)句需要,就建上索引?答案是否定的。因?yàn)樗饕m然加快了查詢速度,但索引也是有代價(jià)的:索引文件本身要消耗存儲(chǔ)空間,同時(shí)索引會(huì)加重插入、刪除和修改記錄時(shí)的負(fù)擔(dān),另外,MySQL在運(yùn)行時(shí)也要消耗資源維護(hù)索引,因此索引并不是越多越好。一般兩種情況下不建議建索引。
第一種情況是表記錄比較少,例如一兩千條甚至只有幾百條記錄的表,沒(méi)必要建索引,讓查詢做全表掃描就好了。至于多少條記錄才算多,這個(gè)個(gè)人有個(gè)人的看法,我個(gè)人的經(jīng)驗(yàn)是以2000作為分界線,記錄數(shù)不超過(guò) 2000可以考慮不建索引,超過(guò)2000條可以酌情考慮索引。
另一種不建議建索引的情況是索引的選擇性較低。所謂索引的選擇性(Selectivity),是指不重復(fù)的索引值(也叫基數(shù),Cardinality)與表記錄數(shù)(#T)的比值:
Index Selectivity = Cardinality / #T
顯然選擇性的取值范圍為(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 |+-------------+
title的選擇性不足0.0001(精確值為0.00001579),所以實(shí)在沒(méi)有什么必要為其單獨(dú)建索引。
有一種與索引選擇性有關(guān)的索引優(yōu)化策略叫做前綴索引,就是用列的前綴代替整個(gè)列作為索引key,當(dāng)前綴長(zhǎng)度合適時(shí),可以做到既使得前綴索引的選擇性接近全列索引,同時(shí)因?yàn)樗饕齥ey變短而減少了索引文件的大小和維護(hù)開(kāi)銷。下面以employees.employees表為例介紹前綴索引的選擇和使用。
假設(shè)employees表只有一個(gè)索引,那么如果我們想按名字搜索一個(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 |+----+-------------+-----------+------+---------------+------+---------+------+--------+-------------+
如果頻繁按名字搜索員工,這樣顯然效率很低,因此我們可以考慮建索引。有兩種選擇,建或,看下兩個(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 |+-------------+
顯然選擇性太低,`選擇性很好,但是first_name和last_name加起來(lái)長(zhǎng)度為30,有沒(méi)有兼顧長(zhǎng)度和選擇性的辦法?可以考慮用first_name和last_name的前幾個(gè)字符建立索引,例如,看看其選擇性:
SELECT count(DISTINCT(concat(first_name, left(last_name, 3))))/count(*) AS Selectivity FROM employees.employees;+-------------+| Selectivity |+-------------+| 0.7879 |+-------------+
選擇性還不錯(cuò),但離0.9313還是有點(diǎn)距離,那么把last_name前綴加到4:
SELECT count(DISTINCT(concat(first_name, left(last_name, 4))))/count(*) AS Selectivity FROM employees.employees;+-------------+| Selectivity |+-------------+| 0.9007 |
這時(shí)選擇性已經(jīng)很理想了,而這個(gè)索引的長(zhǎng)度只有18,比短了接近一半,我們把這個(gè)前綴索引建上:
ALTER TABLE employees.employeesADD INDEX `first_name_last_name4` (first_name, last_name(4));
此時(shí)再執(zhí)行一遍按名字查詢,比較分析一下與建索引前的結(jié)果:
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' |+----------+------------+---------------------------------------------------------------------------------+
性能的提升是顯著的,查詢速度提高了120多倍。
前綴索引兼顧索引大小和查詢速度,但是其缺點(diǎn)是不能用于ORDER BY和GROUP BY操作,也不能用于Covering index(即當(dāng)索引本身包含查詢所需全部數(shù)據(jù)時(shí),不再訪問(wèn)數(shù)據(jù)文件本身)。
總結(jié)
本文主要講了索引優(yōu)化的二十個(gè)原則,希望讀者喜歡。
作者:不才陳某
鏈接:https://juejin.im/post/6867180058549682184
總結(jié)
以上是生活随笔為你收集整理的mysql删除unionkey_MySQL索引如何优化?二十条铁则送给你的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: android targetapi版本低
- 下一篇: mysql 索引能不能太多,mysql索