我正在开发一项功能,它将用作流程进度监控器。
我将跨越40-50个线程,这些线程可能需要几分钟甚至几个小时才能完成,并且它们会将其状态更新为数据表。
我将从Web应用程序创建一种轮询机制,该机制将每0.5秒读取一次状态。
我需要优化该表,使其每秒可以进行多次写入,每0.5秒可以进行一次读取。我不在乎是否读取脏状态,因为它只是用于监视过程,并不是那么关键。
这是我正在使用的表格
CREATE TABLE [cmn].[ProcessProgress]
(
[id] [bigint] NOT NULL,
[status] [smallint] NOT NULL,
[step] [int] NOT NULL,
[max_step] [int] NOT NULL,
CONSTRAINT [PK_ProcessProgress] PRIMARY KEY CLUSTERED
(
[id] ASC
) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
)
GO
我想我必须使用(NOLOCK)
进行选择
SELECT * FROM [cmn].[ProcessProgress] (NOLOCK)
我是否必须使用带有特殊ISOLATION LEVEL
或(nolock)
的交易?还是(nolock)
会使思考更糟?
您能建议对这个问题最优化的解决方案吗?
答案 0 :(得分:1)
只需在数据库上设置READ_COMMITTED_SNAPSHOT选项,读取器和写入器就不会发生冲突。相反,他们将使用Row Versioning:
alter database current set read_committed_snapshot on
除了通过消除读写器之间的阻塞来提高应用程序的并发性和可伸缩性之外,它还消除了许多死锁,并消除了执行脏读的动机。
答案 1 :(得分:0)
这里是一个示例(希望id不会犯太多错误)。底部的测试代码是每个线程将执行的代码。
if object_id('ProcessProgress') is not null
drop table ProcessProgress
Go
CREATE TABLE [ProcessProgress]
(
[id] [bigint] NOT NULL IDENTITY(1,1), --added identity to shorten sample dev
[status] [smallint] NOT NULL, --1 -ready,2-inprogress, 3-complete
[step] [int] NOT NULL,
[max_step] [int] NOT NULL,
CONSTRAINT [PK_ProcessProgress] PRIMARY KEY CLUSTERED
(
[id] ASC
) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
)
GO
SET NOCOUNT ON
INSERT INTO [ProcessProgress]
(status, step, max_step)
VALUES
(1, 1, 1)
GO 1000
Go
IF OBJECT_ID('StartWork') is not null drop proc StartWork
GO
CREATE PROC StartWork
AS
BEGIN
SET TRANSACTION ISOLATION LEVEL READ COMMITTED
BEGIN TRAN
;WITH TODO
AS (
SELECT TOP 1 Id, [status] from ProcessProgress WITH (ROWLOCK, READPAST) WHERE [status] = 1 --ready
)
UPDATE TODO
SET [status] = 2 --InProgress
OUTPUT inserted.id
COMMIT
END
GO
IF OBJECT_ID('FinishWork') is not null drop proc FinishWork
GO
CREATE PROC FinishWork
@id int
AS
BEGIN
SET TRANSACTION ISOLATION LEVEL READ COMMITTED
BEGIN TRAN
Update ProcessProgress
SET [Status] = 3 --finished
WHERE
id = @id
COMMIT
END
GO
/*tester*/
declare @idout table (id int)
insert into @idout exec StartWork
declare @idin int = (Select top 1 id from @idout)
exec FinishWork @idin