NOLOCK은 왜 변수 할당으로 스캔을 느리게합니까?


11

현재 환경에서 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 = notID  --trash variable prevents any slowdown from returning data to SSMS
FROM dbo.JustAnotherTable
ORDER BY ID
OPTION (MAXDOP 1)

SELECT DATEDIFF(MILLISECOND,@dt,SYSDATETIME())

----------------------------------------------
--Now how fast is it with NOLOCK? About 640ms for me
DECLARE @trash CHAR(5), @dt DATETIME = SYSDATETIME()

SELECT @trash = notID
FROM dbo.JustAnotherTable (NOLOCK)
ORDER BY ID --would be an allocation order scan without this, breaking the comparison
OPTION (MAXDOP 1)

SELECT DATEDIFF(MILLISECOND,@dt,SYSDATETIME())

내가 시도한 것은 효과가 없었습니다.

  • 다른 서버에서 실행 (동일한 결과 서버는 2016-SP1 및 2016-SP2, 둘 다 조용함)
  • 다른 버전의 dbfiddle.uk 에서 실행 (잡음은 있지만 아마도 같은 결과)
  • 힌트 대신 ISOLATION LEVEL 설정 (동일 결과)
  • 테이블에서 잠금 에스컬레이션 해제 (같은 결과)
  • 실제 쿼리 계획에서 스캔의 실제 실행 시간 검사 (동일한 결과)
  • 힌트 다시 컴파일 (동일한 결과)
  • 읽기 전용 파일 그룹 (동일한 결과)

가장 유망한 탐색은 휴지통 변수를 제거하고 결과 없음 쿼리를 사용하는 것입니다. 처음에는 NOLOCK이 약간 빠르지 만 데모에 상사에게 보여 주었을 때 NOLOCK은 느려졌습니다.

변수 할당으로 스캔 속도를 늦추는 NOLOCK은 무엇입니까?


소스 코드 액세스 권한이있는 사람과 프로파일 러가 결정적인 답변을 제공해야합니다. 그러나 NOLOCK은 데이터를 변경하는 경우 무한 루프에 들어 가지 않도록 추가 작업을 수행해야합니다. 그리고 NOLOCK 쿼리에 대해 비활성화 된 (일명 테스트되지 않은) 최적화가있을 수 있습니다.
David Browne-Microsoft가

1
Microsoft SQL Server 2016 (SP1) (KB3182545)-13.0.4001.0 (X64) localdb에 대한 재현은 없습니다.
마틴 스미스

답변:


12

참고 : 이것은 당신이 찾고있는 답변 유형이 아닐 수도 있습니다. 그러나 어쩌면 어디서부터 시작해야하는지에 대한 단서를 제공하는 한 다른 잠재적 인 답변자에게 도움이 될 것입니다.

ETW 추적 (PerfView 사용)에서 이러한 쿼리를 실행하면 다음과 같은 결과가 나타납니다.

Plain  - 608 ms  
NOLOCK - 659 ms

차이는 51ms 입니다. 이것은 당신의 차이 (~ 50ms)로 꽤 죽었습니다. 프로파일 러 샘플링 오버 헤드로 인해 내 숫자가 전체적으로 약간 더 높습니다.

차이점 찾기

다음은 sqlmin.dll의 FetchNextRow메서드에 51ms 차이가 있음을 보여주는 단계별 비교입니다 .

FetchNextRow

일반 선택은 왼쪽에 332ms , nolock 버전은 오른쪽에 383 ( 51ms )에 있습니다. 또한 두 코드 경로가 다음과 같이 다르다는 것을 알 수 있습니다.

  • 평원 SELECT

    • sqlmin!RowsetNewSS::FetchNextRow 전화
      • sqlmin!IndexDataSetSession::GetNextRowValuesInternal
  • 사용 NOLOCK

    • sqlmin!RowsetNewSS::FetchNextRow 전화
      • sqlmin!DatasetSession::GetNextRowValuesNoLock 어느 쪽을 호출
        • sqlmin!IndexDataSetSession::GetNextRowValuesInternal 또는
        • kernel32!TlsGetValue

이것은 FetchNextRow격리 수준 / nolock 힌트를 기반으로 한 방법에 분기가 있음을 보여줍니다 .

NOLOCK지점이 더 오래 걸리는 이유는 무엇 입니까?

nolock 브랜치는 실제로 (25ms 적은) 호출에 더 적은 시간을 소비 GetNextRowValuesInternal합니다. 그러나 코드 GetNextRowValuesNoLock(AKA를 "Exc"열이라고하는 방법은 포함하지 않음)에 직접 코드 가 63ms 동안 실행됩니다.이 차이의 대부분은 CPU 시간의 63-25 = 38ms입니다.

그렇다면 다른 13ms (총 51ms-38ms가 지금까지 설명)의 오버 헤드는 FetchNextRow무엇입니까?

인터페이스 디스패치

나는 이것이 무엇보다 호기심이라고 생각했지만 nolock 버전 은 총 17ms kernel32!TlsGetValue를 통해 Windows API 메소드 를 호출하여 인터페이스 디스패치 오버 헤드가 발생하는 것으로 보입니다 kernel32!TlsGetValueStub. 일반 선택은 인터페이스를 거치지 않는 것처럼 보이므로 스텁에 절대로 영향을 미치지 않으며 6ms 만 소비합니다 TlsGetValue( 11ms 차이 ). 첫 번째 스크린 샷에서이 내용을 볼 수 있습니다.

아마도 더 많은 쿼리 반복 으로이 추적을 다시 실행해야합니다 .PerfView의 1ms 샘플링 속도로 선택되지 않은 작은 하드웨어 인터럽트와 같은 작은 것들이 있다고 생각합니다.


그 방법을 제외하고는 nolock 버전이 느리게 실행되는 또 다른 작은 차이점을 발견했습니다.

잠금 해제

nolock 브랜치 sqlmin!RowsetNewSS::ReleaseRows는이 스크린 샷에서 볼 수 있는 방법 을보다 적극적으로 실행하는 것으로 보입니다 .

잠금 해제

일반 선택은 12ms에서 위쪽에 있으며 nolock 버전은 26ms에서 아래쪽에 있습니다 ( 14ms 더 큼 ). 또한 "When"열에서 샘플 중에 코드가 더 자주 실행되었음을 알 수 있습니다. 이것은 nolock의 구현 세부 사항 일 수 있지만 작은 샘플의 경우 약간의 오버 헤드가 발생하는 것으로 보입니다.


다른 작은 차이가 많이 있지만 큰 부분입니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.