MySQL索引及優(yōu)化實戰(zhàn)-創(chuàng)新互聯(lián)

索引概念和作用

我們提供的服務(wù)有:網(wǎng)站建設(shè)、成都網(wǎng)站建設(shè)、微信公眾號開發(fā)、網(wǎng)站優(yōu)化、網(wǎng)站認(rèn)證、宜興ssl等。為千余家企事業(yè)單位解決了網(wǎng)站和推廣的問題。提供周到的售前咨詢和貼心的售后服務(wù),是有科學(xué)管理、有技術(shù)的宜興網(wǎng)站制作公司

索引是一種使記錄有序化的技術(shù),它可以指定按某列/某幾列預(yù)先排序,從而大大提高查詢速度(類似于漢語詞典中按照拼音或者筆畫查找)。

索引的主要作用是加快數(shù)據(jù)查找速度,提高數(shù)據(jù)庫的性能。

MySQL 索引類型

從物理存儲角度上,索引可以分為聚集索引和非聚集索引。

1. 聚集索引(Clustered Index)

聚集索引決定數(shù)據(jù)在磁盤上的物理排序,一個表只能有一個聚集索引。

2. 非聚集索引(Non-clustered Index)

非聚集索引并不決定數(shù)據(jù)在磁盤上的物理排序,索引上只包含被建立索引的數(shù)據(jù),以及一個行定位符 row-locator,這個行定位符,可以理解為一個聚集索引物理排序的指針,通過這個指針,可以找到行數(shù)據(jù)。

從邏輯角度,索引可以分為以下幾種。

1.普通索引:最基本的索引,它沒有任何限制。

2唯一索引:與普通索引類似,不同的就是索引列的值必須唯一,但允許有空值。如果是組合索引,則列值的組合必須唯一。

3.主鍵索引:它是一種特殊的唯一索引,用于唯一標(biāo)識數(shù)據(jù)表中的某一條記錄,不允許有空值,一般用 primary key 來約束。主鍵和聚集索引的關(guān)系詳見“問題詳解”中的第4題。

4.聯(lián)合索引(又叫復(fù)合索引):多個字段上建立的索引,能夠加速復(fù)合查詢條件的檢索。

5.全文索引:老版本 MySQL 自帶的全文索引只能用于數(shù)據(jù)庫引擎為 MyISAM 的數(shù)據(jù)表,新版本 MySQL 5.6 的 InnoDB 支持全文索引。默認(rèn) MySQL 不支持中文全文檢索,可以通過擴展 MySQL,添加中文全文檢索或為中文內(nèi)容表提供一個對應(yīng)的英文索引表的方式來支持中文。

MySQL索引優(yōu)化規(guī)則

可以通過以下規(guī)則對 MySQL 索引進行優(yōu)化。

1.前導(dǎo)模糊查詢不能使用索引。

例如下面 SQL 語句不能使用索引。

select?*?from?doc?where?title?like?'%XX'

而非前導(dǎo)模糊查詢則可以使用索引,如下面的 SQL 語句。

select?*?from?doc?where?title?like?'XX%'

頁面搜索嚴(yán)禁左模糊或者全模糊,如果需要可以用搜索引擎來解決。

2.union、in、or 都能夠命中索引,建議使用 in。

(1)union:能夠命中索引。

示例代碼如下:

select?*?from?doc?where?status=1
union?all
select?*?from?doc?where?status=2

直接告訴 MySQL 怎么做,MySQL 耗費的 CPU 最少,但是一般不這么寫 SQL。

(2)in:能夠命中索引。

示例代碼如下:

select?*?from?doc?where?status?in?(1,?2)

查詢優(yōu)化耗費的 CPU 比 union all 多,但可以忽略不計,一般情況下建議使用 in

(3)or:新版的 MySQL 能夠命中索引。

示例代碼如下:

select?*?from?doc?where?status?=?1?or?status?=?2

查詢優(yōu)化耗費的 CPU 比 in 多,不建議頻繁用 or。

