SQL : CPU 또는 IO가 아닌 경우 INSERT 속도가 느려지는 것은 무엇입니까?


19

쓰기가 많은 제품에 대한 데이터베이스가 있습니다. 방금 SSD를 갖춘 새로운 서버 시스템을 구입했습니다. 놀랍게도 저장 속도가 훨씬 느린 기존 컴퓨터보다 삽입 속도가 빠르지 않았습니다. 벤치마킹 과정에서 SQL Server 프로세스에서 표시되는 IO 비율이 매우 낮다는 것을 알았습니다.

예를 들어 루프 주변에 BEGIN TRAN 및 COMMIT를 추가 한 것을 제외하고는 이 페이지 에서 찾은 스크립트를 실행했습니다 . 기껏해야 디스크 사용량이 7Mb / s에 달하는 반면 CPU는 거의 5 %에 ​​닿지 않았습니다. 서버에 64Gb가 설치되어 있고 10을 사용 중입니다. 총 실행 시간은 첫 번째 호출의 경우 2 분 15 초이고 후속 호출의 경우 약 1 분입니다. 데이터베이스가 단순 복구 상태이며 테스트 중에 유휴 상태입니다. 각 통화 사이에 테이블을 삭제했습니다.

왜 그렇게 간단한 스크립트가 그렇게 느려 집니까? 하드웨어는 거의 사용되지 않습니다. 전용 디스크 벤치마킹 도구와 SQLIO는 모두 SSD가 읽기 및 쓰기 속도가 500Mb / s 이상의 속도로 올바르게 작동 함을 나타냅니다. 임의 쓰기가 순차적 쓰기보다 느리다는 것을 이해하지만 클러스터 인덱싱이없는 테이블에 이와 같은 간단한 삽입이 훨씬 빠를 것으로 예상합니다.

궁극적으로 우리의 시나리오는 훨씬 더 복잡하지만 간단한 사례를 먼저 이해해야한다고 생각합니다. 간단히 말해서 우리의 응용 프로그램은 오래된 데이터를 삭제 한 다음 SqlBulkCopy를 사용하여 새 데이터를 준비 테이블에 복사하고 필터링을 수행 한 다음 마지막으로 데이터를 최종 테이블에 복사하는 경우에 따라 MERGE 및 / 또는 INSERT INTO를 사용합니다.

-> 편집 1 : Martin Smith와 연결된 절차를 따르면 다음과 같은 결과가 나타납니다.

[Wait Type]  [Wait Count] [Total Wait (ms)] [T. Resource Wait (ms)] [T. Signal Wait (ms)]
NETWORK_IO          5008              46735                 46587        148
LOGBUFFER           901               5994                  5977         17
PAGELATCH_UP        40                866                   865          1
SOS_SCHEDULER_YIELD 53279             219                   121          98
WRITELOG            5                 145                   145          0
PAGEIOLATCH_UP      4                 58                    58           0
LATCH_SH            5                 0                     0            0

NETWORK_IO가 표시하는 결과가없고 SQL 파일 이외의 다른 곳으로 전송할 데이터가 없다는 점을 고려하면 NETWORK_IO가 대부분 시간이 걸린다는 것을 알았습니다. NETWORK_IO 유형에 모든 IO가 포함됩니까?

-> 편집 2 : 20Gb RAM 디스크를 만들고 거기에서 데이터베이스를 마운트했습니다. SSD에서 가장 좋은 시간은 48 초이며 RAM 디스크는 37 초로 줄었습니다. NETWORK_IO는 여전히 가장 큰 대기입니다. RAM 디스크에 대한 최대 쓰기 속도는 약 250Mb / s이며 초당 수 기가 바이트를 수행 할 수 있습니다. 여전히 많은 CPU를 사용하지 않았으므로 SQL을 유지하는 것은 무엇입니까?



3
NETWORK_IO메시지는 3 백만 개의 "1 개의 행 영향을받는"메시지가 전송 된 것일 수 있습니다. SET NOCOUNT ON스크립트에 추가하려고 했습니까 ?
Martin Smith

예, NOCOUNT를 추가했습니다.
Djof

2
이상한. 나는 그때 네트워크 활동의 방식으로 많이 기대하지 않을 것입니다. 실행 사이에 이전 확장 이벤트 파일을 삭제 했습니까? 그것들을 읽는 스크립트는 와일드 카드를 사용 EE_WaitStats*.xel하므로 오래된 카드 는 결과를 오염시킵니다.
Martin Smith

좋은 전화, 내일 결과를 업데이트합니다.
Djof

답변:


9

