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 = 공유 보류 세션에는 자원에 대한 공유 액세스 권한이 부여됩니다.
질문
[InsertOrUpdateInverterData]
MERGE 명령 만 사용 하는 프로 시저에 의해 SELECT가 차단되는 이유는 무엇 입니까?
내부에 정의 된 격리 모드로 어떤 종류의 트랜잭션을 사용해야 [InsertOrUpdateInverterData]
합니까?
업데이트 1 (@Paul의 질문 관련)
[InsertOrUpdateInverterData]
다음 통계 에 대한 MS-SQL 서버 내부보고를 기반으로 합니다.
- 평균 CPU 시간 : 0.12ms
- 평균 읽기 프로세스 : 5.76 / 초
- 평균 쓰기 프로세스 : 초당 0.4
이것에 기초하여 MERGE 명령은 대부분 테이블을 잠글 수있는 읽기 작업으로 바쁩니다! (?)
업데이트 2 (@Paul의 질문 관련)
[InverterData]
테이블은 저장 통계를 다음있다 :
- 데이터 공간 : 26,901.86MB
- 행 수 : 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
- CPU : 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 : NULL
- CPU : 3,972
- blocking_session_id : NULL
- 읽는다 : 376,95
- 기록 : 126
- 상태 : 수면
- open_tran_count : 1
([TimeStamp] DESC, [InverterID] ASC)
외모는 클러스터 된 인덱스의 이상한 선택을 좋아한다. 나는 그DESC
부분을 의미한다 .