Sql server 用于多次写入单次读取的Optimaze SQL数据库表
我正在开发一个功能,将用作流程进度监视器 我将跨越40-50个线程,这些线程可能需要几分钟甚至几个小时才能完成,它们会将其状态更新为datatable 从web应用程序中,我将创建一个轮询机制,该机制将使用每0.5秒一次读取来读取进程状态 我需要优化该表,使其每秒写入多个数据,每0.5秒读取一个数据。我不在乎是否读取脏状态,因为它只是用于监视流程,并不是那么关键 这是我正在用的桌子Sql server 用于多次写入单次读取的Optimaze SQL数据库表,sql-server,sql-optimization,Sql Server,Sql Optimization,我正在开发一个功能,将用作流程进度监视器 我将跨越40-50个线程,这些线程可能需要几分钟甚至几个小时才能完成,它们会将其状态更新为datatable 从web应用程序中,我将创建一个轮询机制,该机制将使用每0.5秒一次读取来读取进程状态 我需要优化该表,使其每秒写入多个数据,每0.5秒读取一个数据。我不在乎是否读取脏状态,因为它只是用于监视流程,并不是那么关键 这是我正在用的桌子 CREATE TABLE [cmn].[ProcessProgress] ( [id] [bigint]
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)
我是否必须使用具有特殊隔离级别的事务
或(nolock)
就足够了?或者(nolock)
会让事情变得更糟
您能为这个问题提出最优化的解决方案吗?只需在数据库上设置READ\u COMMITTED\u SNAPSHOT选项,读写器就不会发生冲突。相反,他们将使用: 除了通过消除读写器之间的阻塞来提高应用程序的并发性和可伸缩性外,它还消除了许多死锁,并消除了执行脏读的动机。下面是一个示例(希望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
我还要添加一个datetime列;随着时间的推移,您可以对这个表进行分区,以便更快地读取。您是否需要保存历史数据,或者可以随时间推移将其卸载到另一个表中(用于报告、历史目的)。如果它真的是40/50小行写入/秒和2次读取/秒(最高),那么您使用什么方法真的没有多大关系。即使是最脆弱的服务器也可以在95%的空闲状态下拉动这样的负载,即使一切都发生在
可序列化的下。您是在为网站优化还是为队列优化?@Jeroenmoster这40个可能会增长到400个,或者有一天可能会增长到4000个,所以我想确保表已准备好处理此问题load@DanielN我想要“队列”,比如说WebJob,快速写入而不出现死锁,网站读取而不影响webjob的性能这对一个简单的问题来说是一个巨大的打击:)不,它应该是所有新开发的默认值。这是非常好的信息!我没有意识到这个选择,我会在过去从很多头痛中解脱出来。它不是在读取时锁定行,而是提供查询开始时的数据快照。但是,问题是什么?更多内存消耗?@DavidBrowne Microsoft能否请您指向一些官方MS文档,这些文档进一步解释或详细说明了为什么这应该是新开发人员工作的默认选项。更新/删除查询必须维护行版本,以便读者可以看到行的“之前”图像,行需要新的14字段来携带行版本指针。您还没有解释代码的作用。陈述想法以及您的实现是如何实现的。
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