SQL Server通過鎖定資源來保證數(shù)據(jù)庫(kù)的一致性。SQL Server中的鎖不會(huì)對(duì)行、頁(yè)、表或索引等資源有實(shí)際影響,它更像一個(gè)預(yù)訂系統(tǒng),所有任務(wù)在數(shù)據(jù)庫(kù)內(nèi)預(yù)訂某些資源時(shí)都遵守它。過多的鎖或長(zhǎng)時(shí)間持有的鎖會(huì)導(dǎo)致阻塞和其他問題,但鎖本身也可能產(chǎn)生一些問題。
1 解決鎖內(nèi)存問題
為了確定SQL Server中鎖使用的內(nèi)存量,可以監(jiān)視SQL Server中的“鎖內(nèi)存(KB)”計(jì)數(shù)器和系統(tǒng)監(jiān)視器(Perfmon)中的“內(nèi)存管理”對(duì)象。通過設(shè)置sp_configure中的鎖選項(xiàng),可以修改SQL Server中鎖的內(nèi)存配額。使用SQLServer:Locks計(jì)數(shù)器,可以了解更多關(guān)于鎖行為的細(xì)節(jié)。
如果系統(tǒng)中的鎖內(nèi)存消耗完了,SQL Server不能分配更多的鎖內(nèi)存,session會(huì)收到消息1204:
The instance of the SQL Server Database Engine cannot obtain a LOCK resource at this time. Rerun your statement when there are fewer active users. Ask the database administrator to check the lock and memory configuration for this instance, or to check for long-running transactions. |
這個(gè)消息說得很清楚:需要增加鎖的內(nèi)存配額,或者減少系統(tǒng)中鎖的數(shù)量。
如果鎖占用很大的內(nèi)存,應(yīng)該首先嘗試找出造成這么多鎖的根本原因。例如,可能是SQL Server的鎖升級(jí)不充分。如果是這樣,就需要修改鎖的配置。一旦修改了鎖動(dòng)態(tài)配置,就影響了鎖升級(jí)的行為,由此可能造成意外的影響。
如果數(shù)據(jù)庫(kù)不需要任何寫訪問,建議將其設(shè)置為只讀的。這會(huì)降低系統(tǒng)中產(chǎn)生的鎖的數(shù)量。在一個(gè)只讀的數(shù)據(jù)庫(kù)中,SQL Server仍會(huì)發(fā)行數(shù)據(jù)庫(kù)的共享鎖和讀表的意向共享鎖,但行鎖、頁(yè)鎖及SERIALIZABLE隔離級(jí)別的行鎖,都不會(huì)被發(fā)行。例如,對(duì)于只在夜間更新的報(bào)表數(shù)據(jù)庫(kù),用戶可以將在白天對(duì)數(shù)據(jù)庫(kù)的查詢?cè)O(shè)置為只讀的。這樣做對(duì)鎖內(nèi)存的影響會(huì)降低,這也是SQL Server的鎖管理器必須做的。還可以在同一臺(tái)服務(wù)器上對(duì)只讀數(shù)據(jù)庫(kù)創(chuàng)建數(shù)據(jù)庫(kù)快照,SQL Server不會(huì)在數(shù)據(jù)庫(kù)快照上發(fā)行共享鎖。
為了減少鎖內(nèi)存,同樣建議將讀操作與寫操作分開。一種方法是把報(bào)表從一個(gè)OLTP系統(tǒng)中分開,通過創(chuàng)建報(bào)表服務(wù)器和使用事務(wù)復(fù)制或SQL Server集成服務(wù)(SSIS)來為另一個(gè)用戶查詢讀操作的服務(wù)器獲取數(shù)據(jù)。這會(huì)去掉OLTP主服務(wù)器的共享鎖。如果數(shù)據(jù)庫(kù)服務(wù)器能夠支持這種方法,可以考慮用一個(gè)數(shù)據(jù)庫(kù)快照來定期卸載讀操作。在本章后面我們還可以看到使用一種基于行版本的快照隔離級(jí)別來減少讀數(shù)據(jù)查詢產(chǎn)生的鎖。
2 鎖超時(shí)
默認(rèn)狀態(tài)下,一個(gè)被阻塞的查詢會(huì)無限地等待一個(gè)未被滿足的鎖的請(qǐng)求。通過使用LOCK_TIMEOUT設(shè)置,可以指定一個(gè)session鎖等待的時(shí)間。當(dāng)鎖超時(shí)發(fā)生時(shí),session會(huì)收到消息1222:
Lock request time out period exceeded.
使用LOCK_TIMEOUT給事務(wù)帶來了問題,因?yàn)殄e(cuò)誤1222發(fā)生后,SQL Server只是退出當(dāng)前程序語(yǔ)句,而并沒有中止事務(wù)。因此需要在Transact-SQL代碼中使用TRY/CATCH模塊來捕獲1222錯(cuò)誤。若發(fā)生了超時(shí),可能需要回滾事務(wù)。若要了解更多內(nèi)容,可以參考Inside SQL Server 2005:The Storage Engine(《Microsoft SQL Server 2005:存儲(chǔ)引擎》,電子工業(yè)出版社,2007)第8章的“設(shè)置鎖超時(shí)”。