2016-12-03 62 views
0

假設我們有一個表Users只有3列。SQL Server:鎖和獨佔類型的選擇

UserId - primary key clustered int 
Username - nvarchar(50) 
CityId - int, which has a non-unique non-clustered index (mouthful). 

如果我做了選擇,並帶有專用鎖和一個標籤的begin tran:

declare @CityId int = 10 

begin tran 
    SELECT * 
    FROM [dbo].Users WITH (XLOCK,INDEX (MyCityIndex)) 
    WHERE CityId = @CityId 

而且注意到我不提交或回滾事務。

然後在另一個(YES另一個選項卡ELSE SQL會做一些優化和慣於工作)選項卡上,我跑:

declare @CityId int = 10 

begin tran 
    SELECT * 
    FROM [dbo].Users WITH (XLOCK, INDEX (MyCityIndex)) 
    WHERE CityId = @CityId 

,如果我用同樣的CityId(10)很明顯我們阻塞。但我猜如果我使用CityId = 11會發生什麼?

declare @CityId int = 11 

它會工作。

但它會工作,如果我做索引提示搜索,否則它將無法正常工作,即使我把CityId = 11,然後它會阻止和等待其他tran會失敗。

這是怎麼回事?

當它使用獨佔鎖讀取數據時,執行全表掃描,然後一旦它遇到記錄被鎖定,就會被阻止?真?

如果索引得到碎片並且碰到實際上被阻塞的不包含其'id'的行,會發生什麼情況。那塊還會嗎?

在進行測試時,請使用2個不同的選項卡進行選擇,因爲這裏有SQL Server優化。

http://sqlblog.com/blogs/louis_davidson/archive/2006/12/13/does-xlock-always-prevent-reads-by-others.aspx

我使用SQL Server 2014

回答

0

不同的行爲提出不同的執行計劃。如果許多用戶具有相同的城市,SQL Server可能會選擇聚簇索引掃描,因爲您選擇所有列時效率更高。當掃描在另一個會話中遇到不兼容的鎖定行時,掃描將被阻止。

我想你可以避免提示,如果你明確INCLUDE其他2列在MyCityIndex定義。或者,將其他3列添加到CityId後的MyCityIndex鍵列表中。

下面的示例尋求使用MyCityIndex,即使沒有提示也不會阻止,因爲它僅觸及請求的CityId值。如果刪除INCLUDE子句,則會完成聚簇索引掃描,並且第二個查詢將被阻止。

CREATE TABLE dbo.Users(
     CityId int 
    , UserID int 
     CONSTRAINT PK_Users PRIMARY KEY CLUSTERED 
    , OtherData int 
    ); 
CREATE INDEX MyCityIndex ON dbo.Users(CityId) INCLUDE(UserID, OtherData); 
GO 

INSERT INTO dbo.Users VALUES(1,1,1); 
INSERT INTO dbo.Users VALUES(1,2,1); 
INSERT INTO dbo.Users VALUES(1,3,1); 
INSERT INTO dbo.Users VALUES(10,4,1); 
INSERT INTO dbo.Users VALUES(10,5,1); 
INSERT INTO dbo.Users VALUES(10,6,1); 
INSERT INTO dbo.Users VALUES(11,7,1); 
INSERT INTO dbo.Users VALUES(11,8,1); 
INSERT INTO dbo.Users VALUES(11,9,1); 
UPDATE STATISTICS dbo.Users; 
GO 

--run this on session 1 
DECLARE @CityId int = 10; 
BEGIN TRAN 
     --SELECT * FROM dbo.Users with (XLOCK,INDEX (MyCityIndex)) where CityId= @CityId 
    SELECT * FROM dbo.Users with (XLOCK) WHERE CityId= @CityId; 
--ROLLBACK; 
GO 

--run this on session 2 
DECLARE @CityId int = 11; 
BEGIN TRAN 
     --SELECT * FROM dbo.Users with (XLOCK,INDEX (MyCityIndex)) where CityId= @CityId 
    SELECT * FROM dbo.Users with (XLOCK) WHERE CityId= @CityId; 
--ROLLBACK; 
GO 
+0

ty爲答案,但你仍然沒有回答我的問題。在做'select ... where ..'語句時,實際上是否打鎖記錄?如果索引是分散的並且包含不應存在的數據會怎麼樣?我的解決方案是否仍然有效? – Aflred

+0

@Afred,我在我的答案中添加了一個腳本,表明即使沒有索引提示,查詢也不會被搜索阻塞。碎片不會導致索引頁面包含它不應該包含的數據;它可能會降低頁面預期壽命並導致更多的IO。 –

+0

這就是我正在得到的確切行爲,並且它是正確的,並且可以自己實際嘗試它。是的,您已使用'分段不會導致索引頁包含它不應該包含的數據來回答我的整個問題。它可能會降低頁面預期壽命,並導致更多的IO',所以這就是我的指數錯了'。我猜這一切都取決於SQL優化計劃。 – Aflred