SQL Server 2016 잘못된 쿼리 계획은 일주일에 한 번 DB를 잠급니다


16

일주일에 지난 5 주 동안 같은 시간 (이른 아침에 사람들이 사용하기 시작할 때 사용자 활동을 기반으로 할 수 있음), SQL Server 2016 (AWS RDS, 미러 됨)이 쿼리.

모든 테이블의 UPDATE STATISTICS는 항상 즉시 수정합니다.

처음으로, 매주가 아닌 매일 밤 모든 테이블의 모든 통계를 업데이트했지만 업데이트 통계가 실행 된 후 약 8 시간이 지났지 만 매일 실행되는 것은 아닙니다.

지난 번에는 Query Store를 통해 어떤 특정 쿼리 / 쿼리 계획을 찾을 수 있는지 확인할 수있었습니다. 나는 그것을 하나로 좁힐 수 있다고 생각한다.

잘못된 쿼리 계획

해당 쿼리를 찾은 후 자주 사용하지 않는이 쿼리에서 누락 된 권장 인덱스를 추가했습니다 (그러나 자주 사용되는 테이블을 많이 만짐).

잘못된 쿼리 계획은 인덱스 스캔을 수행하는 것입니다 (10k 행만있는 테이블에서). 밀리 초 단위로 반환 된 다른 쿼리 계획은 동일한 스캔을 수행하는 데 사용됩니다. 새 인덱스를 만든 후 최신 쿼리 계획은 검색 만합니다. 그러나 99 %의 시간이 그 인덱스가 없어도 몇 밀리 초 안에 돌아 왔지만 매주 40 초가 걸렸습니다.

2012 년부터 SQL Server 2016으로 전환 한 후에 시작되었습니다.

DBCC CHECKDB는 오류를 반환하지 않습니다.

  1. 새 인덱스가 문제를 해결하여 잘못된 계획을 다시 선택하지 않습니까?
  2. 지금 잘 작동하는 계획을 "강제"해야합니까?
  3. 다른 쿼리 / 계획에이 문제가 발생하지 않도록하려면 어떻게해야합니까?
  4. 이것이 더 큰 문제의 증상입니까?

방금 추가 한 색인 :

CREATE NONCLUSTERED INDEX idx_AppointmetnAttendee_AttendeeType
ON [dbo].[AppointmentAttendee] ([UserID],[AttendeeType])

CREATE NONCLUSTERED INDEX [idx_appointment_start] ON [dbo].[Appointment]
(
    [ProjectID] ASC,
    [Start] ASC
)
INCLUDE (   [ID],
    [AllDay],
    [End],
    [Location],
    [Notes],
    [Title],
    [CreatedByID]) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]

전체 쿼리 텍스트 :

https://pastebin.com/Z5szPBfu(LINQ 생성, 선택한 열을 최적화 할 수는 있지만이 문제와 관련이 없어야 함)


시간이 초과되지 않은 이전 계획의 스캔이 동일한 크기의 다른 테이블에있는 것으로 나타났습니다. 약속 : 11931 행, 약속 참석자 : 11937 행.
프로페셔널 사운 딩 이름

답변:


16

질문과 다른 순서로 질문에 대답하겠습니다.

4. 이것이 더 큰 문제의 증상입니까?

SQL Server 2016 의 새로운 카디널리티 추정기 가 문제의 원인 일 수 있습니다. SQL Server 2012는 레거시 CE를 사용하므로 해당 버전에서 문제가 발생하지 않았습니다. 새로운 카디널리티 추정기는 데이터에 대해 다른 가정을하고 동일한 SQL에 대해 다른 쿼리 계획을 생성 할 수 있습니다. 쿼리 및 데이터에 따라 레거시 CE를 사용하는 일부 쿼리의 성능이 향상 될 수 있습니다. 따라서 데이터 모델의 일부가 새 CE에 가장 적합하지 않을 수 있습니다. 괜찮지 만 지금은 새로운 CE를 해결해야 할 수도 있습니다.

또한 일일 통계 업데이트에서도 일관성없는 쿼리 성능에 관심이 있습니다. 한 가지 중요한 점은 모든 테이블에서 통계를 수집하면 캐시에서 모든 쿼리 계획이 효과적으로 지워 지므로 통계에 문제가 있거나 매개 변수 스니핑과 관련이있을 수 있다는 것입니다. 데이터 모델, 데이터 변경 률, 통계 업데이트 정책, 코드 호출 방법 등에 대한 많은 정보 없이는 결정하기가 어렵습니다. SQL Server 2016은 유용한 매개 변수 스니핑에 대한 일부 데이터베이스 수준 설정을 제공 합니다. 하지만 문제가되는 하나의 쿼리 대신 전체 애플리케이션에 영향을 줄 수 있습니다.

이 동작으로 이어질 수있는 시나리오 예를 버리겠습니다. 당신은 말했다 :

일부 사용자는 1 개의 권한 레코드를 보유하고 일부는 최대 20k까지 보유 할 수 있습니다.

모든 쿼리 계획을 지우는 모든 테이블에서 통계를 수집한다고 가정하십시오. 위에서 언급 한 요소에 따라 오늘의 첫 번째 쿼리가 단 하나의 권한 레코드를 가진 사용자를 대상으로하는 경우 SQL Server는 하나의 레코드를 가진 사용자에게는 잘 작동하지만 20k 레코드를 가진 사용자에게는 크게 작동하는 계획을 캐시 할 수 있습니다. 오늘의 첫 번째 쿼리가 20k 레코드를 가진 사용자에 대한 것이면 20k 레코드에 대한 좋은 계획을 얻을 수 있습니다. 코드가 1 레코드 인 사용자에 대해 코드를 실행하면 가장 최적의 쿼리는 아니지만 여전히 ms 단위로 끝날 수 있습니다. 실제로 매개 변수 스니핑처럼 들립니다. 항상 문제가 표시되지 않는 이유 또는 때때로 표시되는 데 몇 시간이 걸리는 이유를 설명합니다.

