我遇到大量阻止SELECT操作的INSERT问题。
架构图
我有一个这样的表:
CREATE TABLE [InverterData](
[InverterID] [bigint] NOT NULL,
[TimeStamp] [datetime] NOT NULL,
[ValueA] [decimal](18, 2) NULL,
[ValueB] [decimal](18, 2) NULL
CONSTRAINT [PrimaryKey_e149e28f-5754-4229-be01-65fafeebce16] PRIMARY KEY CLUSTERED
(
[TimeStamp] DESC,
[InverterID] ASC
) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF
, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON
, ALLOW_PAGE_LOCKS = ON)
)
我也有这个小帮助程序,可让我使用MERGE命令插入或更新(冲突时更新):
CREATE PROCEDURE [InsertOrUpdateInverterData]
@InverterID bigint, @TimeStamp datetime
, @ValueA decimal(18,2), @ValueB decimal(18,2)
AS
BEGIN
MERGE [InverterData] AS TARGET
USING (VALUES (@InverterID, @TimeStamp, @ValueA, @ValueB))
AS SOURCE ([InverterID], [TimeStamp], [ValueA], [ValueB])
ON TARGET.[InverterID] = @InverterID AND TARGET.[TimeStamp] = @TimeStamp
WHEN MATCHED THEN
UPDATE
SET [ValueA] = SOURCE.[ValueA], [ValueB] = SOURCE.[ValueB]
WHEN NOT MATCHED THEN
INSERT ([InverterID], [TimeStamp], [ValueA], [ValueB])
VALUES (SOURCE.[InverterID], SOURCE.[TimeStamp], SOURCE.[ValueA], SOURCE.[ValueB]);
END
用法
我现在在多个服务器上运行了服务实例,这些服务器通过[InsertOrUpdateInverterData]
快速调用过程来执行大量更新。
还有一个网站在[InverterData]
表上执行SELECT查询。
问题
如果我在[InverterData]
表上执行SELECT查询,则查询将在不同的时间范围内进行,具体取决于我的服务实例的INSERT用法。如果我暂停所有服务实例,则SELECT快如闪电,如果实例执行快速插入,则SELECT会变得非常慢,甚至超时取消。
尝试次数
我在[sys.dm_tran_locks]
表上做了一些SELECT 来查找锁定过程,像这样
SELECT
tl.request_session_id,
wt.blocking_session_id,
OBJECT_NAME(p.OBJECT_ID) BlockedObjectName,
h1.TEXT AS RequestingText,
h2.TEXT AS BlockingText,
tl.request_mode
FROM sys.dm_tran_locks AS tl
INNER JOIN sys.dm_os_waiting_tasks AS wt ON tl.lock_owner_address = wt.resource_address
INNER JOIN sys.partitions AS p ON p.hobt_id = tl.resource_associated_entity_id
INNER JOIN sys.dm_exec_connections ec1 ON ec1.session_id = tl.request_session_id
INNER JOIN sys.dm_exec_connections ec2 ON ec2.session_id = wt.blocking_session_id
CROSS APPLY sys.dm_exec_sql_text(ec1.most_recent_sql_handle) AS h1
CROSS APPLY sys.dm_exec_sql_text(ec2.most_recent_sql_handle) AS h2
结果如下:
S =共享的。保持会话被授予对资源的共享访问权限。
题
为什么SELECT被[InsertOrUpdateInverterData]
仅使用MERGE命令的过程阻止?
我是否必须使用某种内部定义了隔离模式的事务[InsertOrUpdateInverterData]
?
更新1(与@Paul的问题有关)
基于有关[InsertOrUpdateInverterData]
以下统计信息的MS-SQL Server内部报告:
- 平均CPU时间:0.12ms
- 平均读取过程:5.76个/秒
- 平均写入过程:0.4个/秒
基于此,似乎MERGE命令主要忙于将锁定表的读取操作!(?)
更新2(与@Paul的问题有关)
该[InverterData]
表具有以下存储统计信息:
- 数据空间:26,901.86 MB
- 行数:131,827,749
- 分区:true
- 分区数:62
这是(所有)完整的sp_WhoIsActive结果集:
SELECT
命令
- dd hh:mm:ss.mss:00 00:01:01.930
- session_id:73
- wait_info:(12629ms)LCK_M_S
- 处理器:198
- blocking_session_id:146
- 读取:99,368
- 写:0
- 状态:已暂停
- open_tran_count:0
封锁[InsertOrUpdateInverterData]
指令
- dd hh:mm:ss.mss:00 00:00:00.330
- session_id:146
- wait_info:空
- 处理器:3,972
- blocking_session_id:NULL
- 读取:376,95
- 写道:126
- 状态:睡觉
- open_tran_count:1
([TimeStamp] DESC, [InverterID] ASC)
对于聚集索引,看起来像是一个奇怪的选择。我的意思是那DESC
部分。