我还没有找到有关此主题的信息,但也许有人尝试过这样做。想象一下负载很重的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:
这是并发执行的结果,由以下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
答案 0 :(得分:1)
您应该能够以下列方式运行查询:
UPDATE top (@lockCount) T
SET Lock = @lockID
FROM
[dbo].[Resources] T WITH (READPAST)
WHERE Lock IS NULL
这会导致UPDATE跳过已经锁定以进行更新的行,同时搜索@lockCount行以便自行更新。请注意,由于其工作方式的性质,锁定仍然会一次分配在@lockCount
的块中,除非可能存在 lot 的事务同时飞行。事实上,我正在努力想出一种方法,让你满意地证明这与你能够得到的并行,但确实如此。
更多关于READPAST