亚洲免费在线-亚洲免费在线播放-亚洲免费在线观看-亚洲免费在线观看视频-亚洲免费在线看-亚洲免费在线视频

走向DBA[MSSQL篇] 從SQL語句的角度 提高數據庫

系統 2197 0
原文: 走向DBA[MSSQL篇] 從SQL語句的角度 提高數據庫的訪問性能

最近公司來一個非常虎的dba? 10幾年的經驗 這里就稱之為蔡老師吧?在征得我們蔡老同意的前提下? 我們來分享一下蔡老給我們帶來的寶貴財富 歡迎其他的dba來拍磚


?目錄

1、什么是執行計劃?執行計劃是依賴于什么信息。
2、 統一SQL語句的寫法減少解析開銷
3、 減少SQL語句的嵌套
4、 使用“臨時表”暫存中間結果
5、 OLTP系統SQL語句必須采用綁定變量
6、 傾斜字段的綁定變量窺測問題
7、 begin tran的事務要盡量地小。
8、 一些SQL查詢語句應加上nolock
9、加nolock后查詢經常發生頁分裂的表,容易產生跳讀或重復讀
10、聚集索引沒有建在表的順序字段上,該表容易發生頁分裂
11、使用復合索引提高多個where條件的查詢速度
13、使用like進行模糊查詢時應注意盡量不要使用前%
14、SQL Server 表連接的三種方式
15、Row_number 會導致表掃描,用臨時表分頁更好


?什么是執行計劃?執行計劃是依賴于什么信息。

執行計劃是數據庫根據SQL語句和相關表的統計信息作出的一個查詢方案,這個方案是由查詢優化器自動分析產生的,比如一條SQL語句如果用來從一個10萬條記錄的表中查1條記錄,那查詢優化器會選擇“索引查找”方式,如果該表進行了歸檔,當前只剩下5000條記錄了,那查詢優化器就會改變方案,采用“全表掃描”方式。

可見,執行計劃并不是固定的,它是“個性化的”。產生一個正確的“執行計劃”有兩點很重要:
SQL語句是否清晰地告訴查詢優化器它想干什么?
查詢優化器得到的數據庫統計信息是否是最新的、正確的?


統一SQL語句的寫法減少解析開銷

對于以下兩句 SQL 語句,程序員認為是相同的,數據庫查詢優化器 可能認為是不同的。

select?*?from?dual

Select?*?From?dual

其實就是大小寫不同,查詢分析器就認為是兩句不同的 SQL 語句,必須進行兩次解析。生成 2 個執行計劃。所以作為程序員,應該保證相同的查詢語句在任何地方都一致,多一個空格都不行!


減少SQL語句的嵌套

我經常看到,從數據庫中捕捉到的一條 SQL 語句打印出來有 2 A4 紙這么長。一般來說這么復雜的語句通常都是有問題的。我拿著這 2 頁長的 SQL 語句去請教原作者,結果他說時間太長,他一時也看不懂了。可想而知,連原作者都有可能看糊涂的 SQL 語句,數據庫也一樣會看糊涂。

一般,將一個 Select 語句的結果作為子集,然后從該子集中再進行查詢,這種一層嵌套語句還是比較常見的,但是根據經驗,超過 3 層嵌套,查詢優化器就很容易給出錯誤的執行計劃。因為它被繞暈了。像這種類似人工智能的東西,終究比人的分辨力要差些,如果人都看暈了,我可以保證數據庫也會暈的。

另外,執行計劃是可以被重用的,越簡單的 SQL 語句被重用的可能性越高。而復雜的 SQL 語句只要有一個字符發生變化就必須重新解析,然后再把這一大堆垃圾塞在內存里。可想而知,數據庫的效率會何等低下。


使用“臨時表”暫存中間結果

?簡化SQL語句的重要方法就是采用臨時表暫存中間結果,但是,臨時表的好處遠遠不止這些,將臨時結果暫存在臨時表,后面的查詢就在tempdb中了,這可以避免程序中多次掃描主表,也大大減少了程序執行中“共享鎖”阻塞“更新鎖”,減少了阻塞,提高了并發性能。