나는 그것이 오래된 질문이라는 것을 알고 있지만 이것은 여전히 ​​검색 자에게 도움이 될 수 있으며 때때로 나타나는 문제입니다.

리소스 병목 현상을 보지 않고 성능 상한에 도달하는 주된 이유는 단일 세션 단일 스레드 내에서 처리 할 수있는 한계에 도달했기 때문입니다. 루프는 병렬로 처리되지 않지만 모든 인서트는 순차적으로 수행됩니다.

필자의 경우 3 백만 행을 삽입하는 데 36 초가 걸립니다. 이는 행당 36/30000000 = 0.000012 초를 의미합니다. 꽤 빠릅니다. 내 시스템에서는 필요한 모든 단계를 수행하는 데 0.000012가 필요합니다.

더 빠르게하는 유일한 방법은 두 번째 세션을 동시에 시작하는 것입니다.

2 세션을 병렬로 시작하면 1,500 만 개의 삽입을 수행합니다. 둘 다 18 초 안에 끝납니다. 더 확장 할 수는 있지만 현재 테스트 설정이 2 개의 병렬 세션으로 95 % CPU를 기록하고 있으므로 3을 수행하면 CPU 병목 현상이 발생하므로 결과가 왜곡됩니다.

두 개의 병렬 세션을 시작하여 3 백만 개의 행을 삽입하면 둘 다 39 초 안에 완료됩니다. 이제 39 초 동안 6 백만 행이되었습니다.

그래도 여전히 NETWORK_IO 대기 상태가 나타납니다.

NETWORK_IO 대기는 확장 이벤트를 사용하여 추적한다는 사실에 의해 추가됩니다. 필자의 경우 인서트는 평균 36 초가 걸립니다. 확장 이벤트 방식을 사용할 때 (첫 번째 주석의 위 링크에서) 다음과 같이 등록됩니다.

Wait Type             Wait Count  Total Wait Time (ms) Total Resource Wait Time (ms) Total Signal Wait Time (ms)
NETWORK_IO            3455        68808                68802                         6
PAGEIOLATCH_SH        3           64                   64                            0
PAGEIOLATCH_UP        12          58                   58                            0
WRITE_COMPLETION      8           15                   15                            0
WRITELOG              3           9                    9                             0
PAGELATCH_UP          2           4                    4                             0
SOS_SCHEDULER_YIELD   32277       1                    0                             1
IO_COMPLETION         8           0                    0                             0
LATCH_SH              3           0                    0                             0
LOGBUFFER             1           0                    0                             0

68 초의 NETWORK_IO가 등록되어 있음을 알 수 있습니다. 그러나 삽입 루프는 단일 스레드 작업이므로 36 초가 걸렸으므로 불가능합니다. (예, 여러 스레드가 사용되지만 작업은 직렬 적이며 병렬 적이 지 않으므로 쿼리의 총 지속 시간보다 더 많은 대기 시간을 누적 할 수 없습니다)

확장 이벤트를 사용하지 않고 조용한 인스턴스에서 대기 통계 DMV 만 사용하면 (삽입을 실행하면) 다음과 같이 나타납니다.

Wait Type                   Wait Count  Total Wait Time (ms)  Total Resource Wait Time (ms) Signal Resource Wait Time (ms)
SOS_SCHEDULER_YIELD             8873                 0.21                                    0.01                                    0.20
PAGEIOLATCH_UP                  3                    0.02                                    0.02                                    0.00
PREEMPTIVE_OS_AUTHENTICATIONOPS 17                   0.02                                    0.02                                    0.00
PAGEIOLATCH_SH                  1                    0.00                                    0.00                                    0.00

따라서 확장 이벤트 로그에 표시된 NETWORK_IO는 삽입 루프와 관련이 없습니다. (nocount를 켜지 않으면 대규모 비동기 네트워크 IO 대기가 발생합니다. +1 Martin)

그러나 왜 NETWORK_IO가 확장 이벤트 추적에 나타나는지 모르겠습니다. 이벤트의 비동기 파일 대상에 쓰는 것이 ASYNC_NETWORK_IO를 축적하는지 확인하십시오. 그러나 이것은 반드시 우리가 필터링하는 것과 다른 SPID에서 수행됩니다. 나는 이것을 새로운 질문으로 스스로에게 물을 수도있다)


1
"자원 병목 현상을 보지 않고 성능 상한에 도달 한 것은 단일 세션 단일 스레드 내에서 처리 할 수있는 한계에 도달했기 때문입니다."100 % CPU 병목 현상 (한 코어)을 설명하고 있습니다. 병목 현상이 없으면 시스템 더 빨리 진행되므로 다른 작업을 수행해야합니다.
Remus Rusanu

