在我目前的环境中,我正在与 NOLOCK 作斗争。我听到的一个论点是锁定的开销会减慢查询速度。所以,我设计了一个测试来看看这个开销可能有多少。
我发现 NOLOCK 实际上减慢了我的扫描速度。
起初我很高兴,但现在我很困惑。我的测试以某种方式无效吗?NOLOCK 实际上不应该允许稍微快一点的扫描吗?这里发生了什么事?
这是我的脚本:
USE TestDB
GO
--Create a five-million row table
DROP TABLE IF EXISTS dbo.JustAnotherTable
GO
CREATE TABLE dbo.JustAnotherTable (
ID INT IDENTITY PRIMARY KEY,
notID CHAR(5) NOT NULL )
INSERT dbo.JustAnotherTable
SELECT TOP 5000000 'datas'
FROM sys.all_objects a1
CROSS JOIN sys.all_objects a2
CROSS JOIN sys.all_objects a3
/********************************************/
-----Testing. Run each multiple times--------
/********************************************/
--How fast is a plain select? (I get about 587ms)
DECLARE @trash CHAR(5), @dt DATETIME = SYSDATETIME()
SELECT @trash …Run Code Online (Sandbox Code Playgroud) 我有一个处理并发 SELECT 和 DELETE 的表。我的 SELECT 语句出现了一些死锁。我假设来自其他事务的 DELETE 正在获取排他锁并与 SELECT 的共享锁冲突。
在并发 SELECT 和 DELETE 的情况下,它可能看起来像这样(在 MS Paint 中绘制,因为我试图变得专业......):
编辑:上面的“触发器”不是指实际的数据库触发器,而是指应用程序行为。
我在 DBA Stack Exchange 上四处闲逛,发现我可以SELECT myTable WITH (NOLOCK)阻止共享锁。我正在考虑使用它,但我知道有很多警告和问题,所以我想验证我的决定或在必要时更换它。
我是 WITH (NOLOCK) 的新手,所以这是我从这个有用的网站学到的东西:
WITH (NOLOCK) 表提示检索行,而无需等待其他正在读取或修改相同数据的查询来完成其处理。
这些报价来自同一个链接。在每一项下,我都描述了我的想法,得出结论认为该行为不会影响我。
通常,经常使用显式表提示被认为是一种应避免的不良做法。特别是对于NOLOCK表提示,读取未提交的数据,读完后可能会回滚,会导致Dirty read,在未提交的数据读取过程中读取正在修改或删除的数据时会出现这种情况,从而导致数据你读到的可能会有所不同,或者甚至从未存在过。
脏读:我认为我不需要关心这个,因为 SELECT 实际上并不是因为脏读而无效。任何导致脏读的 DELETE 都会触发一个新的 SELECT 来纠正最终结果。 我认为两个 SELECT 结果都是有效的,尽管一个只在几毫秒内有效。
WITH (NOLOCK) 表提示也会导致不可重复读取;当需要多次读取相同的数据并且在这些读取过程中数据发生变化时,就会发生这种读取。在这种情况下,您将阅读同一行的多个版本。
不可重复读:我的 SELECT 只有该表中的一个 SELECT 语句,所以我认为这不是问题。 …
许多应用程序将日志记录信息存储在日志表中。
这些表很特殊,因为您只会执行插入操作。你永远不会进行更新。如果您确实进行了删除,那么清除旧数据将是一项每晚的工作。
该表有一个日期时间字段,该字段通常严格递增。调用应用程序可能会失去与数据库的连接,并且会堆积插入,并且在它获得连接时,它不能保证它将按照严格的顺序执行插入。但总的来说,如果日期时间字段是聚集索引,我预计在插入时对表进行排序会很便宜。
大多数查询将查询日期时间,但这些将是不等式查询。
有了这些特殊的属性,感觉应该有一种方法来优化它。
最佳实践是什么?
日志表的示例可以是:
CREATE TABLE logMessages (
logTime datetime2(6) NOT NULL,
logSeverity varchar(10) NOT NULL,
logStatus varchar(10) NOT NULL,
logText varchar(255),
processID bigint,
processUser varchar(25)
)
Run Code Online (Sandbox Code Playgroud)
一个典型的查询:
SELECT logTime, logSeverity, logText
FROM logMessages
WHERE logTime >= '2020-10-01'
AND logTime < '2021-11-01'
AND logSeverity IN ('WARNING','ERROR','FATAL')
Run Code Online (Sandbox Code Playgroud)
唯一身份?
日期时间字段不唯一,我们是否需要唯一标识符?
选项 1:添加logId BIGINT IDENTITY列
如果我们添加一个唯一的 logId,它将不会在任何其他表中用作外键。
如果我们单独使用它作为聚集索引,并且 SQL Server 认为我们的查询将检索太多行而无法使用非聚集索引,那么它将执行全表扫描。
将聚集索引设置为 (logTime, logId) 是否有意义?我想要这样做的原因是,当查询优化器不会在 logTime 上使用非聚集索引时,因为它需要太多行,那么依靠良好的聚集索引将减少要扫描的行数。
选项 2:不添加 logId
将聚集索引设置为 (logTime)。由于 logTime 不是唯一的,SQL Server 必须通过添加不可见列来使其唯一。这是一个问题吗?至少我得到了一个良好的聚集索引,这将有助于我的大多数查询。
查找表
logSeverity …