OLTP系統SQL語句必須采用綁定變量

select * from orderheader where changetime > ‘2010-10-20 00:00:01’
select * from orderheader where changetime > ‘2010-09-22 00:00:01’
以上兩句語句,查詢優化器認為是不同的SQL語句,需要解析兩次。如果采用綁定變量
select * from orderheader where changetime > @chgtime
@chgtime變量可以傳入任何值,這樣大量的類似查詢可以重用該執行計劃了,這可以大大降低數據庫解析SQL語句的負擔。一次解析,多次重用,是提高數據庫效率的原則。


?傾斜字段的綁定變量窺測問題

事物都存在兩面性,綁定變量對大多數OLTP處理是適用的,但是也有例外。比如在where條件中的字段是“傾斜字段”的時候。

“傾斜字段”指該列中的絕大多數的值都是相同的,比如一張人口調查表,其中“民族”這列,90%以上都是漢族。那么如果一個SQL語句要查詢30歲的漢族人口有多少,那“民族”這列必然要被放在where條件中。這個時候如果采用綁定變量@nation會存在很大問題。

試想如果@nation傳入的第一個值是“漢族”,那整個執行計劃必然會選擇表掃描。然后,第二個值傳入的是“布依族”,按理說“布依族”占的比例可能只有萬分之一,應該采用索引查找。但是,由于重用了第一次解析的“漢族”的那個執行計劃,那么第二次也將采用表掃描方式。這個問題就是著名的“綁定變量窺測”,建議對于“傾斜字段”不要采用綁定變量。


begin tran的事務要盡量地小

SQL Server中一句SQL語句默認就是一個事務,在該語句執行完成后也是默認commit的。其實,這就是begin tran的一個最小化的形式,好比在每句語句開頭隱含了一個begin tran,結束時隱含了一個commit。
有些情況下,我們需要顯式聲明begin tran,比如做“插、刪、改”操作需要同時修改幾個表,要求要么幾個表都修改成功,要么都不成功。begin tran 可以起到這樣的作用,它可以把若干SQL語句套在一起執行,最后再一起commit。好處是保證了數據的一致性,但任何事情都不是完美無缺的。Begin tran付出的代價是在提交之前,所有SQL語句鎖住的資源都不能釋放,直到commit掉。
可見,如果Begin tran套住的SQL語句太多,那數據庫的性能就糟糕了。在該大事務提交之前,必然會阻塞別的語句,造成block很多。
Begin tran使用的原則是,在保證數據一致性的前提下,begin tran 套住的SQL語句越少越好!有些情況下可以采用觸發器同步數據,不一定要用begin tran。


一些SQL查詢語句應加上nolock

SQL 語句中加 nolock 是提高 SQL?Server 并發性能的重要手段,在 oracle 中并不需要這樣做,因為 oracle 的結構更為合理,有 undo 表空間保存“數據前影”,該數據如果在修改中還未 commit ,那么你讀到的是它修改之前的副本,該副本放在 undo 表空間中。這樣, oracle 的讀、寫可以做到互不影響,這也是 oracle 廣受稱贊的地方。 SQL?Server? 的讀、寫是會相互阻塞的,為了提高并發性能,對于一些查詢,可以加上 nolock ,這樣讀的時候可以允許寫,但缺點是可能讀到未提交的臟數據。使用 nolock 3 條原則。

(1)?查詢的結果用于“插、刪、改”的不能加 nolock?

(2)?查詢的表屬于頻繁發生頁分裂的,慎用 nolock?

(3)?使用臨時表一樣可以保存“數據前影”,起到類似 oracle undo 表空間的功能,

能采用臨時表提高并發性能的,不要用 nolock?


加nolock后查詢經常發生頁分裂的表,容易產生跳讀或重復讀

