|
【問(wèn)題描述】
我們可能會(huì)在數(shù)據(jù)庫(kù)的錯(cuò)誤日志里,發(fā)現(xiàn)這么一條信息:
A time-out occurred while waiting for buffer latch -- type 4, bp 000000097BFDEDC0, page 1:19239, stat 0xc00009, database id: 5, allocation unit Id: 72057615247867904, task 0x0000000005E594C8 : 0, waittime 300, flags 0x1018, owning task 0x0000000000169DC8. Not continuing to wait.
我們的問(wèn)題是,這個(gè)錯(cuò)誤到底是什么含義,在什么情況下會(huì)報(bào)上面的錯(cuò)誤,以及如何解決?
【背景介紹】
Latch是SQL Server內(nèi)部用來(lái)同步資源訪問(wèn)的一個(gè)數(shù)據(jù)結(jié)構(gòu)。和操作系統(tǒng)的Critical Section或ReaderWriterLock類(lèi)似。Latch保護(hù)了那些想保護(hù)的資源,使得訪問(wèn)同步有序。比方說(shuō),當(dāng)某個(gè)線程獲得某個(gè)資源的Latch的獨(dú)占使用權(quán)時(shí)候,別的線程如果也需要訪問(wèn)這個(gè)Latch,則它必須等待。
從大的方面來(lái)講,有兩種Latch,一種叫Buffer Latch,另外一種叫I/O Latch。
我們先來(lái)簡(jiǎn)短介紹一下I/O Latch。當(dāng)SQL Server從硬盤(pán)上讀取一個(gè)頁(yè)時(shí),會(huì)先在內(nèi)存預(yù)留該頁(yè)的空間。并且在該預(yù)留空間的某一個(gè)位BUF_IO設(shè)為1。如果數(shù)據(jù)從硬盤(pán)讀寫(xiě)完成,則該位設(shè)為0。從硬盤(pán)讀取頁(yè)的期間,其他也需要訪問(wèn)該頁(yè)的線程當(dāng)然要等待,等待類(lèi)型為PAGEIOLATCH_SH,直到讀寫(xiě)完成,BUF_IO被設(shè)為0為止。因此,如果我們看到大量PAGEIOLATCH_SH等待,則基本可以斷定問(wèn)題是出在磁盤(pán)性能上面。
另外一種Latch則稱(chēng)為Buffer Latch,用來(lái)保護(hù)內(nèi)存里的數(shù)據(jù)結(jié)構(gòu),如Index, Data Pages, B樹(shù)中的Non-Leaf頁(yè)。當(dāng)進(jìn)程需要讀取一個(gè)內(nèi)存里的數(shù)據(jù)頁(yè)時(shí),該進(jìn)程要先獲取該數(shù)據(jù)頁(yè)上的Buffer Latch。有各種類(lèi)型的Latch,包括獨(dú)占Latch(PAGELATCH_EX)和共享Latch(PAGELATCH_SH)。
下面來(lái)演示,為什么我們需要Latch。如下圖所示,我們?cè)陧?yè)面100上,已經(jīng)存放了兩條記錄。
如果沒(méi)有Latch鎖的話,某進(jìn)程在頁(yè)面100上,插入如下數(shù)據(jù):INSERT VALUES(3, 300),其結(jié)果如下:
這時(shí),另外一個(gè)進(jìn)程要在頁(yè)面100上,插入如下數(shù)據(jù): INSERT VALUES(4, 400), 因?yàn)闆](méi)有Latch鎖,所以會(huì)覆蓋之前的數(shù)據(jù)。導(dǎo)致數(shù)據(jù)插入出問(wèn)題。
正確的做法是,我們要在第一個(gè)線程進(jìn)行操作時(shí),加獨(dú)占Latch鎖。第二個(gè)線程必須要等待,直到第一個(gè)線程操作完成。如下圖所示:
開(kāi)始第一條記錄插入,隨后修改m_freedata(值為141)以及Row的指針(值為126),在此期間,第二條插入語(yǔ)句處于等待Latch狀態(tài)。第一條記錄插入完成后,釋放獨(dú)占Latch鎖。
第二條記錄開(kāi)始插入,插入期間也會(huì)加獨(dú)占Latch鎖,以防止其他進(jìn)程修改或讀取頁(yè)。完成后,也隨即釋放Latch鎖。最后結(jié)果如圖所示。由于有Latch鎖,所以數(shù)據(jù)的插入可以有序的進(jìn)行。
【LATCH申請(qǐng)模式】
Latch在申請(qǐng)的時(shí)候有以下幾種模式,
- KP – Keep Latch 保證引用的結(jié)構(gòu)不能被破壞
- SH – Shared Latch 讀數(shù)據(jù)頁(yè)的時(shí)候需要
- UP – Update Latch 更改數(shù)據(jù)頁(yè)的時(shí)候需要
- EX – Exclusive Latch 獨(dú)占模式,主要用于寫(xiě)數(shù)據(jù)頁(yè)的時(shí)候需要
- DT – Destroy Latch 在破壞引用的數(shù)據(jù)結(jié)構(gòu)時(shí)所需要
下表顯示各種Latch申請(qǐng)的兼容模式:
Y表明是兼容的,如果兩個(gè)線程都去讀某頁(yè),則他們都會(huì)去申請(qǐng)SH鎖,因?yàn)镾H鎖是兼容的,則兩個(gè)線程都不會(huì)互相妨礙。而N表明是不兼容的,必須要等待。直到前面一個(gè)Latch被釋放為止。
【LATCH等待類(lèi)型】
Latch的等待主要有三種。
- Buffer (BUF) Latch 用來(lái)保護(hù)索引或數(shù)據(jù)頁(yè),也包括PFS, GAM, SGAM和IAM數(shù)據(jù)頁(yè),等待類(lèi)型是PAGELATCH_*模式。
- Non-buffer (Non-BUF) Latch 除了上述數(shù)據(jù)結(jié)構(gòu)以外的其他內(nèi)存結(jié)構(gòu),等待類(lèi)型是LATCH_*模式。
- IO Latch 保護(hù)數(shù)據(jù)從磁盤(pán)到頁(yè)面的讀寫(xiě)過(guò)程,等待類(lèi)型是PAGEIOLATCH_*模式。
我們可以查詢(xún)下面的語(yǔ)句了解一下具體的等待種類(lèi):
SELECT * FROM sys.dm_os_wait_stats WHERE wait_type like '%Latch%'
it知識(shí)庫(kù):Buffer Latch Timeout的解析,轉(zhuǎn)載需保留來(lái)源!
鄭重聲明:本文版權(quán)歸原作者所有,轉(zhuǎn)載文章僅為傳播更多信息之目的,如作者信息標(biāo)記有誤,請(qǐng)第一時(shí)間聯(lián)系我們修改或刪除,多謝。