带范围锁定的MSSQL SERIALIZABLE事务

时间:2016-01-30 10:42:08

标签: sql-server entity-framework-6

我正在尝试使用实体框架进行范围锁定。假设我有一个包含以下列的表:

| Id    | int   |
| Type  | int   |
| Value | int   |

其中Id是具有CLUSTERED INDEX的PRIMARY KEY且Type具有非聚集的NOT-UNIQUE INDEX。

如果我想使用此代码在可序列化事务中选择一个值

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
BEGIN TRAN
SELECT Value  FROM MyTable WHERE Type = 5

SELECT * FROM sys.dm_tran_locks WHERE  request_session_id = @@SPID AND resource_type = 'KEY'
COMMIT 

它正确地锁定了一行Type = 5和下一行。

如果我执行此查询:

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
BEGIN TRAN
SELECT Id, Type, Value FROM MyTable WHERE Type = 5

SELECT * FROM sys.dm_tran_locks WHERE  request_session_id = @@SPID AND resource_type = 'KEY'
COMMIT 

它锁定所有行。不幸的是,Entity Framwork会选择所有列:

SELECT [Id], [Type], [Value] FROM ...

我正在使用FOREIGN KEY过滤我的真实表格,此列不是唯一的。我试图在Type列UNIQUE上创建我的非聚集索引,即使我选择了所有列,它也会锁定正确的行。

如何使用NON UNIQUE INDEX?

2 个答案:

答案 0 :(得分:1)

锁定的内容取决于查询计划。计划读取的所有内容都需要锁定。因此,您需要让SQL Server找到您想要锁定的索引。首先为该查询创建最佳索引。

为什么要发生特定的锁定模式?如果由于性能原因而完全有效。如果出于行为原因这是非常不可靠的。

您还可以通过不选择实体而不是DTP对象(例如匿名类型)来使EF选择较少的列。

答案 1 :(得分:0)

很遗憾,当WHERE子句包含具有NON-UNIQUE INDEX或NO INDEX的不同列时,SERIALIZABLE事务无法使用Clustered Index进行范围锁定。

我为Entity Framework找到了一个很好的解决方法。

如果要使用特定值锁定ROWS,例如所有Type = FINISHED的行,请创建一个NON-UNIQUE索引(如果该列可以包含重复项)。

我们必须告诉SQL DB我们应该使用哪些INDEX。

// 1
while (listening)
{
    TcpClient client = listener.AcceptTcpClient();
    // Start a thread to handle this client...
    new Thread(() => HandleClient(client)).Start();
}

// 2
while (listening)
{
    TcpClient client = listener.AcceptTcpClient();
    // Start a task to handle this client...
    Task.Run(() => HandleClient(client));
}

// 3
public async void StartListener() //non blocking listener
{
    listener = new TcpListener(ipAddress, port);
    listener.Start();
    while (listening)
    {
        TcpClient client = await listener.AcceptTcpClientAsync().ConfigureAwait(false);//non blocking waiting                    
        // We are already in the new task to handle this client...   
        HandleClient(client);
    }
}
//... in your code
StartListener();
//...
//use Thread.CurrentThread.ManagedThreadId to check task/thread id to make yourself sure

我使用WITH(INDEX(MyIndex)),所以它锁定Type ='FINISHED'的所有行,即使它有NON-UNIQUE INDEX

也许有人会提供比RAW QUERY更好的解决方案。

编辑:Rangelock使用NON-UNIQUE INDEX没有任何问题。它没有使用,因为数据库中没有足够的数据。