擦亮自己的眼睛去看SQL Server之簡(jiǎn)單Insert
本來(lái)是打算先寫(xiě)SQLServer歷史的,不過(guò)感覺(jué)寫(xiě)那部分內(nèi)容比較難還需要多查些資料。于是調(diào)整了下順序?qū)懴潞?jiǎn)單的Insert語(yǔ)句。數(shù)據(jù)庫(kù)結(jié)構(gòu)還是采用上一篇的結(jié)構(gòu)。具體查看上一篇文章擦亮自己的眼睛去看SQL Server之簡(jiǎn)單Select。今天討論的語(yǔ)句也比較簡(jiǎn)單,Insert語(yǔ)句。
一、Insert腳本
- insert into Test([Name]) values('xiaojun')
沒(méi)什么好說(shuō)的,因?yàn)橄雽?xiě)這樣的語(yǔ)句太簡(jiǎn)單。
二、 語(yǔ)句分析
這條語(yǔ)句到底發(fā)生了什么呢?假設(shè)讀者已經(jīng)知道了SQLServer整體架構(gòu)或者已經(jīng)閱讀過(guò)這個(gè)系列第一篇文章。當(dāng)這條語(yǔ)句被可靠的傳遞到關(guān)系引擎中后已經(jīng)生成執(zhí)行計(jì)劃,并且開(kāi)始被調(diào)度執(zhí)行。接下來(lái)就發(fā)生了:
寫(xiě)事務(wù)日志:數(shù)據(jù)修改事務(wù)中唯一一個(gè)總是需要寫(xiě)入磁盤(pán)的操作。并不是修改查詢語(yǔ)句的清單,而是修改操作發(fā)生之后數(shù)據(jù)頁(yè)面的具體變化。是由日志管理器完成??吹綄?xiě)入磁盤(pán),我們應(yīng)該立刻聯(lián)想到性能問(wèn)題,因?yàn)檫@個(gè)操作是總是寫(xiě)入磁盤(pán)。如果一條語(yǔ)句的操作的數(shù)據(jù)很大的話,這個(gè)耗時(shí)是十分可怕的。舉個(gè)例子:如果想知道這個(gè)差距,你可以在百萬(wàn)或者千萬(wàn)的表中執(zhí)行以下兩條語(yǔ)句體會(huì)以下:truncate table Test以及delete from Test。當(dāng)然嚴(yán)謹(jǐn)?shù)耐瑢W(xué)會(huì)說(shuō)truncate是針對(duì)區(qū)操作,delete是針對(duì)頁(yè)操作,truncate的鎖消耗也比delete的鎖消耗少。這些是會(huì)導(dǎo)致truncate比delete快的原因。但是這些原因不是主要原因,主要原因就是這里說(shuō)的寫(xiě)事務(wù)日志,delete是每次刪除一行,并在事務(wù)日志中為所刪除的每行記錄一項(xiàng),而truncate是通過(guò)釋放存儲(chǔ)表數(shù)據(jù)所用的數(shù)據(jù)頁(yè)來(lái)刪除數(shù)據(jù),并且只在事務(wù)日志中記錄頁(yè)的釋放。既然事務(wù)日志會(huì)影響性能,為什么還記錄呢?主要解決保護(hù)數(shù)據(jù)以及數(shù)據(jù)一致性的問(wèn)題。
接收寫(xiě)請(qǐng)求:一旦訪問(wèn)方法接收到寫(xiě)事務(wù)日志成功的確認(rèn)信息,就會(huì)接收寫(xiě)請(qǐng)求,將寫(xiě)請(qǐng)求發(fā)送緩存區(qū)管理器。注意了,這里是把請(qǐng)求交給緩存區(qū)管理器,緩存區(qū)管理器只是操作緩存跟物理文件沒(méi)有任何關(guān)系。這里強(qiáng)調(diào)的目的是,如果沒(méi)有理解這里說(shuō)的原理的話。你可能會(huì)為自己做了大量的插入操作,而數(shù)據(jù)文件的大小沒(méi)有任何變化而感到匪夷所思。訪問(wèn)方法表面上起了請(qǐng)求傳遞的作用,其實(shí)它很智能有一些比較復(fù)雜的算法來(lái)預(yù)測(cè)執(zhí)行情況。
插入緩沖池:緩沖區(qū)管理器在內(nèi)存中插入數(shù)據(jù),插入成功后將確認(rèn)結(jié)果發(fā)送給訪問(wèn)方法,最終確認(rèn)結(jié)果到達(dá)客戶端。
寫(xiě)入數(shù)據(jù)文件:這個(gè)步驟可以由兩個(gè)組件任何一個(gè)完成。惰性寫(xiě)入器線程定期檢查SQLServer空閑緩沖列表的大小,當(dāng)這個(gè)值過(guò)低的時(shí)候,惰性寫(xiě)入器會(huì)掃描整個(gè)數(shù)據(jù)緩存,將所有一段時(shí)間沒(méi)被使用的頁(yè)面老化。如果找到一段時(shí)間沒(méi)有被使用的臟頁(yè),惰性寫(xiě)入器則將其寫(xiě)入磁盤(pán)并且刪除,然后將這個(gè)頁(yè)面的內(nèi)存空間標(biāo)記為空閑空間。惰性寫(xiě)入器還會(huì)監(jiān)測(cè)服務(wù)器上的空閑物理內(nèi)存,如果內(nèi)存很少它會(huì)將SQLServer的空閑緩沖列表釋放給windows,在SQLServer負(fù)載很重時(shí),它還會(huì)在服務(wù)器有空閑物理內(nèi)存且已給SQLServer分配的內(nèi)存還沒(méi)有達(dá)到我們配置的最大服務(wù)器內(nèi)存(max server memory)時(shí)增加SQLServer的空閑緩沖列表以適應(yīng)負(fù)載。檢查點(diǎn)是檢查點(diǎn)線程創(chuàng)建的一個(gè)時(shí)間點(diǎn),將保證臟頁(yè)都寫(xiě)入磁盤(pán),并且在頁(yè)面頭將緩存中的這個(gè)頁(yè)面標(biāo)記為干凈的頁(yè)面注意檢查點(diǎn)是不刪除臟頁(yè)的。至于檢查點(diǎn)的執(zhí)行時(shí)間是要分幾種情況的:如果你配置了recovery interval(min),就以這個(gè)為準(zhǔn)。如果沒(méi)有配置,并且這上一次檢查點(diǎn)結(jié)束后寫(xiě)入的事務(wù)日志數(shù)據(jù)超過(guò)10MB,則大約每分鐘啟動(dòng)一致。還比如,我們?nèi)藶閳?zhí)行checkpoint執(zhí)行,或者執(zhí)行備份重啟命令都會(huì)觸發(fā)檢查點(diǎn)。拋開(kāi)我們?nèi)藶椴僮?,這個(gè)具體時(shí)間確實(shí)無(wú)法確定,SQLServer有內(nèi)部啟發(fā)算法控制這個(gè)值。不過(guò)我們可以開(kāi)啟一個(gè)跟蹤標(biāo)志3502能查看。這個(gè)跟蹤標(biāo)志在錯(cuò)誤日志中記錄了檢查點(diǎn)的開(kāi)始與結(jié)束為止。sql語(yǔ)句為:dbcc traceon(3502) 。
三、結(jié)尾
今天主要就是介紹了插入語(yǔ)句的執(zhí)行過(guò)程,內(nèi)容不多。你從這個(gè)過(guò)程中你會(huì)發(fā)現(xiàn)SQLServer真的很智能。比如這里的預(yù)寫(xiě)日志來(lái)保護(hù)數(shù)據(jù),延遲將數(shù)據(jù)寫(xiě)入磁盤(pán)、預(yù)測(cè)SQL執(zhí)行情況、監(jiān)控負(fù)載調(diào)整內(nèi)存等等。設(shè)計(jì)的都是那么巧妙,大家可以想想如果我們?cè)谠O(shè)計(jì)自己的軟件時(shí)是否可以參考和借鑒呢?
今天分析就到此結(jié)束,文中如有描述不當(dāng)?shù)牡胤?,歡迎指出。共同進(jìn)步才是硬道理。
原文鏈接:http://www.cnblogs.com/yueyue_jwfm/archive/2011/06/30/2095006.html
【編輯推薦】