방법론과 관련하여 잘못된 b-tree를 짖는 것으로 생각합니다 ;-).
우리가 아는 것 :
먼저 상황에 대해 알고있는 것을 통합하고 검토하겠습니다.
우리가 추측 할 수있는 것 :
다음으로, 이러한 모든 데이터 포인트를 함께 검토하여 하나 이상의 병목을 찾고 솔루션을 가리 키거나 가능한 일부 솔루션을 배제하는 데 도움이되는 추가 세부 정보를 합성 할 수 있는지 확인할 수 있습니다.
의견에서 현재 생각되는 방향은 주요 문제는 SQL Server와 Excel 간의 데이터 전송이라는 것입니다. 정말 그렇습니까? 저장 프로 시저가 각 80 만 행에 대해 호출되고 각 호출 당 (즉, 각 행당) 50ms가 걸리는 경우 최대 40,000 초 (ms 아님)가 추가됩니다. 이는 666 분 (hhmm ;-) 또는 11 시간 이상에 해당합니다. 그러나 전체 프로세스는 실행하는 데 7 시간 밖에 걸리지 않습니다. 우리는 총 시간에 대해 이미 4 시간을 보냈으며 계산을 수행하거나 결과를 SQL Server에 다시 저장하기 위해 시간을 추가했습니다. 그래서 여기에 뭔가가 없습니다.
저장 프로 시저의 정의를 보면 @FileID
;에 대한 입력 매개 변수 만 있습니다 . 에 필터가 없습니다 @RowID
. 따라서 다음 두 시나리오 중 하나가 발생하고 있다고 생각합니다.
- 이 저장 프로 시저는 않습니다 하지 실제로 대신 각 당, 각 행마다 호출되는
@FileID
약 4000 행에 걸쳐 나타나는. 반환 된 명시된 4000 개의 행이 상당히 일관된 경우, 800,000 개의 행에는 그룹화 중 200 개만 있습니다. 그리고 각각 50ms를 200 번 실행하면 7 시간 중 10 초에 불과합니다.
- 이 저장 프로 시저가 실제로 모든 행에 대해 호출
@FileID
되는 경우 새 행을 처음 전달할 때 새 행을 버퍼 풀로 가져 오는 데 약간 더 오래 걸리지는 않지만 다음 3999 실행은 일반적으로 이미 캐시, 맞습니까?
이 "필터"저장 프로 시저 또는 SQL Server에서 Excel 로의 데이터 전송에 중점을 두는 것은 빨간 청어라고 생각 합니다.
현재로서는 부족한 성능의 가장 관련성있는 지표는 다음과 같습니다.
- 800,000 개의 행이 있습니다
- 작업은 한 번에 한 행씩 작동합니다.
- 데이터가 SQL Server에 다시 저장되므로 "일부 열의 값을 사용 하여 다른 열을 조작합니다 " "[my emphas is ;-)]
나는 의심한다 :
- 데이터 검색 및 계산 기능을 개선 할 여지가 있지만 처리 시간이 크게 단축되는 것은 아닙니다.
- 주요 병목 현상은
UPDATE
80 만 개의 개별 거래를 발행하는 800,000 건의 별도 명세서를 발행 하는 것입니다.
나의 추천 (현재 이용 가능한 정보에 근거) :
가장 큰 개선 영역은 한 번에 (예 : 한 트랜잭션에서) 여러 행을 업데이트하는 것입니다. 각 프로세스 FileID
대신 프로세스를 업데이트해야합니다 RowID
. 그래서:
- 특정의 모든 4000 행을
FileID
배열로 읽습니다.
- 배열은 조작되는 필드를 나타내는 요소를 포함해야합니다
- 배열을 순환하면서 현재와 같이 각 행을 처리하십시오.
- 배열의 모든 행 (예 :이 특정 행
FileID
)이 계산 되면 :
- 거래를 시작하다
- 각 업데이트마다 호출
RowID
- 오류가 없으면 트랜잭션을 커밋하십시오.
- 오류가 발생하면 롤백하고 적절히 처리하십시오.
클러스터형 인덱스가 아직 정의되어 있지 않으면 (FileID, RowID)
@MikaelEriksson이 질문에 대한 의견에서 제안한 것처럼 고려해야합니다. 이 싱글 톤 업데이트에는 도움이되지 않지만 "필터"저장 프로 시저에서 수행하는 작업 (예 : 모두 기반)이므로 집계 작업을 약간 개선합니다 FileID
.
논리를 컴파일 된 언어로 이동하는 것을 고려해야합니다. .NET WinForms 앱 또는 콘솔 앱을 만드는 것이 좋습니다. SQL Agent 또는 Windows 예약 작업을 통해 쉽게 예약 할 수 있으므로 콘솔 앱을 선호합니다. VB.NET에서 수행되는지 또는 C #에서 수행되는지는 중요하지 않습니다. VB.NET은 개발자에게 더 적합 할 수 있지만 여전히 학습 곡선이 있습니다.
이 시점에서 SQLCLR로 이동해야 할 이유가 없습니다. 알고리즘이 자주 바뀌면 성가신 어셈블리를 항상 다시 배치해야합니다. 콘솔 응용 프로그램을 다시 작성하고 .exe를 네트워크의 적절한 공유 폴더에 배치하여 동일한 프로그램을 실행하면 항상 최신 상태로 유지되므로 쉽게 수행 할 수 있습니다.
문제가 내가 의심하고 한 번에 하나의 업데이트를 수행하는 경우 처리를 T-SQL로 완전히 옮기는 것이 도움이되지 않는다고 생각합니다.
처리가 .NET으로 이동되면 TVP (Table-Valued Parameters)를 사용하여 UPDATE
해당 JOIN을 TVP 테이블 변수로 호출하여 단일 트랜잭션 인 스토어드 프로 시저로 배열을 전달할 수 있습니다. . TVP는 4000 INSERT
개의 단일 트랜잭션으로 그룹화 하는 것보다 빠릅니다 . 그러나 한 번의 INSERT
거래에서 4000 초가 넘는 TVP를 사용함으로써 얻는 이익 은 80 만 개의 개별 거래에서 각각 4000 개의 행으로 200 개의 거래로 이동할 때의 개선만큼 중요하지 않을 것입니다.
TVP 옵션은 VBA 측에서는 기본적으로 제공되지 않지만 누군가는 테스트 가치가있는 해결 방법을 제안했습니다.
VBA에서 SQL Server 2008 R2로 전환 할 때 데이터베이스 성능을 개선하려면 어떻게해야합니까?
필터 PROC 만 사용하는 경우 FileID
에 WHERE
절 그 시저가 정말 모든 행마다 호출되는 경우, 그리고, 당신은 첫 번째 실행의 결과를 캐싱하고 당 행의 나머지를 사용하여 일부 처리 시간을 절약 할 수 있습니다 FileID
, 권리?
당신이 처리가 완수되면 FileID에 당 , 다음 우리는 병렬 처리에 대해 이야기를 시작할 수 있습니다. 그러나 그것은 그 시점에 필요하지 않을 수도 있습니다 :). Excel, VBA 및 800k 트랜잭션, SSIS 또는 평행 사변형에 대한 이야기, 또는 누가 아는 것 등 3 가지 주요 비 이상적인 부분을 다루고 있다고 가정하면 조기 최적화 / 말보다 카트 형 제품 . 이 7 시간 프로세스를 10 분 이하로 줄일 수 있다고하더라도 더 빠른 방법을 찾고 계십니까? 목표 완료 시간이 있습니까? FileID별로 처리가 완료되면 기본적으로 VB.NET 콘솔 앱 (예 : 명령 줄 .EXE)이있는 경우 SQL Agent CmdExec 단계 또는 Windows 예약 작업을 통해 해당 FileID를 한 번에 몇 개 실행하지 못하게하는 것은 없습니다. 기타
또한 "단계별"접근 방식을 사용하여 한 번에 몇 가지 개선 작업을 수행 할 수 있습니다. 예를 들어 업데이트를 시작하여 FileID
해당 그룹에 대해 하나의 트랜잭션을 사용하는 것과 같습니다. 그런 다음 TVP가 작동하는지 확인하십시오. 그런 다음 해당 코드를 가져 와서 VB.NET으로 옮기는 방법을 참조하십시오. TVP는 .NET에서 작동하므로 제대로 작동합니다.
우리가 모르는 것은 여전히 도움이 될 수 있습니다.
- "필터"저장 프로시 저는 RowID 또는 FileID마다 실행 됩니까? 해당 스토어드 프로 시저에 대한 완전한 정의가 있습니까?
- 테이블의 전체 스키마. 이 테이블은 얼마나 넓습니까? 가변 길이 필드는 몇 개입니까? NULL이 가능한 필드는 몇 개입니까? NULL이 가능한 경우 NULL을 포함하는 수는 몇 개입니까?
- 이 테이블의 인덱스 분할되어 있습니까? ROW 또는 PAGE 압축이 사용되고 있습니까?
- 이 테이블의 크기는 MB / GB로 얼마입니까?
- 이 테이블에 대한 인덱스 유지 관리는 어떻게 처리됩니까? 인덱스는 얼마나 조각화되어 있습니까? 통계는 어떻게 최신 상태입니까?
- 이 7 시간 프로세스가 진행되는 동안 다른 프로세스가이 테이블에 기록합니까? 가능한 분쟁 원인.
- 이 7 시간 프로세스가 진행되는 동안이 테이블에서 다른 프로세스를 읽습니까? 가능한 분쟁 원인.
업데이트 1 :
** VBA (Visual Basic for Applications) 및 VBA로 수행 할 수있는 작업에 대해 약간의 혼동이있는 것 같습니다. 이는 모두 동일한 웹 페이지에 있는지 확인하기위한 것입니다.
업데이트 2 :
한 가지 더 고려해야 할 점 : 연결은 어떻게 처리됩니까? VBA 코드가 각 작업마다 Connection을 열고 닫습니까, 아니면 프로세스 시작시 연결을 열고 프로세스가 끝날 때 (예 : 7 시간 후) 닫습니까? 연결 풀링 (기본적으로 ADO를 사용하도록 설정해야 함)을 사용하더라도 800,200 또는 1,600,000 회를 여는 것과는 대조적으로 한 번만 여는 것과 닫는 사이에는 여전히 상당한 영향이 있어야합니다. 이러한 값은 필터 저장 프로 시저가 실제로 얼마나 자주 실행되는지에 따라 최소 800,000 개의 UPDATE와 200 또는 800k EXEC를 기반으로합니다.
너무 많은 연결 문제는 위에서 설명한 권장 사항에 의해 자동으로 완화됩니다. 트랜잭션을 작성하고 해당 트랜잭션 내에서 모든 UPDATE를 수행하면 해당 연결을 열린 상태로 유지하고 각각에 대해 재사용하게됩니다 UPDATE
. 지정된 호출 당 4000 개의 행을 얻기 위해 연결이 초기 호출에서 열린 상태로 유지되는지 또는 FileID
"get"조작 후 닫히고 UPDATEs에 대해 다시 열린 상태인지 여부는 이제 둘 중 하나의 차이점에 대해 이야기하므로 영향을 훨씬 덜받습니다. 전체 프로세스에서 200 또는 400 개의 총 연결.
업데이트 3 :
나는 빠른 테스트를했다. 이 테스트는 소규모 테스트이며 정확히 동일한 작업이 아닙니다 (INSERT vs EXEC + UPDATE). 그러나 연결 및 트랜잭션 처리 방법과 관련된 타이밍의 차이는 여전히 관련이 있으므로 정보가 여기에서 비교적 유사한 영향을 미치는 것으로 추정 될 수 있습니다.
테스트 매개 변수 :
- SQL Server 2012 개발자 버전 (64 비트), SP2
표:
CREATE TABLE dbo.ManyInserts
(
RowID INT NOT NULL IDENTITY(1, 1) PRIMARY KEY,
InsertTime DATETIME NOT NULL DEFAULT (GETDATE()),
SomeValue BIGINT NULL
);
조작:
INSERT INTO dbo.ManyInserts (SomeValue) VALUES ({LoopIndex * 12});
- 각 테스트 당 총 인서트 : 10,000
- 각 테스트 당 재설정 :
TRUNCATE TABLE dbo.ManyInserts;
(이 테스트의 특성상 FREEPROCCACHE, FREESYSTEMCACHE 및 DROPCLEANBUFFERS를 수행하면 큰 가치가없는 것으로 보입니다.)
- 복구 모델 : 단순 (및 로그 파일에서 1GB 여유 공간)
- 트랜잭션을 사용하는 테스트는 트랜잭션 수에 관계없이 단일 연결 만 사용합니다.
결과 :
Test Milliseconds
------- ------------
10k INSERTs across 10k Connections 3968 - 4163
10k INSERTs across 1 Connection 3466 - 3654
10k INSERTs across 1 Transaction 1074 - 1086
10k INSERTs across 10 Transactions 1095 - 1169
보시다시피, DB에 대한 ADO 연결이 모든 작업에서 이미 공유되고 있더라도 명시 적 트랜잭션 (ADO 개체가이를 처리 할 수 있어야 함)을 사용하여 일괄 처리 그룹으로 그룹화하면 크게 (즉, 2 배 이상 향상) 보장됩니다. 전체 프로세스 시간을 줄입니다.