당신의 대답은 매우 유익한 에드워드입니다. 병렬 처리가 이미 진행중인 문제에 대한 해결책 인 것처럼 보이지만 데이터베이스 레이아웃을 변경해야합니다. 그러나 Remus와 마찬가지로 머신이 왜 하나의 CPU 또는 디스크 리소스를 모두 사용하지 않는지 궁금합니다.
Djof

9

일반적으로 sys.dm_exec_requests, 특히 wait_time, wait_typewait_resourceINSERT 요청 을 살펴 보는 것으로 시작합니다 . 이렇게하면 INSERT를 차단하는 내용이 명확하게 나타납니다. 결과는 잠금 경합, 파일 증가 이벤트, 로그 플러시 대기, 할당 경합 (PFS 페이지 래치 경합과 같은 매니페스트) 등인지 여부를 나타냅니다. 측정 한 후에 질문을 적절히 업데이트하십시오. 계속 진행하기 전에 지금 중지하고 대기 및 대기열 문제 해결 방법론을 읽어 보시기 바랍니다 .


3

루프 주변의 BEGIN TRAN / COMMIT와 함께 OP에 연결된 페이지에서 테스트 스크립트를 실행했습니다. 내 컴퓨터에서 처음 완료하는 데 1:28이 걸렸습니다.

그런 다음이 두 명령을 루프 외부로 옮겼습니다.

SELECT @Random = ROUND(((@Upper - @Lower -1) * RAND() + @Lower), 0)
SET @InsertDate = DATEADD(dd, @Random, GETDATE())

그 후 28 초 만에 완료되었습니다.

무슨 일이 일어나고 있는지 확실하지 않지만 RAND()코드 에 어떤 종류의 수면이있을 수 있습니다. 아마도 엔트로피를 생성하는 데 사용하는 알고리즘의 일부로 더 나은 난수입니다.

FWIW, SSD는 쓰기가 많은 앱에 항상 최고의 기술은 아닙니다. 최상의 성능을 위해서는 DB 로그가 DB 데이터와 다른 드라이브 문자에 있고 로그 파일이 최대 크기로 미리 자르고 로그를 자르지 않도록하십시오.


입력하신 RickNZ에 감사드립니다. 루프 밖으로 코드를 이동하여 더 빠른 결과를 얻지 못했습니다. 내가 여러 번 실행하면 속도가 빨라지면 경험 한 것일 수 있습니다. 나는 SSD가 은색 총알이 아니라는 것을 알고 있지만 여전히 성능이 그렇지 않은 것처럼 느낍니다.
Djof

1

속도 저하를 식별하는 데 사용하는 또 다른 DMV는 sys.dm_os_waiting_tasks 입니다. 쿼리가 CPU를 많이 사용하지 않는 경우이 DMV의 대기에 대한 자세한 정보를 찾을 수 있습니다.


0

SQL 2008의 대기 이벤트 목록을 확인하고 있으며 NETWORK_IO가 표시되지 않습니다 : http://technet.microsoft.com/en-us/library/ms179984(v=sql.100).aspx

NETWORK_IO가 방금 ASYNC_NETWORK_IO로 표시되었다고 생각했기 때문에 해당 버전에서 대기 이벤트가 어떻게 / 왜 나타나는지 궁금하기 때문에 SQL 버전을 다시 확인할 수 있는지 묻고 싶었습니다.

네트워크 대기가 전혀 나타나지 않으면 독립형 서버에서 작업하는 경우에도 발생할 수 있습니다. 네트워크 카드의 설정을 확인 했습니까? 그들이 문제인지 궁금합니다.

하루가 끝나면 메모리, CPU, 디스크 I / O, 네트워크 및 잠금과 같은 몇 가지 리소스 병목 현상이 발생합니다. CPU 및 I / O가 문제가 아니라고 대기 이벤트 NETWORK_IO가 있으므로 NIC 카드를 먼저 살펴 보는 것이 좋습니다.


1
NETWORK_IO영업 확장 된 이벤트를 사용하고 있기 때문에 표시됩니다. 그것은 업데이트되지 않았습니다sys.dm_xe_map_values
마틴 스미스

나는 같은 SQLRockstar를 생각하고 있습니다. 네트워크 카드를 완전히 비활성화하려고했습니다. 마틴은 일부 오래된 파일이 여전히있을 수 있다고 지적했는데 결과가 변경되는지 확인하기 위해 결과를 내일 업데이트 할 것입니다.
Djof

또한 명령문에 대한 실행 계획을 볼 수 있으면 도움이 될 수 있습니다.
SQLRockstar
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.