nolock 后可以在“插、刪、改”的同時進行查詢,但是由于同時發生“插、刪、改”,在某些情況下,一旦該數據頁滿了,那么頁分裂不可避免,而此時 nolock 的查詢正在發生,比如在第 100 頁已經讀過的記錄,可能會因為頁分裂而分到第 101 頁,這有可能使得 nolock 查詢在讀 101 頁時重復讀到該條數據,產生“重復讀”。同理,如果在 100 頁上的數據還沒被讀到就分到 99 頁去了,那 nolock 查詢有可能會漏過該記錄,產生“跳讀”。

?上面提到的哥們,在加了 nolock 后一些操作出現報錯,估計有可能因為 nolock 查詢產生了重復讀, 2 條相同的記錄去插入別的表,當然會發生主鍵沖突。


聚集索引沒有建在表的順序字段上,該表容易發生頁分裂

比如訂單表,有訂單編號 orderid ,也有客戶編號 contactid ,那么聚集索引應該加在哪個字段上呢?對于該表,訂單編號是順序添加的,如果在 orderid 上加聚集索引,新增的行都是添加在末尾,這樣不容易經常產生頁分裂。然而,由于大多數查詢都是根據客戶編號來查的,因此,將聚集索引加在 contactid 上才有意義。而 contactid 對于訂單表而言,并非順序字段。

比如“張三”的“ contactid ”是 001 ,那么“張三”的訂單信息必須都放在這張表的第一個數據頁上,如果今天“張三”新下了一個訂單,那該訂單信息不能放在表的最后一頁,而是第一頁!如果第一頁放滿了呢?很抱歉,該表所有數據都要往后移動為這條記錄騰地方。

SQL?Server 的索引和 Oracle 的索引是不同的, SQL?Server 的聚集索引實際上是對表按照聚集索引字段的順序進行了排序,相當于 oracle 的索引組織表。 SQL?Server 的聚集索引就是表本身的一種組織形式,所以它的效率是非常高的。也正因為此,插入一條記錄,它的位置不是隨便放的,而是要按照順序放在該放的數據頁,如果那個數據頁沒有空間了,就引起了頁分裂。所以很顯然,聚集索引沒有建在表的順序字段上,該表容易發生頁分裂。

曾經碰到過一個情況,一位哥們的某張表重建索引后,插入的效率大幅下降了。估計情況大概是這樣的。該表的聚集索引可能沒有建在表的順序字段上,該表經常被歸檔,所以該表的數據是以一種稀疏狀態存在的。比如張三下過 20 張訂單,而最近 3 個月的訂單只有 5 張,歸檔策略是保留 3 個月數據,那么張三過去的 15 張訂單已經被歸檔,留下 15 個空位,可以在 insert 發生時重新被利用。在這種情況下由于有空位可以利用,就不會發生頁分裂。但是查詢性能會比較低,因為查詢時必須掃描那些沒有數據的空位。

重建聚集索引后情況改變了,因為重建聚集索引就是把表中的數據重新排列一遍,原來的空位沒有了,而頁的填充率又很高,插入數據經常要發生頁分裂,所以性能大幅下降。

對于聚集索引沒有建在順序字段上的表,是否要給與比較低的頁填充率?是否要避免重建聚集索引?是一個值得考慮的問題!


使用復合索引提高多個where條件的查詢速度

復合索引通常擁有比單一索引更好的選擇性。而且,它是特別針對某個 where 條件所設立的索引,它已經進行了排序,所以查詢速度比單索引更快。復合索引的引導字段必須采用“選擇性高”的字段。比如有 3 個字段:日期,性別,年齡。大家看,應該采用哪個字段作引導字段?顯然應該采用“日期”作為引導字段。日期是 3 個字段中選擇性最高的字段。

這里有一個例外,如果日期同時也是聚集索引的引導字段,可以不建復合索引,直接走聚集索引,效率也是比較高的。

不要把聚集索引建成“復合索引”,聚集索引越簡單越好,選擇性越高越好!聚集索引包括 2 個字段尚可容忍。但是超過 2 個字段,應該考慮建 1 個自增字段作為主鍵,聚集索引可以不做主鍵。


使用like進行模糊查詢時應注意盡量不要使用前%

有的時候會需要進行一些模糊查詢比如

?Select?*?from?contact?where?username?like?‘%yue%’