3.負(fù)向條件查詢不能使用索引,可以優(yōu)化為 in 查詢。

負(fù)向條件有:!=、<>、not in、not exists、not like 等。

例如下面代碼:

select?*?from?doc?where?status?!=?1?and?status?!=?2

可以優(yōu)化為 in 查詢:

select?*?from?doc?where?status?in?(0,3,4)

4.聯(lián)合索引最左前綴原則(又叫最左側(cè)查詢)

如果在(a,b,c)三個字段上建立聯(lián)合索引,那么它能夠加快 a | (a,b) | (a,b,c) 三組查詢速度。

例如登錄業(yè)務(wù)需求,代碼如下。

select?uid,?login_time?from?user?where?login_name=??andpasswd=?

可以建立(login_name, passwd)的聯(lián)合索引。

因為業(yè)務(wù)上幾乎沒有 passwd 的單條件查詢需求,而有很多 login_name 的單條件查詢需求,所以可以建立(login_name, passwd)的聯(lián)合索引,而不是(passwd, login_name)。

MySQL 索引及優(yōu)化實戰(zhàn)

(1)建聯(lián)合索引的時候,區(qū)分度最高的字段在最左邊。

如果建立了(a,b)聯(lián)合索引,就不必再單獨建立 a 索引。同理,如果建立了(a,b,c)聯(lián)合索引,就不必再單獨建立 a、(a,b) 索引。

(2)存在非等號和等號混合判斷條件時,在建索引時,請把等號條件的列前置。

如 where a>? and b=?,那么即使 a 的區(qū)分度更高,也必須把 b 放在索引的最前列。

(3)最左側(cè)查詢需求,并不是指 SQL 語句的 where 順序要和聯(lián)合索引一致。

下面的 SQL 語句也可以命中 (login_name, passwd) 這個聯(lián)合索引。

select?uid,?login_time?from?user?where?passwd=??andlogin_name=?

但還是建議 where 后的順序和聯(lián)合索引一致,養(yǎng)成好習(xí)慣。

5.范圍列可以用到索引(聯(lián)合索引必須是最左前綴)。

范圍條件有:<、<=、>、>=、between等。

范圍列可以用到索引(聯(lián)合索引必須是最左前綴),但是范圍列后面的列無法用到索引,索引最多用于一個范圍列,如果查詢條件中有兩個范圍列則無法全用到索引。

假如有聯(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.把計算放到業(yè)務(wù)層而不是數(shù)據(jù)庫層。

在字段上進行計算不能命中索引。

例如下面的 SQL 語句。

select?*?from?doc?where?YEAR(create_time)?<=?'2016'

即使 date 上建立了索引,也會全表掃描,可優(yōu)化為值計算,如下:

select?*?from?doc?where?create_time?<=?'2016-01-01'

把計算放到業(yè)務(wù)層,這樣做不僅可以節(jié)省數(shù)據(jù)庫的 CPU,還可以起到查詢緩存優(yōu)化效果。

比如下面的 SQL 語句:

select?*?from?order?where?date?<?=?CURDATE()

可以優(yōu)化為:

select?*?from?order?where?date?<?=?'2018-01-2412:00:00'

優(yōu)化后的 SQL 釋放了數(shù)據(jù)庫的 CPU 多次調(diào)用,傳入的 SQL 相同,才可以利用查詢緩存。

7.強制類型轉(zhuǎn)換會全表掃描

如果 phone 字段是 varchar 類型,則下面的 SQL 不能命中索引。

?select?*?from?user?where?phone=13800001234

可以優(yōu)化為:

select?*?from?user?where?phone='13800001234'

8.更新十分頻繁、數(shù)據(jù)區(qū)分度不高的字段上不宜建立索引。

更新會變更 B+ 樹,更新頻繁的字段建立索引會大大降低數(shù)據(jù)庫性能。

“性別”這種區(qū)分度不大的屬性,建立索引是沒有什么意義的,不能有效過濾數(shù)據(jù),性能與全表掃描類似。

一般區(qū)分度在80%以上的時候就可以建立索引,區(qū)分度可以使用 count(distinct(列名))/count(*) 來計算。

9.利用覆蓋索引來進行查詢操作,避免回表。

被查詢的列,數(shù)據(jù)能從索引中取得,而不用通過行定位符 row-locator 再到 row 上獲取,即“被查詢列要被所建的索引覆蓋”,這能夠加速查詢速度。

例如登錄業(yè)務(wù)需求,代碼如下。

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.如果有 order by、group by 的場景,請注意利用索引的有序性。

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)無法排序。

