과부하 된 서버의 증상입니까?


12

응용 프로그램의 속도 저하를 진단하려고했습니다. 이를 위해 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 ReadsDuration 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

계획이 일치하는 것이 합리적입니다. 동일한 매개 변수를 사용하여 동일한 저장 프로 시저를 실행 중입니다.


2
쿼리 실행 계획을 게시 할 수 있습니까?
Kin Shah

4
여기에서 내 첫 번째 추측은 차단 될 것입니다 ...
Tibor Karaszi

3
누가 이것을 하향 조정 했습니까? 쿼리 계획이 부족하더라도 매우 상세하고 철저하게 조사 된 질문입니다! 나에게서 +1!
Vérace

4
쿼리 계획이 "동일"하다는 결론에 도달 한 적이 있습니까? 당신은 그들이 같은 모양을 의미합니까? 우리가 비교할 수 있도록 어딘가에 게시하십시오. 그들이 같다고한다고해서 그들이 동일하다는 의미는 아닙니다.
Aaron Bertrand

3
사용 acutal 실행 계획을 추가 PasteThePlan하는 것은 우리에게 쿼리를 기다리고 있었는지 아이디어를 줄 수 있습니다.
Randi Vertongen

답변:


2

wait_stats를 살펴보면 SQL 서버에서 가장 큰 병목 현상이 무엇인지 보여줍니다.

최근에 외부 응용 프로그램이 간헐적으로 느려지는 문제가 발생했습니다. 서버 자체에서 저장 프로 시저를 실행하는 것은 항상 빠릅니다.

성능 모니터링은 서버의 SQL 캐시 또는 RAM 사용 및 IO와 관련이 전혀 없었습니다.

조사 범위를 좁히는 데 도움이 된 것은 SQL에서 수집 한 대기 통계를 쿼리하는 것입니다. sys.dm_os_wait_stats

SQLSkills 웹 사이트 의 우수한 스크립트 는 가장 많이 경험하는 스크립트를 보여줍니다. 그런 다음 검색 범위를 좁혀 원인을 식별 할 수 있습니다.

대기 문제가 무엇인지 알면이 스크립트는 대기중인 세션 / 데이터베이스를 좁히는 데 도움이됩니다.

SELECT OSW.session_id,
       OSW.wait_duration_ms,
       OSW.wait_type,
       DB_NAME(EXR.database_id) AS DatabaseName
FROM sys.dm_os_waiting_tasks OSW
INNER JOIN sys.dm_exec_sessions EXS ON OSW.session_id = EXS.session_id
INNER JOIN sys.dm_exec_requests EXR ON EXR.session_id = OSW.session_id
OPTION(Recompile);

위의 쿼리와 자세한 내용은 MSSQLTips 웹 사이트를 참조하십시오 .

sp_BlitzFirst브렌트 오자르 (Brent Ozar) 웹 사이트 의 스크립트 는 또한 속도 저하의 원인을 보여줍니다.

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