關鍵詞 %yue% ,由于 yue 前面用到了“ % ”,因此該查詢必然走全表掃描,除非必要,否則不要在關鍵詞前加 %


SQL Server 表連接的三種方式

?? (1)?Merge?Join

????(2)?Nested?Loop?Join?

????(3)?Hash?Join?

SQL?Server?2000 只有一種 join 方式—— Nested?Loop?Join ,如果 A 結果集較小,那就默認作為外表, A 中每條記錄都要去 B 中掃描一遍,實際掃過的行數相當于 A 結果集行數 x?B 結果集行數。所以如果兩個結果集都很大,那 Join 的結果很糟糕。

SQL?Server?2005 新增了 Merge?Join ,如果 A 表和 B 表的連接字段正好是聚集索引所在字段,那么表的順序已經排好,只要兩邊拼上去就行了,這種 join 的開銷相當于 A 表的結果集行數加上 B 表的結果集行數,一個是加,一個是乘,可見 merge?join? 的效果要比 Nested?Loop?Join 好多了。

如果連接的字段上沒有索引,那 SQL2000 的效率是相當低的,而 SQL2005 提供了 Hash?join ,相當于臨時給 A B 表的結果集加上索引,因此 SQL2005 的效率比 SQL2000 有很大提高,我認為,這是一個重要的原因。

總結一下,在表連接時要注意以下幾點:

(1)?連接字段盡量選擇聚集索引所在的字段

(2)?仔細考慮 where 條件,盡量減小 A B 表的結果集

(3)?如果很多 join 的連接字段都缺少索引,而你還在用 SQL2000 ,干緊升級吧 .


Row_number 會導致表掃描,用其他方式例如top方案并且將大表存進臨時表會更好

ROW_Number分頁的測試結果:
使用ROW_Number來分頁:CPU 時間= 317265 毫秒,占用時間= 423090 毫秒
使用top+臨時表來分頁:CPU 時間= 1266 毫秒,占用時間= 6705 毫秒

ROW_Number實現是基于order by的,排序對查詢的影響顯而易見。


其他

諸如 有的寫法會限制使用索引?

Select?*?from?tablename?where?chgdate?+7?<?sysdate

Select?*?from?tablename?where?chgdate?<?sysdate?-7

前者會抑制chgdate列上的索引 后者不會


本篇先到此?歡迎有愛的同學拍磚

走向DBA[MSSQL篇] 從SQL語句的角度 提高數據庫的訪問性能


更多文章、技術交流、商務合作、聯系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯系: 360901061

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對您有幫助就好】

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長會非常 感謝您的哦!!!

發表我的評論
最新評論 總共0條評論
主站蜘蛛池模板: 中文字幕亚洲综合久久男男 | 亚洲一区欧美二区 | 99视频全部免费精品全部四虎 | 中日韩欧美中文字幕毛片 | 午夜欧美 | 国产福利在线观看视频 | 色综合久久综合欧美综合网 | 亚洲一区在线日韩在线深爱 | 精品国产品香蕉在线观看75 | 日本3p视频在线看高清 | 伊人第一页 | 婷婷射| 国产91精品系列在线观看 | 免费国产视频 | 国内精品七七久久影院 | 4虎影院永久地址www | 天天干天天玩 | 黄色.www| 日韩成人精品视频 | 国产成人综合亚洲欧美在 | 99精品久久99久久久久久 | 久草在线视频免费播放 | 国产1769一七六九视频在线 | 四虎影院精品 | 国产精自产拍久久久久久蜜 | 四虎永久在线观看免费网站网址 | 久青草视频在线播放 | 精品国产免费久久久久久婷婷 | 国产成人18黄禁网站免费观看 | 亚洲日韩精品欧美一区二区 | 99精品wwxx在线观看 | 欧美视频久久久 | 日日摸夜夜爽夜夜爽出水 | 久久国产欧美另类久久久 | 四虎精品成人免费永久 | 欧美亚洲h在线一区二区 | 九九欧美| 色视频网 | 男人草女人的视频 | 国产精品男人的天堂 | 日韩在线免费视频 |