11.使用短索引(又叫前綴索引)來優(yōu)化索引。

前綴索引,就是用列的前綴代替整個列作為索引 key,當(dāng)前綴長度合適時,可以做到既使得前綴索引的區(qū)分度接近全列索引,同時因為索引 key 變短而減少了索引文件的大小和維護開銷,可以使用 count(distinct left(列名, 索引長度))/count(*) 來計算前綴索引的區(qū)分度。

前綴索引兼顧索引大小和查詢速度,但是其缺點是不能用于 ORDER BY 和 GROUP BY 操作,也不能用于覆蓋索引(Covering Index,即當(dāng)索引本身包含查詢所需全部數(shù)據(jù)時,不再訪問數(shù)據(jù)文件本身),很多時候沒必要對全字段建立索引,根據(jù)實際文本區(qū)分度決定索引長度即可。

例如對于下面的 SQL 語句:

SELEC?*?FROM?employees?WHERE?first_name='Eric'AND?last_name='Anido';

我們可以建立索引:(firstname, lastname)。

12.建立索引的列,不允許為 null。

單列索引不存 null 值,復(fù)合索引不存全為 null 的值,如果列允許為 null,可能會得到“不符合預(yù)期”的結(jié)果集,所以,請使用 not null 約束以及默認(rèn)值。

13.利用延遲關(guān)聯(lián)或者子查詢優(yōu)化超多分頁場景。

MySQL 并不是跳過 offset 行,而是取 offset+N 行,然后返回放棄前 offset 行,返回 N 行,那當(dāng) offset 特別大的時候,效率就非常的低下,要么控制返回的總頁數(shù),要么對超過特定閾值的頁數(shù)進行 SQL 改寫。

示例如下,先快速定位需要獲取的 id 段,然后再關(guān)聯(lián):

selecta.*?from?表1?a,(select?id?from?表1?where?條件?limit100000,20?)?b?where?a.id=b.id

14.業(yè)務(wù)上具有唯一特性的字段,即使是多個字段的組合,也必須建成唯一索引。

不要以為唯一索引影響了 insert 速度,這個速度損耗可以忽略,但提高查找速度是明顯的。另外,即使在應(yīng)用層做了非常完善的校驗控制,只要沒有唯一索引,根據(jù)墨菲定律,必然有臟數(shù)據(jù)產(chǎn)生。

15.超過三個表最好不要 join。

需要 join 的字段,數(shù)據(jù)類型必須一致,多表關(guān)聯(lián)查詢時,保證被關(guān)聯(lián)的字段需要有索引。

16.如果明確知道只有一條結(jié)果返回,limit 1 能夠提高效率。

比如如下 SQL 語句:

select?*?from?user?where?login_name=?

可以優(yōu)化為:

select?*?from?user?where?login_name=??limit?1

自己明確知道只有一條結(jié)果,但數(shù)據(jù)庫并不知道,明確告訴它,讓它主動停止游標(biāo)移動。

17.SQL 性能優(yōu)化 explain 中的 type:至少要達到 range 級別,要求是 ref 級別,如果可以是 consts 最好。

consts:單表中最多只有一個匹配行(主鍵或者唯一索引),在優(yōu)化階段即可讀取到數(shù)據(jù)。

ref:使用普通的索引(Normal Index)。

range:對索引進行范圍檢索。

當(dāng) type=index 時,索引物理文件全掃,速度非常慢。

