MySQL憑借著出色的性能、低廉的成本掂林、豐富的資源臣缀,已經(jīng)成為絕大多數(shù)互聯(lián)網(wǎng)公司的首選關(guān)系型數(shù)據(jù)庫。雖然性能出色泻帮,但所謂“好馬配好鞍”,如何能夠更好的使用它计寇,已經(jīng)成為開發(fā)工程師的必修課锣杂,我們經(jīng)常會從職位描述上看到諸如“精通MySQL”、“SQL語句優(yōu)化”番宁、“了解數(shù)據(jù)庫原理”等要求元莫。
我們知道一般的應(yīng)用系統(tǒng),讀寫比例在10:1左右,而且插入操作和一般的更新操作很少出現(xiàn)性能問題,遇到最多的曹质,也是最容易出問題的唱矛,還是一些復(fù)雜的查詢操作,所以查詢語句的優(yōu)化顯然是重中之重瞧掺。
本文旨在以開發(fā)工程師的角度來解釋數(shù)據(jù)庫索引的原理和如何優(yōu)化慢查詢。
MySQL索引原理
1.索引目的
索引的目的在于提高查詢效率,可以類比字典企锌,如果要查“mysql”這個單詞,我們肯定需要定位到m字母于未,然后從下往下找到y(tǒng)字母撕攒,再找到剩下的sql陡鹃。如果沒有索引,那么你可能需要把所有單詞看一遍才能找到你想要的抖坪,如果我想找到m開頭的單詞呢萍鲸?或者ze開頭的單詞呢?是不是覺得如果沒有索引擦俐,這個事情根本無法完成脊阴?
2.索引原理
除了詞典,生活中隨處可見索引的例子捌肴,如火車站的車次表蹬叭、圖書的目錄等。它們的原理都是一樣的状知,通過不斷的縮小想要獲得數(shù)據(jù)的范圍來篩選出最終想要的結(jié)果秽五,同時把隨機的事件變成順序的事件,也就是我們總是通過同一種查找方式來鎖定數(shù)據(jù)饥悴。
數(shù)據(jù)庫也是一樣坦喘,但顯然要復(fù)雜許多,因為不僅面臨著等值查詢西设,還有范圍查詢(>瓣铣、<、between贷揽、in)棠笑、模糊查詢(like)、并集查詢(or)等等禽绪。數(shù)據(jù)庫應(yīng)該選擇怎么樣的方式來應(yīng)對所有的問題呢蓖救?我們回想字典的例子,能不能把數(shù)據(jù)分成段印屁,然后分段查詢呢循捺?最簡單的如果1000條數(shù)據(jù),1到100分成第一段雄人,101到200分成第二段从橘,201到300分成第三段……這樣查第250條數(shù)據(jù),只要找第三段就可以了础钠,一下子去除了90%的無效數(shù)據(jù)恰力。但如果是1千萬的記錄呢,分成幾段比較好珍坊?稍有算法基礎(chǔ)的同學(xué)會想到搜索樹牺勾,其平均復(fù)雜度是lgN,具有不錯的查詢性能阵漏。但這里我們忽略了一個關(guān)鍵的問題驻民,復(fù)雜度模型是基于每次相同的操作成本來考慮的翻具,數(shù)據(jù)庫實現(xiàn)比較復(fù)雜,數(shù)據(jù)保存在磁盤上回还,而為了提高性能裆泳,每次又可以把部分數(shù)據(jù)讀入內(nèi)存來計算,因為我們知道訪問磁盤的成本大概是訪問內(nèi)存的十萬倍左右柠硕,所以簡單的搜索樹難以滿足復(fù)雜的應(yīng)用場景工禾。
3.磁盤IO與預(yù)讀
前面提到了訪問磁盤,那么這里先簡單介紹一下磁盤IO和預(yù)讀蝗柔,磁盤讀取數(shù)據(jù)靠的是機械運動闻葵,每次讀取數(shù)據(jù)花費的時間可以分為尋道時間、旋轉(zhuǎn)延遲癣丧、傳輸時間三個部分槽畔,尋道時間指的是磁臂移動到指定磁道所需要的時間,主流磁盤一般在5ms以下胁编;旋轉(zhuǎn)延遲就是我們經(jīng)常聽說的磁盤轉(zhuǎn)速厢钧,比如一個磁盤7200轉(zhuǎn),表示每分鐘能轉(zhuǎn)7200次嬉橙,也就是說1秒鐘能轉(zhuǎn)120次早直,旋轉(zhuǎn)延遲就是1/120/2 = 4.17ms;傳輸時間指的是從磁盤讀出或?qū)?shù)據(jù)寫入磁盤的時間市框,一般在零點幾毫秒霞扬,相對于前兩個時間可以忽略不計。那么訪問一次磁盤的時間枫振,即一次磁盤IO的時間約等于5+4.17 = 9ms左右祥得,聽起來還挺不錯的,但要知道一臺500 -MIPS的機器每秒可以執(zhí)行5億條指令蒋得,因為指令依靠的是電的性質(zhì),換句話說執(zhí)行一次IO的時間可以執(zhí)行40萬條指令乒疏,數(shù)據(jù)庫動輒十萬百萬乃至千萬級數(shù)據(jù)额衙,每次9毫秒的時間,顯然是個災(zāi)難怕吴。下圖是計算機硬件延遲的對比圖窍侧,供大家參考:
考慮到磁盤IO是非常高昂的操作,計算機操作系統(tǒng)做了一些優(yōu)化转绷,當(dāng)一次IO時伟件,不光把當(dāng)前磁盤地址的數(shù)據(jù),而是把相鄰的數(shù)據(jù)也都讀取到內(nèi)存緩沖區(qū)內(nèi)议经,因為局部預(yù)讀性原理告訴我們斧账,當(dāng)計算機訪問一個地址的數(shù)據(jù)的時候谴返,與其相鄰的數(shù)據(jù)也會很快被訪問到。每一次IO讀取的數(shù)據(jù)我們稱之為一頁(page)咧织。具體一頁有多大數(shù)據(jù)跟操作系統(tǒng)有關(guān)嗓袱,一般為4k或8k,也就是我們讀取一頁內(nèi)的數(shù)據(jù)時候习绢,實際上才發(fā)生了一次IO渠抹,這個理論對于索引的數(shù)據(jù)結(jié)構(gòu)設(shè)計非常有幫助。
4.索引的數(shù)據(jù)結(jié)構(gòu)
前面講了生活中索引的例子闪萄,索引的基本原理梧却,數(shù)據(jù)庫的復(fù)雜性,又講了操作系統(tǒng)的相關(guān)知識败去,目的就是讓大家了解放航,任何一種數(shù)據(jù)結(jié)構(gòu)都不是憑空產(chǎn)生的,一定會有它的背景和使用場景为迈,我們現(xiàn)在總結(jié)一下三椿,我們需要這種數(shù)據(jù)結(jié)構(gòu)能夠做些什么,其實很簡單葫辐,那就是:每次查找數(shù)據(jù)時把磁盤IO次數(shù)控制在一個很小的數(shù)量級搜锰,最好是常數(shù)數(shù)量級。那么我們就想到如果一個高度可控的多路搜索樹是否能滿足需求呢耿战?就這樣蛋叼,b+樹應(yīng)運而生。
5.詳解b+樹
如上圖剂陡,是一顆b+樹狈涮,關(guān)于b+樹的定義可以參見B+樹,這里只說一些重點鸭栖,淺藍色的塊我們稱之為一個磁盤塊歌馍,可以看到每個磁盤塊包含幾個數(shù)據(jù)項(深藍色所示)和指針(黃色所示),如磁盤塊1包含數(shù)據(jù)項17和35晕鹊,包含指針P1松却、P2、P3溅话,P1表示小于17的磁盤塊晓锻,P2表示在17和35之間的磁盤塊,P3表示大于35的磁盤塊飞几。真實的數(shù)據(jù)存在于葉子節(jié)點即3砚哆、5、9屑墨、10躁锁、13纷铣、15、28灿里、29关炼、36、60匣吊、75儒拂、79、90色鸳、99社痛。非葉子節(jié)點只不存儲真實的數(shù)據(jù),只存儲指引搜索方向的數(shù)據(jù)項命雀,如17蒜哀、35并不真實存在于數(shù)據(jù)表中。
6.b+樹的查找過程
如圖所示吏砂,如果要查找數(shù)據(jù)項29撵儿,那么首先會把磁盤塊1由磁盤加載到內(nèi)存,此時發(fā)生一次IO狐血,在內(nèi)存中用二分查找確定29在17和35之間淀歇,鎖定磁盤塊1的P2指針,內(nèi)存時間因為非常短(相比磁盤的IO)可以忽略不計匈织,通過磁盤塊1的P2指針的磁盤地址把磁盤塊3由磁盤加載到內(nèi)存浪默,發(fā)生第二次IO,29在26和30之間缀匕,鎖定磁盤塊3的P2指針纳决,通過指針加載磁盤塊8到內(nèi)存,發(fā)生第三次IO乡小,同時內(nèi)存中做二分查找找到29阔加,結(jié)束查詢,總計三次IO满钟。真實的情況是掸哑,3層的b+樹可以表示上百萬的數(shù)據(jù),如果上百萬的數(shù)據(jù)查找只需要三次IO零远,性能提高將是巨大的,如果沒有索引厌蔽,每個數(shù)據(jù)項都要發(fā)生一次IO牵辣,那么總共需要百萬次的IO,顯然成本非常非常高奴饮。
7.b+樹性質(zhì)
1.通過上面的分析纬向,我們知道IO次數(shù)取決于b+數(shù)的高度h择浊,假設(shè)當(dāng)前數(shù)據(jù)表的數(shù)據(jù)為N,每個磁盤塊的數(shù)據(jù)項的數(shù)量是m逾条,則有h=㏒(m+1)N琢岩,當(dāng)數(shù)據(jù)量N一定的情況下,m越大师脂,h越械?住;而m = 磁盤塊的大小 / 數(shù)據(jù)項的大小吃警,磁盤塊的大小也就是一個數(shù)據(jù)頁的大小糕篇,是固定的,如果數(shù)據(jù)項占的空間越小酌心,數(shù)據(jù)項的數(shù)量越多拌消,樹的高度越低。這就是為什么每個數(shù)據(jù)項安券,即索引字段要盡量的小墩崩,比如int占4字節(jié),要比bigint8字節(jié)少一半侯勉。這也是為什么b+樹要求把真實的數(shù)據(jù)放到葉子節(jié)點而不是內(nèi)層節(jié)點鹦筹,一旦放到內(nèi)層節(jié)點,磁盤塊的數(shù)據(jù)項會大幅度下降壳鹤,導(dǎo)致樹增高盛龄。當(dāng)數(shù)據(jù)項等于1時將會退化成線性表。
2.當(dāng)b+樹的數(shù)據(jù)項是復(fù)合的數(shù)據(jù)結(jié)構(gòu)芳誓,比如(name,age,sex)的時候余舶,b+數(shù)是按照從左到右的順序來建立搜索樹的,比如當(dāng)(張三,20,F)這樣的數(shù)據(jù)來檢索的時候锹淌,b+樹會優(yōu)先比較name來確定下一步的所搜方向匿值,如果name相同再依次比較age和sex,最后得到檢索的數(shù)據(jù)赂摆;但當(dāng)(20,F)這樣的沒有name的數(shù)據(jù)來的時候挟憔,b+樹就不知道下一步該查哪個節(jié)點,因為建立搜索樹的時候name就是第一個比較因子烟号,必須要先根據(jù)name來搜索才能知道下一步去哪里查詢绊谭。比如當(dāng)(張三,F)這樣的數(shù)據(jù)來檢索時,b+樹可以用name來指定搜索方向汪拥,但下一個字段age的缺失达传,所以只能把名字等于張三的數(shù)據(jù)都找到,然后再匹配性別是F的數(shù)據(jù)了, 這個是非常重要的性質(zhì)宪赶,即索引的最左匹配特性宗弯。
慢查詢優(yōu)化
關(guān)于MySQL索引原理是比較枯燥的東西,大家只需要有一個感性的認識搂妻,并不需要理解得非常透徹和深入蒙保。我們回頭來看看一開始我們說的慢查詢,了解完索引原理之后欲主,大家是不是有什么想法呢邓厕?先總結(jié)一下索引的幾大基本原則
建索引的幾大原則
1.最左前綴匹配原則
非常重要的原則,mysql會一直向右匹配直到遇到范圍查詢(>岛蚤、<邑狸、between、like)就停止匹配涤妒,比如a = 1 and b = 2 and c > 3 and d = 4 如果建立(a,b,c,d)順序的索引单雾,d是用不到索引的,如果建立(a,b,d,c)的索引則都可以用到她紫,a,b,d的順序可以任意調(diào)整硅堆。
2.=和in可以亂序
比如a = 1 and b = 2 and c = 3 建立(a,b,c)索引可以任意順序,mysql的查詢優(yōu)化器會幫你優(yōu)化成索引可以識別的形式
3.盡量選擇區(qū)分度高的列作為索引
區(qū)分度的公式是count(distinct col)/count(*)贿讹,表示字段不重復(fù)的比例渐逃,比例越大我們掃描的記錄數(shù)越少,唯一鍵的區(qū)分度是1民褂,而一些狀態(tài)茄菊、性別字段可能在大數(shù)據(jù)面前區(qū)分度就是0,那可能有人會問赊堪,這個比例有什么經(jīng)驗值嗎面殖?使用場景不同,這個值也很難確定哭廉,一般需要join的字段我們都要求是0.1以上脊僚,即平均1條掃描10條記錄
4.索引列不能參與計算,保持列“干凈”
比如from_unixtime(create_time) = ’2014-05-29’就不能使用到索引遵绰,原因很簡單辽幌,b+樹中存的都是數(shù)據(jù)表中的字段值,但進行檢索時椿访,需要把所有元素都應(yīng)用函數(shù)才能比較乌企,顯然成本太大。所以語句應(yīng)該寫成create_time = unix_timestamp(’2014-05-29’);
5.盡量的擴展索引成玫,不要新建索引逛犹。
比如表中已經(jīng)有a的索引端辱,現(xiàn)在要加(a,b)的索引,那么只需要修改原來的索引即可
查詢優(yōu)化神器 – explain命令
關(guān)于explain命令相信大家并不陌生虽画,具體用法和字段含義可以參考官網(wǎng)explain-output,這里需要強調(diào)rows是核心指標荣病,絕大部分rows小的語句執(zhí)行一定很快(有例外码撰,下面會講到)。所以優(yōu)化語句基本上都是在優(yōu)化rows个盆。
慢查詢優(yōu)化基本步驟
0.先運行看看是否真的很慢脖岛,注意設(shè)置SQL_NO_CACHE
1.where條件單表查,鎖定最小返回記錄表颊亮。這句話的意思是把查詢語句的where都應(yīng)用到表中返回的記錄數(shù)最小的表開始查起柴梆,單表每個字段分別查詢,看哪個字段的區(qū)分度最高
2.explain查看執(zhí)行計劃终惑,是否與1預(yù)期一致(從鎖定記錄較少的表開始查詢)
3.order by limit 形式的sql語句讓排序的表優(yōu)先查
4.了解業(yè)務(wù)方使用場景
5.加索引時參照建索引的幾大原則
6.觀察結(jié)果绍在,不符合預(yù)期繼續(xù)從0分析
慢查詢案例
下面幾個例子詳細解釋了如何分析和優(yōu)化慢查詢
復(fù)雜語句寫法
很多情況下,我們寫SQL只是為了實現(xiàn)功能雹有,這只是第一步偿渡,不同的語句書寫方式對于效率往往有本質(zhì)的差別,這要求我們對mysql的執(zhí)行計劃和索引原則有非常清楚的認識霸奕,請看下面的語句
select
distinct cert.emp_id
from
cm_log cl
inner join
(
select
emp.id as emp_id,
emp_cert.id as cert_id
from
employee emp
left join
emp_certificate emp_cert
on emp.id = emp_cert.emp_id
where
emp.is_deleted=0
) cert
on (
cl.ref_table='Employee'
and cl.ref_oid= cert.emp_id
)
or (
cl.ref_table='EmpCertificate'
and cl.ref_oid= cert.cert_id
)
where
cl.last_upd_date >='2013-11-07 15:03:00'
and cl.last_upd_date<='2013-11-08 16:00:00';
0.先運行一下溜宽,53條記錄 1.87秒,又沒有用聚合語句质帅,比較慢
53 rows in set (1.87 sec)
1.explain
+----+-------------+------------+-------+---------------------------------+-----------------------+---------+-------------------+-------+--------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+------------+-------+---------------------------------+-----------------------+---------+-------------------+-------+--------------------------------+
| 1 | PRIMARY | cl | range | cm_log_cls_id,idx_last_upd_date | idx_last_upd_date | 8 | NULL | 379 | Using where; Using temporary |
| 1 | PRIMARY | <derived2> | ALL | NULL | NULL | NULL | NULL | 63727 | Using where; Using join buffer |
| 2 | DERIVED | emp | ALL | NULL | NULL | NULL | NULL | 13317 | Using where |
| 2 | DERIVED | emp_cert | ref | emp_certificate_empid | emp_certificate_empid | 4 | meituanorg.emp.id | 1 | Using index |
+----+-------------+------------+-------+---------------------------------+-----------------------+---------+-------------------+-------+--------------------------------+
簡述一下執(zhí)行計劃适揉,首先mysql根據(jù)idx_last_upd_date索引掃描cm_log表獲得379條記錄;然后查表掃描了63727條記錄煤惩,分為兩部分嫉嘀,derived表示構(gòu)造表,也就是不存在的表盟庞,可以簡單理解成是一個語句形成的結(jié)果集吃沪,后面的數(shù)字表示語句的ID。derived2表示的是ID = 2的查詢構(gòu)造了虛擬表什猖,并且返回了63727條記錄票彪。我們再來看看ID = 2的語句究竟做了寫什么返回了這么大量的數(shù)據(jù),首先全表掃描employee表13317條記錄不狮,然后根據(jù)索引emp_certificate_empid關(guān)聯(lián)emp_certificate表降铸,rows = 1表示,每個關(guān)聯(lián)都只鎖定了一條記錄摇零,效率比較高推掸。獲得后,再和cm_log的379條記錄根據(jù)規(guī)則關(guān)聯(lián)。從執(zhí)行過程上可以看出返回了太多的數(shù)據(jù)谅畅,返回的數(shù)據(jù)絕大部分cm_log都用不到登渣,因為cm_log只鎖定了379條記錄。
如何優(yōu)化呢毡泻?可以看到我們在運行完后還是要和cm_log做join,那么我們能不能之前和cm_log做join呢胜茧?仔細分析語句不難發(fā)現(xiàn),其基本思想是如果cm_log的ref_table是EmpCertificate就關(guān)聯(lián)emp_certificate表仇味,如果ref_table是Employee就關(guān)聯(lián)employee表呻顽,我們完全可以拆成兩部分,并用union連接起來丹墨,注意這里用union廊遍,而不用union all是因為原語句有“distinct”來得到唯一的記錄,而union恰好具備了這種功能贩挣。如果原語句中沒有distinct不需要去重喉前,我們就可以直接使用union all了,因為使用union需要去重的動作揽惹,會影響SQL性能被饿。
優(yōu)化過的語句如下
select
emp.id
from
cm_log cl
inner join
employee emp
on cl.ref_table = 'Employee'
and cl.ref_oid = emp.id
where
cl.last_upd_date >='2013-11-07 15:03:00'
and cl.last_upd_date<='2013-11-08 16:00:00'
and emp.is_deleted = 0
union
select
emp.id
from
cm_log cl
inner join
emp_certificate ec
on cl.ref_table = 'EmpCertificate'
and cl.ref_oid = ec.id
inner join
employee emp
on emp.id = ec.emp_id
where
cl.last_upd_date >='2013-11-07 15:03:00'
and cl.last_upd_date<='2013-11-08 16:00:00'
and emp.is_deleted = 0
4.不需要了解業(yè)務(wù)場景,只需要改造的語句和改造之前的語句保持結(jié)果一致
5.現(xiàn)有索引可以滿足搪搏,不需要建索引
6.用改造后的語句實驗一下狭握,只需要10ms 降低了近200倍!
+----+--------------+------------+--------+---------------------------------+-------------------+---------+-----------------------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+--------------+------------+--------+---------------------------------+-------------------+---------+-----------------------+------+-------------+
| 1 | PRIMARY | cl | range | cm_log_cls_id,idx_last_upd_date | idx_last_upd_date | 8 | NULL | 379 | Using where |
| 1 | PRIMARY | emp | eq_ref | PRIMARY | PRIMARY | 4 | meituanorg.cl.ref_oid | 1 | Using where |
| 2 | UNION | cl | range | cm_log_cls_id,idx_last_upd_date | idx_last_upd_date | 8 | NULL | 379 | Using where |
| 2 | UNION | ec | eq_ref | PRIMARY,emp_certificate_empid | PRIMARY | 4 | meituanorg.cl.ref_oid | 1 | |
| 2 | UNION | emp | eq_ref | PRIMARY | PRIMARY | 4 | meituanorg.ec.emp_id | 1 | Using where |
| NULL | UNION RESULT | <union1,2> | ALL | NULL | NULL | NULL | NULL | NULL | |
+----+--------------+------------+--------+---------------------------------+-------------------+---------+-----------------------+------+-------------+
53 rows in set (0.01 sec)
明確應(yīng)用場景
舉這個例子的目的在于顛覆我們對列的區(qū)分度的認知疯溺,一般上我們認為區(qū)分度越高的列论颅,越容易鎖定更少的記錄,但在一些特殊的情況下囱嫩,這種理論是有局限性的
select
*
from
stage_poi sp
where
sp.accurate_result=1
and (
sp.sync_status=0
or sp.sync_status=2
or sp.sync_status=4
);
0.先看看運行多長時間,951條數(shù)據(jù)6.22秒恃疯,真的很慢
951 rows in set (6.22 sec)
1.先explain,rows達到了361萬墨闲,type = ALL表明是全表掃描
+----+-------------+-------+------+---------------+------+---------+------+---------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+---------+-------------+
| 1 | SIMPLE | sp | ALL | NULL | NULL | NULL | NULL | 3613155 | Using where |
+----+-------------+-------+------+---------------+------+---------+------+---------+-------------+
2.所有字段都應(yīng)用查詢返回記錄數(shù)今妄,因為是單表查詢 0已經(jīng)做過了951條
3.讓explain的rows 盡量逼近951
看一下accurate_result = 1的記錄數(shù)
select count(*),accurate_result from stage_poi group by accurate_result;
+----------+-----------------+
| count(*) | accurate_result |
+----------+-----------------+
| 1023 | -1 |
| 2114655 | 0 |
| 972815 | 1 |
+----------+-----------------+
我們看到accurate_result這個字段的區(qū)分度非常低,整個表只有-1,0,1三個值鸳碧,加上索引也無法鎖定特別少量的數(shù)據(jù)
再看一下sync_status字段的情況
select count(*),sync_status from stage_poi group by sync_status;
+----------+-------------+
| count(*) | sync_status |
+----------+-------------+
| 3080 | 0 |
| 3085413 | 3 |
+----------+-------------+
同樣的區(qū)分度也很低盾鳞,根據(jù)理論,也不適合建立索引
問題分析到這瞻离,好像得出了這個表無法優(yōu)化的結(jié)論腾仅,兩個列的區(qū)分度都很低,即便加上索引也只能適應(yīng)這種情況套利,很難做普遍性的優(yōu)化推励,比如當(dāng)sync_status 0鹤耍、3分布的很平均,那么鎖定記錄也是百萬級別的
4.找業(yè)務(wù)方去溝通验辞,看看使用場景稿黄。業(yè)務(wù)方是這么來使用這個SQL語句的,每隔五分鐘會掃描符合條件的數(shù)據(jù)跌造,處理完成后把sync_status這個字段變成1,五分鐘符合條件的記錄數(shù)并不會太多抛猖,1000個左右。了解了業(yè)務(wù)方的使用場景后鼻听,優(yōu)化這個SQL就變得簡單了,因為業(yè)務(wù)方保證了數(shù)據(jù)的不平衡联四,如果加上索引可以過濾掉絕大部分不需要的數(shù)據(jù)
5.根據(jù)建立索引規(guī)則撑碴,使用如下語句建立索引
alter table stage_poi add index idx_acc_status(accurate_result,sync_status);
6.觀察預(yù)期結(jié)果,發(fā)現(xiàn)只需要200ms,快了30多倍朝墩。
952 rows in set (0.20 sec)
我們再來回顧一下分析問題的過程醉拓,單表查詢相對來說比較好優(yōu)化,大部分時候只需要把where條件里面的字段依照規(guī)則加上索引就好收苏,如果只是這種“無腦”優(yōu)化的話亿卤,顯然一些區(qū)分度非常低的列,不應(yīng)該加索引的列也會被加上索引鹿霸,這樣會對插入排吴、更新性能造成嚴重的影響,同時也有可能影響其它的查詢語句懦鼠。
所以我們第4步調(diào)差SQL的使用場景非常關(guān)鍵钻哩,我們只有知道這個業(yè)務(wù)場景,才能更好地輔助我們更好的分析和優(yōu)化查詢語句肛冶。
慢查詢的案例就分析到這兒街氢,以上只是一些比較典型的案例。
我們在優(yōu)化過程中遇到過超過1000行睦袖,涉及到16個表join的“垃圾SQL”珊肃,也遇到過線上線下數(shù)據(jù)庫差異導(dǎo)致應(yīng)用直接被慢查詢拖死,也遇到過varchar等值比較沒有寫單引號馅笙,還遇到過笛卡爾積查詢直接把從庫搞死伦乔。再多的案例其實也只是一些經(jīng)驗的積累,如果我們熟悉查詢優(yōu)化器延蟹、索引的內(nèi)部原理评矩,那么分析這些案例就變得特別簡單了。
最后阱飘,我在插播一則公告:
如果你也想在IT行業(yè)拿高薪斥杜,可以參加我們的免費訓(xùn)練營虱颗,選擇最適合自己的方法學(xué)習(xí),技術(shù)大牛親授蔗喂,9個月后忘渔,進入名企拿高薪。我們的免費訓(xùn)練營內(nèi)容有:Java工程化缰儿、高性能及分布式畦粮、高性能、深入淺出乖阵。高架構(gòu)宣赔。性能調(diào)優(yōu)、Spring瞪浸,MyBatis儒将,Netty源碼分析和項目實戰(zhàn)等多個知識點。如果你想拿高薪的对蒲,想學(xué)習(xí)的钩蚊,想就業(yè)前景好的,想跟別人競爭能取得優(yōu)勢的蹈矮,想進阿里面試但擔(dān)心面試不過的砰逻,你都可以來,群號為:833145934
進群:可以領(lǐng)取免費的架構(gòu)師學(xué)習(xí)資料泛鸟。
進群:了解最新的IT互聯(lián)網(wǎng)動態(tài)
進群:了解最新的阿里蝠咆,京東招聘資訊
進群:獲取更多的面試資料
1、具有1-5工作經(jīng)驗的谈况,面對目前流行的技術(shù)不知從何下手勺美,需要突破技術(shù)瓶頸的可以加群。
2碑韵、在公司待久了赡茸,過得很安逸,但跳槽時面試碰壁祝闻。需要在短時間內(nèi)進修占卧、跳槽拿高薪的可以加群。
3联喘、如果沒有工作經(jīng)驗华蜒,但基礎(chǔ)非常扎實,對java工作機制豁遭,常用設(shè)計思想叭喜,常用java開發(fā)框架掌握熟練的,可以加群蓖谢。
4捂蕴、覺得自己很牛B譬涡,一般需求都能搞定。但是所學(xué)的知識點沒有系統(tǒng)化啥辨,很難在技術(shù)領(lǐng)域繼續(xù)突破的可以加群涡匀。
群號:程序員交流群 :833145934 備注好信息!
6.阿里Java高級架構(gòu)師直播講解知識點溉知,分享知識陨瘩,多年工作經(jīng)驗的梳理和總結(jié),帶著大家全面级乍、科學(xué)地建立自己的技術(shù)體系和技術(shù)認知舌劳!