SQL是处理并发的优雅方式

时间:2011-06-15 08:45:55

标签: sql sql-server sql-server-2008

我还没有找到有关此主题的信息,但也许有人尝试过这样做。想象一下负载很重的Web应用程序(.NET / SQL Server 2008)。该应用程序为首先请求“资源”的客户提供服务,并在一段时间后“提交”它们。资源有限,因此几个客户端存在并发数据库访问的高风险。目的是以这种方式优化“请求资源”机制,以便即使发生并发数据库访问,客户端也不会请求重叠的数据库记录,因此操作将尽可能并行完成...

概念数据库结构:

CREATE TABLE [dbo].[Resources] (
    [ID] [uniqueidentifier] NOT NULL,
    [Data] [xml] NOT NULL,
    [Lock] [uniqueidentifier] NULL,
) ON [PRIMARY]
GO

CREATE TABLE [dbo].[ResourceRequests] (
    [ID] [uniqueidentifier] NOT NULL,
    [ResourceID] [uniqueidentifier] NOT NULL,
    [DateCreated] [datetime] NOT NULL,
) ON [PRIMARY]
GO

CREATE TABLE [dbo].[ResourceSubmits](
    [ResourceRequestID] [uniqueidentifier] NOT NULL,
    [NewData] [xml] NOT NULL,
) ON [PRIMARY]
GO

要请求数据,首先我需要使用“UPDATE”语句“保留”它,这是同步点,因为更新是按顺序执行的

UPDATE T
SET T.Lock = (@lockID)
FROM
(
    SELECT TOP (@lockCount) R.*
    FROM [dbo].[Resources] AS R
    WHERE R.Lock IS NULL  
) AS T

(我不确定但是......)据我所知,“UPDATE”操作使用内部数据库锁,因此两个或更多并发“UPDATE”操作不可能在上面使用时改变相同的记录查询。

但是当由于“SELECT”的固定顺序而发生并发访问时,只有一个客户端会一次执行UPDATE:

Resulting data

这是并发执行的结果,由以下C#/ Linq2Sql代码完成:

Thread[] threads = new Thread[20];

for (int threadIndex = 0; threadIndex < 20; threadIndex++)
{
    threads[threadIndex] = new Thread
    (
        new ThreadStart
        (
            delegate
            {
                using (var context = new L2S.l2sDataContext())
                {
                    for (int i = 0; i < 20; i++)
                    {
                        context.LockRoots(Guid.NewGuid(), 5);
                    }
                }
            }
        )
    );
}

foreach (var thread in threads)
{
    thread.Start();
}

foreach (var thread in threads)
{
    thread.Join();
}

正如您所看到的,按顺序分配锁定,Lock列值的顺序不会混合,这意味着更新操作是按顺序执行的。

所以问题是,如果“UPDATE”查询将按以下方式更改,如何在并行上执行它们并且会改变某些内容(性能除外):

UPDATE T
SET T.Lock = (@lockID)
FROM
(
    SELECT TOP (@lockCount) R.*
    FROM [dbo].[Resources] AS R
    WHERE R.Lock IS NULL  
    ORDER BY NEWID() <--------------- The order is random now, but will the updates get executed concurrently?
) AS T

1 个答案:

答案 0 :(得分:1)

您应该能够以下列方式运行查询:

UPDATE top (@lockCount) T
SET Lock = @lockID
FROM
    [dbo].[Resources] T WITH (READPAST)
WHERE Lock IS NULL

这会导致UPDATE跳过已经锁定以进行更新的行,同时搜索@lockCount行以便自行更新。请注意,由于其工作方式的性质,锁定仍然会一次分配在@lockCount的块中,除非可能存在 lot 的事务同时飞行。事实上,我正在努力想出一种方法,让你满意地证明这与你能够得到的并行,但确实如此。

更多关于READPAST