记一次T-SQL查询优化 索引的重要性
概述
在一次調(diào)優(yōu)一個(gè)項(xiàng)目組件的性能問(wèn)題時(shí),發(fā)現(xiàn)SQL的設(shè)計(jì)真的是非常的重要,所以寫一篇博文來(lái)記錄總結(jié)一下。
環(huán)境介紹
這個(gè)項(xiàng)目組件是一個(gè)Window服務(wù),內(nèi)部在使用輪循機(jī)會(huì)在處理一個(gè)事件表中的事件,將其轉(zhuǎn)換在對(duì)應(yīng)的任務(wù)。性能問(wèn)題在于,統(tǒng)計(jì)下來(lái),這個(gè)服務(wù)一秒的時(shí)間內(nèi)只能處理完成12條左右。這個(gè)性能是非常的差。
我使用的SQL版本是SQL 2012,機(jī)器是CPU I7-2670,內(nèi)存16G,SSD硬盤。
在這個(gè)數(shù)據(jù)庫(kù)中有一個(gè)表的數(shù)據(jù)量大概30萬(wàn)條數(shù)據(jù),并不是很多, 事先沒(méi)有建立任何索引,只有一個(gè)主鍵的索引。
?那么在這其中有一條非常簡(jiǎn)單的查詢語(yǔ)句:
SELECT TOP 1 * FROM SMS_SHORTNO_ASSIGN WHERE APP_CODE = 'SMSNotice'AND IS_DYNAMIC_ASSIGN = 'N'AND SMS_TYPE_CODE = 'Mas'有數(shù)據(jù)和無(wú)數(shù)據(jù)的性能對(duì)比
?在上面的查詢中,IS_DYNAMIC_ASSIGN = 'N'是查詢不到任何數(shù)據(jù)的,IS_DYNAMIC_ASSIGN = 'Y'是有數(shù)據(jù)的,對(duì)比一下,在沒(méi)有任何數(shù)據(jù)的情況下,查詢是非常的慢,但是有數(shù)據(jù)的情況下,就不同了。
首先來(lái)看一下這個(gè)SQL的查詢計(jì)劃是什么樣子:
下面是更清晰的執(zhí)行查詢計(jì)劃:
?可以看到,在沒(méi)有索引的情況下,會(huì)執(zhí)行表掃描。
?來(lái)看一下各自的執(zhí)行時(shí)間:
| 可以查詢到數(shù)據(jù): | 不能查詢到數(shù)據(jù): |
可以看到,在沒(méi)有查詢到數(shù)據(jù)的情況下,總共需要耗時(shí)89ms. 不要覺(jué)得89ms才只有0.1s都不到,但是想一想之前上面說(shuō)的1S鐘才處理12條記錄,就可以想像到和這個(gè)89ms有相當(dāng)大的關(guān)系,如果只執(zhí)行這一條SQL,那么1S鐘也只能執(zhí)行12條左右。
在這種情況下,我們來(lái)優(yōu)化一下這條SQL語(yǔ)句。首先這句SQL本身已經(jīng)是最簡(jiǎn)單的,不能再簡(jiǎn)化,那么只有在索引上下功夫。
聚集索引和非聚集索引
兩者之間有什么區(qū)別呢?大家可以參考一篇博客圓另一博主的博文?聚集索引和非聚集索引(整理)。
首先我們按照我們一般沒(méi)有深入研究過(guò)索引童鞋們的思路,就是把WHERE后面條件的字段加起來(lái)建一個(gè)索引。
根據(jù)WHERE 條件字段創(chuàng)建非聚集索引
?
創(chuàng)建后好,我們來(lái)看看上面的語(yǔ)句的查詢計(jì)劃:
咦,為什么還是使用了表掃描呢,而不用使用索引呢? ?
在這里貼上一篇博文?Select * 一定不走索引是否正確??這篇博文分析了SELECT * 和各種索引的關(guān)系,但是這個(gè)博文里面分析的和我得出的結(jié)論不一樣,我也在作者的評(píng)論留言了,同時(shí)我找到別一篇博文?SELECT * 的真相: 索引覆蓋(index coverage)?來(lái)解釋我現(xiàn)在的現(xiàn)象。因?yàn)槲也辉趺囱芯縎QL,所以我不清楚到底是什么原因,望有知者,可以告知一下。關(guān)于索引覆蓋也可以參考這篇博文?SQL Server 查詢性能優(yōu)化——覆蓋索引(二)。
那么我現(xiàn)在將SELECT * 改成 SELECT 字段后,索引才真正的應(yīng)用了。
可以看到如果SELECT中的字段包含在索引中,將可以利用到索引。
但是這樣的話,改變了我原來(lái)程序的用意,這是不能接受的。那有什么別的辦法可以解決嗎?這個(gè)時(shí)候我想到了聚集索引。
創(chuàng)建聚集索引
默認(rèn)情況下,在使用表設(shè)計(jì)器的創(chuàng)建表的時(shí)候,會(huì)默認(rèn)創(chuàng)建一個(gè)主鍵的聚集索引。根據(jù)主鍵創(chuàng)建聚集索引,并不一定是最優(yōu)的選擇。關(guān)于聚集索引 可以參考下?索引優(yōu)化(2)聚集索引?。我觀察了一下我的表結(jié)構(gòu),我根據(jù)可能使用的列頻率最高的兩個(gè)字段上建立了聚集索引,這兩個(gè)字段包含在上述語(yǔ)句的WHERE語(yǔ)句中。這兩個(gè)字段并不是主鍵。
創(chuàng)建好后,我們?cè)賮?lái)看一下查詢計(jì)劃和查詢的時(shí)間:
查詢時(shí)間:
可以看到,查詢速度已經(jīng)0ms了,非常的快速了。到這里面,其實(shí)問(wèn)題關(guān)于這一條SQL優(yōu)化應(yīng)該是已經(jīng)結(jié)束了。
聚集索引很重要并且一個(gè)表只能建一條聚集索引,不能根據(jù)某一條SQL的WHERE來(lái)建立,而是要考慮到各種不同的WHERE條件才確定這樣建立聚集索引是不是最優(yōu)的,我根據(jù)這兩個(gè)字段建立好聚集索引后,我使用別的WHERE來(lái)查詢,速度也是非常的快,所以最后才確認(rèn)使用這兩個(gè)字段建聚集索引。
當(dāng)然我的項(xiàng)目中還是有很多的語(yǔ)句可以優(yōu)化,以及程序C#代碼本身也可以優(yōu)化,經(jīng)過(guò)我的優(yōu)化后,處理速度可以達(dá)到1秒處理130條左右了。
題外篇
?===========================題外篇=======================
在學(xué)習(xí)這個(gè)優(yōu)化過(guò)程中,還有一些別的心得和疑問(wèn)的,也在此記錄一下。
根據(jù)上面我創(chuàng)建一條聚集索引就解決了問(wèn)題,并且也建立了非聚集索引,非聚集索引反而沒(méi)有用上,那么是不是說(shuō)非聚集索引就沒(méi)有用呢?并不是這樣的,非聚集索引是SQL優(yōu)化的很大的一部分。
之前上面說(shuō)道SELECT中只包含索引列的情況下會(huì)使用到非聚焦索引。那么下面再說(shuō)一個(gè)例子來(lái)說(shuō)明非聚集索引的用途。
我們們將之前建立的三個(gè)字段的非聚集索引刪除,使用統(tǒng)計(jì)函數(shù)來(lái)統(tǒng)計(jì)一下符合條件的條數(shù):
查詢時(shí)間:
可以看到耗時(shí)還是很久28ms的. 大家不用關(guān)注COUNT(*)可以使用COUNT(1)或Count(主鍵),這個(gè)討論網(wǎng)上也很多,我自己切換三種寫法也沒(méi)有什么本質(zhì)的不同。
這時(shí),我們將之前刪除的非聚集索引加回來(lái),再來(lái)查看查詢計(jì)劃和時(shí)間:
可以看到查詢計(jì)劃中,這個(gè)時(shí)候優(yōu)先使用了非聚集索引,并且統(tǒng)計(jì)的速度是要快過(guò)使用聚集索引的。?
疑問(wèn)(求答疑)
在別一個(gè)SQL中,也是很簡(jiǎn)單的SQL,使用了LEFT JOIN后,會(huì)導(dǎo)致查詢的性能不高,在這種情況下,該如何來(lái)優(yōu)化呢,我使用了not Exists,子查詢來(lái)各種替換并不能減少這個(gè)SQL的查詢時(shí)間。
業(yè)務(wù)場(chǎng)景是這樣的,SQL還是和之前的一樣,SMS_SHORTNO_LOCKED表里面會(huì)存入SMS_SHORTNO_ASSIGN表里面的記錄,鎖定的時(shí)候會(huì)增加一條,解鎖的時(shí)候會(huì)將這條記錄刪除,所以在此使用LEFT JOIN來(lái)取出一條沒(méi)有鎖定的記錄。
下面是它的查詢語(yǔ)句和查詢計(jì)劃和響應(yīng)時(shí)間:
這個(gè)26ms最主要是在SHORTNO_LOCKED IS NULL這條判斷上,如果不是使用IS NULL,而是使用 SHORTNO_LOCKED = 1或=0這種方法來(lái)判斷的話,查詢是非常的快。
那么在此,請(qǐng)問(wèn)一下大家,相信很多人都使用LEFT JOIN,然后使用IS NULL來(lái)判斷別一個(gè)表沒(méi)有的數(shù)據(jù)。但是這樣的性能并不是很高,有什么辦法可以解決LEFT JOIN的問(wèn)題,或者可以改成別的寫法,我嘗試了很多種都沒(méi)有改善。
所以我想難道以后在設(shè)計(jì)表的時(shí)候,是不是盡量使用 INNER JOIN ,然后根據(jù)某一個(gè)字段判斷特定的值,這樣的話,這個(gè)字段可以使用索引來(lái)優(yōu)化,像上面就因?yàn)镮S NULL的問(wèn)題是沒(méi)辦法使用索引的。
希望有高人指點(diǎn),謝謝。
總結(jié)
以上是生活随笔為你收集整理的记一次T-SQL查询优化 索引的重要性的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Android 中input event
- 下一篇: Centos 升级MySQL版本或者Yu