1. 새로운 인덱스가 문제를 해결하여 잘못된 계획을 다시 선택하지 않습니까?

인덱스를 통해 필요한 데이터에 액세스하는 것이 테이블에 대해 클러스터 된 인덱스 스캔을 수행하는 것보다, 특히 스캔을 조기에 종료 할 수 없을 때 추가 한 인덱스 중 하나가 문제를 예방할 수 있다고 생각합니다. 쿼리 계획의 나쁜 부분을 확대 해 봅시다 :

잘못된 쿼리 계획

SQL Server는 [Permission]및 의 조인에서 하나의 행만 반환 될 것으로 추정합니다 [Project]. 외부 입력의 각 행에 대해 클러스터 된 인덱스 스캔을 수행 [Appointment]합니다. 이 테이블에서 모든 행이 스캔되지만 필터링과 일치하는 행만 [Start]조인 연산자로 리턴됩니다. 조인 연산자 내에서 결과가 더 줄어 듭니다.

실제로 조인의 외부 입력으로 전송되는 행이 하나만있는 경우 위에서 설명한 쿼리 계획은 괜찮을 수 있습니다. 그러나 조인의 카디널리티 추정값이 잘못되어 1000 행이 발생하면 SQL Server는에서 1000 개의 클러스터형 인덱스 스캔을 수행 [Appointment]합니다. 쿼리 계획의 성능은 추정 문제에 매우 민감합니다.

해당 쿼리 계획을 다시 얻지 못하는 가장 직접적인 방법은 [Appointment]테이블 에 대해 포함 인덱스를 만드는 것 입니다. 인덱스와 같은 것이 [ProjectId]있고 [Start]그것을해야합니다. 이것이 [idx_appointment_start]문제를 해결하기 위해 작성한 색인 인 것 같습니다 . SQL Server가 쿼리 계획을 선택하지 못하게하는 또 다른 방법은 [Permission]및 에서 조인의 카디널리티 추정을 수정하는 것 [Project]입니다. 코드 변경, 통계 업데이트, 레거시 CE 사용, 다중 열 통계 작성, RECOMPILE힌트 와 같은 로컬 변수에 대한 추가 정보 제공 또는 해당 행을 임시 테이블로 구체화하는 일반적인 방법 입니다. ms 수준의 응답 시간이 필요하거나 ORM을 통해 코드를 작성해야하는 경우 이러한 기술 중 많은 방법이 적합하지 않습니다.

작성한 색인 [AppointmentAttendee]은 문제점을 해결하는 직접적인 방법이 아닙니다. 그러나 인덱스에 대해 다중 열 통계를 얻을 수 있으며 이러한 통계는 잘못된 쿼리 계획을 방해 할 수 있습니다. 인덱스는 데이터에 액세스하는 더 효율적인 방법을 제공하여 잘못된 쿼리 계획을 방해 할 수 있지만 인덱스 on만으로 다시 발생하지 않을 것이라는 보장은 없습니다 [AppointmentAttendee].

3. 이것이 다른 쿼리 / 계획에 발생하지 않도록하려면 어떻게해야합니까?

왜이 질문을했는지 이해하지만 매우 광범위합니다. 저의 유일한 조언은 쿼리 계획 불안정성의 근본 원인을 더 잘 이해하고, 워크로드에 적합한 인덱스가 생성되었는지 확인하고, 워크로드를 신중하게 테스트 및 모니터링하는 것입니다. Microsoft는 SQL Server 2016의 새로운 CE로 인한 쿼리 계획 회귀를 처리하는 방법에 대한 일반적인 조언 을 제공합니다.

쿼리 프로세서를 최신 버전의 코드로 업그레이드하기 위해 권장되는 워크 플로는 다음과 같습니다.

  1. 데이터베이스 호환성 수준을 변경하지 않고 데이터베이스를 SQL Server 2016으로 업그레이드 (이전 수준으로 유지)

  2. 데이터베이스에서 쿼리 저장소를 활성화하십시오. 쿼리 저장소 사용 및 사용에 대한 자세한 내용은 쿼리 저장소를 사용하여 성능 모니터링을 참조하십시오.

  3. 워크로드의 대표 데이터를 수집하기에 충분한 시간을 기다리십시오.

  4. 데이터베이스의 호환성 수준을 130으로 변경

  5. SQL Server Management Studio를 사용하여 호환성 수준 변경 후 특정 쿼리에 대한 성능 회귀가 있는지 평가

  6. 회귀가있는 경우 쿼리 저장소에서 이전 계획을 강제 실행하십시오.

  7. 강제 실행에 실패한 쿼리 계획이 있거나 성능이 여전히 부족한 경우 호환성 수준을 이전 설정으로 되 돌린 다음 Microsoft 고객 지원에 문의하십시오.

SQL Server 2012로 다운 그레이드하고 다시 시작해야한다고 말하지는 않지만 설명 된 일반적인 기술이 유용 할 수 있습니다.

2. 지금 잘 작동하는 계획을 "강제"해야합니까?

전적으로 당신에게 달려 있습니다. 가능한 모든 입력 매개 변수에 대해 잘 작동하는 쿼리 계획이 있다고 생각하고 쿼리 저장소의 기능에 익숙하고 쿼리 계획을 강제 적용 할 때 안심하고 사용하십시오. 회귀가 발생한 쿼리 계획을 강제하는 것은 결국 Microsoft의 SQL Server 2016으로의 업그레이드 정책의 일부입니다.

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