18.單表索引建議控制在5個以內(nèi)。

19.單索引字段數(shù)不允許超過5個。

字段超過5個時,實際已經(jīng)起不到有效過濾數(shù)據(jù)的作用了。

20.創(chuàng)建索引時避免以下錯誤觀念

(1)索引越多越好,認(rèn)為一個查詢就需要建一個索引。

(2)寧缺勿濫,認(rèn)為索引會消耗空間、嚴(yán)重拖慢更新和新增速度。

(3)抵制惟一索引,認(rèn)為業(yè)務(wù)的惟一性一律需要在應(yīng)用層通過“先查后插”方式解決。

(4)過早優(yōu)化,在不了解系統(tǒng)的情況下就開始優(yōu)化。

MySQL 索引及優(yōu)化實戰(zhàn)

問題詳解

這部分,我將列出平時會遇到的一些問題,并給予解答。

1. 請問如下三條 SQL 該如何建立索引

where?a=1?and?b=1
where?b=1
where?b=1?order?by?time?desc

MySQL 的查詢優(yōu)化器會自動調(diào)整 where 子句的條件順序以使用適合的索引嗎?

回答:

第一問:建議建立兩個索引,即 idxab(a,b) 和 idxbtime(b,time)。

第二問:MySQL 的查詢優(yōu)化器會自動調(diào)整 where 子句的條件順序以使用適合的索引,對于上面的第一條 SQL,如果建立索引為 idxba(b,a) 也是可以用到索引的,不過建議 where 后的字段順序和聯(lián)合索引保持一致,養(yǎng)成好習(xí)慣。

2.假如有聯(lián)合索引(empno、title、fromdate),下面的 SQL 是否可以用到索引,如果可以的話,會使用幾個列?

select?*?fromemployees.titles
where?emp_no?between?'10001'?and'10010'
and?title='Senior?Engineer'?
and?from_date?between?'1986-01-01'and?'1986-12-31'

回答:可以使用索引,可以用到索引全部三個列,這個 SQL 看起來是用了兩個范圍查詢,但作用于 empno 上的“between”實際上相當(dāng)于“in”,也就是說 empno 實際是多值精確匹配,在 MySQL 中要謹(jǐn)慎地區(qū)分多值匹配和范圍匹配,否則會對 MySQL 的行為產(chǎn)生困惑。

3.既然索引可以加快查詢速度,那么是不是只要是查詢語句需要,就建上索引?

回答:不是,因為索引雖然加快了查詢速度,但索引也是有代價的。索引文件本身要消耗存儲空間,同時索引會加重插入、刪除和修改記錄時的負(fù)擔(dān)。另外,MySQL 在運行時也要消耗資源維護索引,因此索引并不是越多越好。一般兩種情況下不建議建索引。第一種情況是表記錄比較少,例如一兩千條甚至只有幾百條記錄的表,沒必要建索引,另一種是數(shù)據(jù)的區(qū)分度比較低,可以使用 count(distinct(列名))/count(*) 來計算區(qū)分度。

4.主鍵和聚集索引的關(guān)系?

回答:在 MySQL 中,InnoDB 引擎表是(聚集)索引組織表(Clustered IndexOrganize Table),它會先按照主鍵進行聚集,如果沒有定義主鍵,InnoDB 會試著使用唯一的非空索引來代替,如果沒有這種索引,InnoDB 就會定義隱藏的主鍵然后在上面進行聚集。由此可見,在 InnoDB 表中,主鍵必然是聚集索引,而聚集索引則未必是主鍵。MyISAM 引擎表是堆組織表(Heap Organize Table),它沒有聚集索引的概念。

5.一個6億的表 a,一個3億的表 b,通過外鍵 tid 關(guān)聯(lián),如何最快的查詢出滿足條件的第50000到第50200中的這200條數(shù)據(jù)記錄?

回答:方法一:如果 a 表 tid 是自增長,并且是連續(xù)的,b表的id為索引。SQL語句如下。

