응용 프로그램의 속도 저하를 진단하려고했습니다. 이를 위해 SQL Server 확장 이벤트를 기록했습니다 .
- 이 질문에 대해 하나의 특정 저장 프로 시저를보고 있습니다.
- 그러나 사과 대 사과 조사로 동일하게 사용할 수있는 12 가지 저장 프로 시저의 핵심 세트가 있습니다.
- 저장 프로 시저 중 하나를 수동으로 실행할 때마다 항상 빠르게 실행됩니다.
- 사용자가 다시 시도하면 빠르게 실행됩니다.
저장 프로 시저의 실행 시간은 크게 다릅니다. 이 저장 프로 시저의 많은 실행은 <1로 반환됩니다.
그리고 그것을 위해 "빠른" 양동이, 훨씬 적은 1 초 이상입니다. 실제로 약 90ms입니다.
그러나 2 초, 3 초, 4 초를 기다려야하는 사용자들의 긴 꼬리가 있습니다. 일부는 12, 13, 14를 기다려야합니다. 그러면 22, 23, 24 초를 기다려야하는 정말 가난한 영혼들이 있습니다.
그리고 30 대 후 클라이언트 응용 프로그램은 포기하고 쿼리를 중단하며 사용자는 30 초 동안 기다려야했습니다 .
인과 관계를 찾는 상관 관계
그래서 나는 상관하려고했습니다.
- 지속 시간 vs 논리적 읽기
- 지속 시간 대 물리적 읽기
- 지속 시간 vs CPU 시간
그리고 아무도 상관 관계를 보이지 않는 것 같습니다. 아무도 원인이 아닌 것 같습니다
지속 시간 vs 논리적 읽기 : 약간 또는 논리적 읽기가 많더라도 지속 시간이 여전히 크게 변동합니다 .
지속 시간 vs 물리적 읽기 : 쿼리가 캐시에서 제공되지 않고 많은 물리적 읽기가 필요한 경우에도 지속 시간에 영향을 미치지 않습니다.
duration vs cpu time : 쿼리에 0 초의 CPU 시간이 걸리든 또는 전체 2.5 초의 CPU 시간이 걸리든 지속 시간은 동일한 변동성을 갖습니다.
보너스 : Duration v Physical Reads 와 Duration v CPU 시간 이 매우 비슷하다는 것을 알았습니다 . 이것은 CPU 시간과 실제 읽기를 연관 시키려고하면 입증됩니다.
I / O에서 많은 CPU 사용량이 발생합니다. 누가 알았 겠어!
따라서 실행 시간의 차이를 설명 할 수있는 쿼리 실행 작업에 대한 것이 없다면 CPU 또는 하드 드라이브와 관련이없는 것입니까?
CPU 또는 하드 드라이브에 병목 현상이 발생한 경우; 병목 현상이 아닌가?
병목 현상이 발생한 CPU라고 가정합니다. 이 서버의 CPU 전원이 부족한 경우 :
- 더 많은 CPU 시간을 사용하는 실행 시간이 더 오래 걸리지 않습니까?
- 오버로드 된 CPU를 사용하여 다른 사람들과 완료해야하기 때문에?
하드 드라이브와 유사합니다. 하드 드라이브에 병목 현상이 있다고 가정하면; 하드 드라이브에이 서버에 대한 임의 처리량이 충분하지 않은 경우 :
- 더 많은 물리적 읽기를 사용하는 실행 시간이 더 오래 걸리지 않습니까?
- 오버로드 된 하드 드라이브 I / O를 사용하여 다른 사람과 완료해야합니까?
저장 프로 시저 자체는 쓰기를 수행하거나 요구하지 않습니다.
- 일반적으로 0 개의 행 (90 %)을 반환합니다.
- 간혹 1 행 (7 %)이 반환됩니다.
- 드물게 2 행 (1.4 %)을 반환합니다.
- 최악의 경우 2 행 이상을 반환했습니다 (한 번에 12 행을 반환 함)
따라서 엄청난 양의 데이터를 반환하는 것은 아닙니다.
서버 CPU 사용량
서버의 프로세서 사용량은 평균 약 1.8 %이며 간혹 최대 18 %까지 급증하므로 CPU로드가 문제가되지 않는 것 같습니다.
따라서 서버 CPU는 오버로드되지 않습니다.
그러나 서버 는 가상입니다 ...
우주 밖에서 뭔가?
내가 상상할 수있는 유일한 것은 서버의 우주 바깥에 존재하는 것입니다.
- 논리적 읽기가 아닌 경우
- 물리적 인 읽기가 아닙니다
- 그리고 CPU 사용량이 아닙니다.
- CPU 부하가 아닙니다
그리고 동일한 쿼리를 수동으로 실행하고 27 초가 걸리지 않기 때문에 저장 프로 시저에 대한 매개 변수와 같지 않습니다 (약 0 초 소요).
서버가 동일한 컴파일 된 저장 프로 시저를 실행하는 데 0 초가 아닌 30 초가 걸리는 경우가 있습니다.
- 체크 포인트?
가상 서버입니다
- 호스트가 오버로드 되었습니까?
- 같은 호스트의 다른 VM?
서버의 확장 이벤트 진행 쿼리에 갑자기 20 초가 걸리면 특별히 발생하는 일은 없습니다. 제대로 실행되면 제대로 실행하지 않기로 결정합니다.
- 2 초
- 1 초
- 30 초
- 3 초
- 2 초
그리고 내가 찾을 수있는 다른 특별한 물건은 없습니다. 2 시간마다의 트랜잭션 로그 백업 중에는 아닙니다.
그 밖의 무엇을 할 수 는있을?
"서버" 외에 내가 말할 수있는 것이 있습니까?
편집 : 시간별 상관
나는 지속 시간을 모든 것과 관련 시켰다는 것을 깨달았다.
- 논리적 읽기
- 물리적 판독
- CPU 사용량
그러나 내가 그것을 상관시키지 않은 것은 하루 중 시간이었습니다 . 아마도 2 시간마다의 트랜잭션 로그 백업 은 문제 일 것입니다.
또는 체크 포인트 도중 척에서 속도 저하 가 발생합니까?
아니:
인텔 제온 골드 쿼드 코어 6142.
편집-사람들이 쿼리 실행 계획을 가정합니다
사람들은 쿼리 실행 계획이 "빠른"것과 "느린"사이에 달라야한다는 가설을 세웁니다. 그들은 아닙니다.
그리고 우리는 이것을 검사에서 즉시 볼 수 있습니다.
질문 기간이 길다는 것은 "불량한"실행 계획으로 인한 것이 아니라는 것을 알고 있습니다.
- 더 논리적으로 읽은 것
- 더 많은 조인 및 키 조회에서 더 많은 CPU를 소비 한 것
읽기 증가 또는 CPU 증가로 인해 쿼리 지속 시간이 증가한 경우 위의 내용을 이미 보았을 것입니다. 상관 관계가 없습니다.
그러나 CPU 읽기 영역 제품 메트릭과 지속 시간을 상관 시키십시오.
역설 인 상관 관계가 훨씬 낮아집니다.
편집 : 많은 값을 가진 Excel 산점도의 버그를 해결하기 위해 산점도를 업데이트했습니다.
다음 단계
다음 단계는 5 초 후에 누군가가 서버에서 차단 된 쿼리에 대한 이벤트를 생성하도록하는 것입니다 .
EXEC sp_configure 'blocked process threshold', '5';
RECONFIGURE
쿼리가 4 초 동안 차단되는지는 설명하지 않습니다 . 그러나 5 초 동안 쿼리를 차단하는 것은 4 초 동안 일부를 차단할 수도 있습니다.
느린 계획
실행되는 두 가지 저장 프로 시저의 느린 계획은 다음과 같습니다.
- `EXECUTE FindFrob @CustomerID = 7383, @StartDate = '20190725 04 : 00 : 00.000', @EndDate = '20190726 04 : 00 : 00.000'
- `EXECUTE FindFrob @CustomerID = 7383, @StartDate = '20190725 04 : 00 : 00.000', @EndDate = '20190726 04 : 00 : 00.000'
동일한 매개 변수를 가진 동일한 저장 프로 시저가 연속적으로 실행됩니다.
| Duration (us) | CPU time (us) | Logical reads | Physical reads |
|---------------|---------------|---------------|----------------|
| 13,984,446 | 47,000 | 5,110 | 771 |
| 4,603,566 | 47,000 | 5,126 | 740 |
전화 1 :
|--Nested Loops(Left Semi Join, OUTER REFERENCES:([Contoso2].[dbo].[Frobs].[FrobGUID]) OPTIMIZED)
|--Nested Loops(Inner Join, OUTER REFERENCES:([Contoso2].[dbo].[FrobTransactions].[OnFrobGUID]))
| |--Nested Loops(Inner Join, OUTER REFERENCES:([Contoso2].[dbo].[FrobTransactions].[RowNumber]) OPTIMIZED)
| | |--Nested Loops(Inner Join, OUTER REFERENCES:([tpi].[TransactionGUID]) OPTIMIZED)
| | | |--Nested Loops(Inner Join, OUTER REFERENCES:([tpi].[TransactionGUID]) OPTIMIZED)
| | | | |--Index Seek(OBJECT:([Contoso2].[dbo].[TransactionPatronInfo].[IX_TransactionPatronInfo_CustomerID_TransactionGUID] AS [tpi]), SEEK:([tpi].[CustomerID]=[@CustomerID]) ORDERED FORWARD)
| | | | |--Index Seek(OBJECT:([Contoso2].[dbo].[Transactions].[IX_Transactions_TransactionGUIDTransactionDate]), SEEK:([Contoso2].[dbo].[Transactions].[TransactionGUID]=[Contoso2].[dbo
| | | |--Index Seek(OBJECT:([Contoso2].[dbo].[FrobTransactions].[IX_FrobTransactions2_MoneyAppearsOncePerTransaction]), SEEK:([Contoso2].[dbo].[FrobTransactions].[TransactionGUID]=[Contos
| | |--Clustered Index Seek(OBJECT:([Contoso2].[dbo].[FrobTransactions].[IX_FrobTransactions_RowNumber]), SEEK:([Contoso2].[dbo].[FrobTransactions].[RowNumber]=[Contoso2].[dbo].[Fin
| |--Clustered Index Seek(OBJECT:([Contoso2].[dbo].[Frobs].[PK_Frobs_FrobGUID]), SEEK:([Contoso2].[dbo].[Frobs].[FrobGUID]=[Contoso2].[dbo].[FrobTransactions].[OnFrobGUID]), WHERE:([Contos
|--Filter(WHERE:([Expr1009]>(1)))
|--Compute Scalar(DEFINE:([Expr1009]=CONVERT_IMPLICIT(int,[Expr1012],0)))
|--Stream Aggregate(DEFINE:([Expr1012]=Count(*)))
|--Index Seek(OBJECT:([Contoso2].[dbo].[FrobTransactions].[IX_FrobTransactins_OnFrobGUID]), SEEK:([Contoso2].[dbo].[FrobTransactions].[OnFrobGUID]=[Contoso2].[dbo].[Frobs].[LC
전화 2
|--Nested Loops(Left Semi Join, OUTER REFERENCES:([Contoso2].[dbo].[Frobs].[FrobGUID]) OPTIMIZED)
|--Nested Loops(Inner Join, OUTER REFERENCES:([Contoso2].[dbo].[FrobTransactions].[OnFrobGUID]))
| |--Nested Loops(Inner Join, OUTER REFERENCES:([Contoso2].[dbo].[FrobTransactions].[RowNumber]) OPTIMIZED)
| | |--Nested Loops(Inner Join, OUTER REFERENCES:([tpi].[TransactionGUID]) OPTIMIZED)
| | | |--Nested Loops(Inner Join, OUTER REFERENCES:([tpi].[TransactionGUID]) OPTIMIZED)
| | | | |--Index Seek(OBJECT:([Contoso2].[dbo].[TransactionPatronInfo].[IX_TransactionPatronInfo_CustomerID_TransactionGUID] AS [tpi]), SEEK:([tpi].[CustomerID]=[@CustomerID]) ORDERED FORWARD)
| | | | |--Index Seek(OBJECT:([Contoso2].[dbo].[Transactions].[IX_Transactions_TransactionGUIDTransactionDate]), SEEK:([Contoso2].[dbo].[Transactions].[TransactionGUID]=[Contoso2].[dbo
| | | |--Index Seek(OBJECT:([Contoso2].[dbo].[FrobTransactions].[IX_FrobTransactions2_MoneyAppearsOncePerTransaction]), SEEK:([Contoso2].[dbo].[FrobTransactions].[TransactionGUID]=[Contos
| | |--Clustered Index Seek(OBJECT:([Contoso2].[dbo].[FrobTransactions].[IX_FrobTransactions_RowNumber]), SEEK:([Contoso2].[dbo].[FrobTransactions].[RowNumber]=[Contoso2].[dbo].[Fin
| |--Clustered Index Seek(OBJECT:([Contoso2].[dbo].[Frobs].[PK_Frobs_FrobGUID]), SEEK:([Contoso2].[dbo].[Frobs].[FrobGUID]=[Contoso2].[dbo].[FrobTransactions].[OnFrobGUID]), WHERE:([Contos
|--Filter(WHERE:([Expr1009]>(1)))
|--Compute Scalar(DEFINE:([Expr1009]=CONVERT_IMPLICIT(int,[Expr1012],0)))
|--Stream Aggregate(DEFINE:([Expr1012]=Count(*)))
|--Index Seek(OBJECT:([Contoso2].[dbo].[FrobTransactions].[IX_FrobTransactins_OnFrobGUID]), SEEK:([Contoso2].[dbo].[FrobTransactions].[OnFrobGUID]=[Contoso2].[dbo].[Frobs].[LC
계획이 일치하는 것이 합리적입니다. 동일한 매개 변수를 사용하여 동일한 저장 프로 시저를 실행 중입니다.