select?*?from?a,b?where?a.tid?=?b.id?and?a.tid>500000?limit200;

方法二:如果 a 表的 tid 不是連續(xù)的,那么就需要使用覆蓋索引,tid 要么是主鍵,要么是輔助索引,b 表 id 也需要有索引。SQL語句如下。

select?*?from?b,?(select?tid?from?a?limit?50000,200)?awhere?b.id?=?a.tid;

6.假如建立聯(lián)合索引(a,b,c),下列語句是否可以使用索引,如果可以,使用了那幾列?(考察聯(lián)合索引最左前綴原則)

where?a=?3
答:是,使用了?a?列。
?
where?a=?3?and?b?=?5
答:是,使用了?a,b?列。
?
where?a?=?3?and?c?=?4?and?b?=?5
答:是,使用了?a,b,c?列。
?
where?b=?3
答:否。
?
where?a=?3?and?c?=?4
答:是,使用了?a?列。
?
where?a?=?3?and?b?>?10?andc?=?7
答:是,使用了?a,b?列。
?
where?a?=?3?and?b?like?'xx%'?andc?=?7
答:是,使用了?a,b?列。

7.文章表的表結(jié)構(gòu)如下:

CREATE?TABLEIF?NOT?EXISTS?`article`?(
`id`?int(10)?unsigned?NOT?NULL?AUTO_INCREMENT,
`author_id`?int(10)?unsigned?NOT?NULL,
`category_id`?int(10)?unsigned?NOT?NULL,
`views`?int(10)?unsigned?NOT?NULL,
`comments`?int(10)?unsigned?NOT?NULL,
`title`?varbinary(255)?NOT?NULL,
`content`?text?NOT?NULL,
PRIMARY?KEY?(`id`)
);

下面語句應(yīng)該如何建立索引?

Select?author_id,?title,?content?from?`article`?where?category_id=?1?and?comments?>?1
order?by?views?desc?limit?1;

回答:

沒有聯(lián)合索引時,explain顯示,如下圖所示:

創(chuàng)建 idxcategoryidcommentsviews(category_id,comments, views) 聯(lián)合索引時,explain顯示,如下圖所示:

創(chuàng)建 idxcategoryidviews(categoryid,views) 聯(lián)合索引,explain 顯示,如下圖所示:

由此可見,可以創(chuàng)建 idxcategoryidviews(categoryid,views) 聯(lián)合索引。

結(jié)語

對于大數(shù)據(jù)量的業(yè)務(wù),應(yīng)該時刻考慮到性能,本文只是從使用層面提供了一些優(yōu)化的思路,由于業(yè)務(wù)和數(shù)據(jù)的復(fù)雜性,需要具體問題具體分析,對于數(shù)據(jù)量比較大的業(yè)務(wù),最好自己使用 explain 具體分析一下,同時,知其然知其所以然,有時間大家可以看看索引的底層實現(xiàn)原理。

另外有需要云服務(wù)器可以了解下創(chuàng)新互聯(lián)scvps.cn,海內(nèi)外云服務(wù)器15元起步,三天無理由+7*72小時售后在線,公司持有idc許可證,提供“云服務(wù)器、裸金屬服務(wù)器、高防服務(wù)器、香港服務(wù)器、美國服務(wù)器、虛擬主機、免備案服務(wù)器”等云主機租用服務(wù)以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡單易用、服務(wù)可用性高、性價比高”等特點與優(yōu)勢,專為企業(yè)上云打造定制,能夠滿足用戶豐富、多元化的應(yīng)用場景需求。

分享標(biāo)題:MySQL索引及優(yōu)化實戰(zhàn)-創(chuàng)新互聯(lián)
URL鏈接:http://bm7419.com/article38/disopp.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供標(biāo)簽優(yōu)化、網(wǎng)站營銷全網(wǎng)營銷推廣、營銷型網(wǎng)站建設(shè)、品牌網(wǎng)站建設(shè)、面包屑導(dǎo)航

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

成都網(wǎng)站建設(shè)