Set 기반 알고리즘 / UDF를 구현하는 방법


13

800K 행과 38 열이있는 테이블의 모든 행에 대해 실행 해야하는 알고리즘이 있습니다. 이 알고리즘은 VBA로 구현되며 일부 열의 값을 사용하여 다른 열을 조작하여 많은 수학을 수행합니다.

현재 Excel (ADO)을 사용하여 SQL을 쿼리하고 클라이언트 쪽 커서와 함께 VBA를 사용하여 모든 행을 통해 루프로 알고리즘을 적용하고 있습니다. 작동하지만 실행하는 데 7 시간이 걸립니다.

VBA 코드는 T-SQL로 코드를 다시 작성하는 데 많은 작업이 필요할 정도로 복잡합니다.

가능한 경로로 CLR 통합 및 UDF에 대해 읽었습니다. 또한 데이터베이스에 가까워 지도록 VBA 코드를 SSIS 스크립트 작업에 넣는 것에 대해 생각했지만 이러한 유형의 성능 문제에 대한 전문적인 방법론이 존재한다고 확신합니다.

이상적으로는 병렬 세트 기반 방식으로 가능한 한 많은 행 (모두?)에 대해 알고리즘을 실행할 수 있습니다.

이러한 유형의 문제로 최상의 성능을 얻는 방법에 대한 모든 도움이 필요합니다.

--편집하다

의견에 감사드립니다 .MS SQL 2014 Enterprise를 사용하고 있습니다. 자세한 내용은 다음과 같습니다.

알고리즘은 시계열 데이터에서 특성 패턴을 찾습니다. 알고리즘 내의 함수는 다항식 스무딩, 윈도 잉을 수행하고 입력 기준에 따라 관심 영역을 찾아 12 개의 값과 일부 부울 결과를 반환합니다.

내 질문은 실제 알고리즘보다 방법론에 관한 것입니다. 한 번에 여러 행에서 병렬 계산을 수행하려면 옵션이 무엇입니까?

많은 작업이지만 가능한 T-SQL로 다시 코딩하는 것이 좋습니다.하지만 알고리즘 개발자는 VBA에서 작동하며 자주 변경되므로 T-SQL 버전과 계속 동기화하고 모든 것을 다시 확인해야합니다. 변화.

T-SQL이 집합 기반 함수를 구현할 수있는 유일한 방법입니까?


3
SSIS는 데이터 흐름을 잘 설계한다고 가정 할 때 몇 가지 기본 병렬 처리를 제공 할 수 있습니다. 이것이 행 단위로 계산해야하기 때문에 찾고있는 작업입니다. 그러나 구체적인 내용 (스키마, 관련 계산 및 이러한 계산이 달성하고자하는 것)을 제공 할 수 없다면 최적화를 도울 수 없습니다. 그들은 어셈블리로 물건을 작성하는 것이 가장 빠른 코드를 만들 수 있다고 말하지만, 나처럼 당신이 그것을 끔찍하게
빨면

2
각 행을 독립적으로 N처리 하면 800K 행을 일괄 처리 로 분할 N하고 N별도의 프로세서 / 컴퓨터 에서 알고리즘 인스턴스를 실행할 수 있습니다. 반면 SQL Server에서 Excel 또는 실제 계산으로 데이터를 전송하는 주요 병목 현상은 무엇입니까? 더미 결과를 즉시 반환하도록 VBA 함수를 변경하면 전체 프로세스에 얼마나 걸립니까? 그래도 몇 시간이 걸리면 병목 현상이 발생하는 것입니다. 몇 초가 걸리면 계산을 수행하는 VBA 코드를 최적화해야합니다.
블라디미르 Baranov

저장 프로 시저로 호출되는 필터입니다 SELECT AVG([AD_Sensor_Data]) OVER (ORDER BY [RowID] ROWS BETWEEN 5 PRECEDING AND 5 FOLLOWING) as 'AD_Sensor_Data' FROM [AD_Points] WHERE [FileID] = @FileID ORDER BY [RowID] ASC . Management Studio에서 각 행에 대해 호출되는이 기능은 50mS
medwar19

1
따라서 50ms가 걸리고 800000 번 (11 시간) 실행되는 쿼리는 시간이 걸리는 것입니다. @FileID가 각 행마다 고유합니까? 아니면 중복되어있어 쿼리 실행 횟수를 최소화 할 수 있습니까? 모든 fileid에 대한 롤링 평균을 한 번에 준비 테이블에 대한 롤링 평균 (FileID의 파티션 사용)을 미리 계산 한 다음 각 행에 윈도우 기능이 없어도 해당 테이블을 쿼리 할 수 ​​있습니다. 스테이징 테이블에 대한 최상의 설정은에 클러스터 된 인덱스가있는 것 같습니다 (FileID, RowID).
Mikael Eriksson

1
무엇보다도 각 행의 db를 터치해야 할 필요성을 제거 할 수 있다면 가장 좋습니다. 즉, TSQL로 이동하여 롤링 평균 쿼리에 참여하거나 각 행에 대해 충분한 정보를 가져와야하므로 알고리즘에 필요한 모든 것이 행에 있어야합니다. .
Mikael Eriksson

답변:


8

방법론과 관련하여 잘못된 b-tree를 짖는 것으로 생각합니다 ;-).

우리가 아는 것 :

먼저 상황에 대해 알고있는 것을 통합하고 검토하겠습니다.

  • 다소 복잡한 계산을 수행해야합니다.
    • 이것은이 테이블의 모든 행에서 발생해야합니다.
    • 알고리즘이 자주 바뀝니다.
    • 알고리즘은 다른 열을 조작하기 위해 일부 열의 값을 사용합니다.
    • 현재 처리 시간 : 7 시간
  • 탁자:
    • 800,000 개의 행을 포함합니다.
    • 38 개의 열이 있습니다.
  • 응용 프로그램 백엔드 :
  • 데이터베이스는 SQL Server 2014 Enterprise Edition입니다.
  • 모든 행에 대해 스토어드 프로 시저가 호출됩니다.

    • 실행하는 데 평균 50ms가 걸립니다.
    • 약 4000 개의 행을 반환합니다.
    • 정의는 (적어도 부분적으로) 다음과 같습니다.

      SELECT AVG([AD_Sensor_Data])
                 OVER (ORDER BY [RowID] ROWS BETWEEN 5 PRECEDING AND 5 FOLLOWING)
                 as 'AD_Sensor_Data'
      FROM   [AD_Points]
      WHERE  [FileID] = @FileID
      ORDER BY [RowID] ASC

우리가 추측 할 수있는 것 :

다음으로, 이러한 모든 데이터 포인트를 함께 검토하여 하나 이상의 병목을 찾고 솔루션을 가리 키거나 가능한 일부 솔루션을 배제하는 데 도움이되는 추가 세부 정보를 합성 할 수 있는지 확인할 수 있습니다.

의견에서 현재 생각되는 방향은 주요 문제는 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 ;-)]

나는 의심한다 :

  • 데이터 검색 및 계산 기능을 개선 할 여지가 있지만 처리 시간이 크게 단축되는 것은 아닙니다.
  • 주요 병목 현상은 UPDATE80 만 개의 개별 거래를 발행하는 800,000 건의 별도 명세서를 발행 하는 것입니다.

나의 추천 (현재 이용 가능한 정보에 근거) :

  1. 가장 큰 개선 영역은 한 번에 (예 : 한 트랜잭션에서) 여러 행을 업데이트하는 것입니다. 각 프로세스 FileID대신 프로세스를 업데이트해야합니다 RowID. 그래서:

    1. 특정의 모든 4000 행을 FileID배열로 읽습니다.
    2. 배열은 조작되는 필드를 나타내는 요소를 포함해야합니다
    3. 배열을 순환하면서 현재와 같이 각 행을 처리하십시오.
    4. 배열의 모든 행 (예 :이 특정 행 FileID)이 계산 되면 :
      1. 거래를 시작하다
      2. 각 업데이트마다 호출 RowID
      3. 오류가 없으면 트랜잭션을 커밋하십시오.
      4. 오류가 발생하면 롤백하고 적절히 처리하십시오.
  2. 클러스터형 인덱스가 아직 정의되어 있지 않으면 (FileID, RowID)@MikaelEriksson이 질문에 대한 의견에서 제안한 것처럼 고려해야합니다. 이 싱글 톤 업데이트에는 도움이되지 않지만 "필터"저장 프로 시저에서 수행하는 작업 (예 : 모두 기반)이므로 집계 작업을 약간 개선합니다 FileID.

  3. 논리를 컴파일 된 언어로 이동하는 것을 고려해야합니다. .NET WinForms 앱 또는 콘솔 앱을 만드는 것이 좋습니다. SQL Agent 또는 Windows 예약 작업을 통해 쉽게 예약 할 수 있으므로 콘솔 앱을 선호합니다. VB.NET에서 수행되는지 또는 C #에서 수행되는지는 중요하지 않습니다. VB.NET은 개발자에게 더 적합 할 수 있지만 여전히 학습 곡선이 있습니다.

    이 시점에서 SQLCLR로 이동해야 할 이유가 없습니다. 알고리즘이 자주 바뀌면 성가신 어셈블리를 항상 다시 배치해야합니다. 콘솔 응용 프로그램을 다시 작성하고 .exe를 네트워크의 적절한 공유 폴더에 배치하여 동일한 프로그램을 실행하면 항상 최신 상태로 유지되므로 쉽게 수행 할 수 있습니다.

    문제가 내가 의심하고 한 번에 하나의 업데이트를 수행하는 경우 처리를 T-SQL로 완전히 옮기는 것이 도움이되지 않는다고 생각합니다.

  4. 처리가 .NET으로 이동되면 TVP (Table-Valued Parameters)를 사용하여 UPDATE해당 JOIN을 TVP 테이블 변수로 호출하여 단일 트랜잭션 인 스토어드 프로 시저로 배열을 전달할 수 있습니다. . TVP는 4000 INSERT개의 단일 트랜잭션으로 그룹화 하는 것보다 빠릅니다 . 그러나 한 번의 INSERT거래에서 4000 초가 넘는 TVP를 사용함으로써 얻는 이익 은 80 만 개의 개별 거래에서 각각 4000 개의 행으로 200 개의 거래로 이동할 때의 개선만큼 중요하지 않을 것입니다.

    TVP 옵션은 VBA 측에서는 기본적으로 제공되지 않지만 누군가는 테스트 가치가있는 해결 방법을 제안했습니다.

    VBA에서 SQL Server 2008 R2로 전환 할 때 데이터베이스 성능을 개선하려면 어떻게해야합니까?

  5. 필터 PROC 만 사용하는 경우 FileIDWHERE절 그 시저가 정말 모든 행마다 호출되는 경우, 그리고, 당신은 첫 번째 실행의 결과를 캐싱하고 당 행의 나머지를 사용하여 일부 처리 시간을 절약 할 수 있습니다 FileID, 권리?

  6. 당신이 처리가 완수되면 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 배 이상 향상) 보장됩니다. 전체 프로세스 시간을 줄입니다.


srutzky가 제안하는 것에 대한 훌륭한 "중간자"접근 방식이 있습니다. 즉, PowerShell을 사용하여 SQL Server에서 필요한 데이터를 가져오고 VBA 스크립트를 호출하여 데이터를 작업 한 다음 SQL Server에서 업데이트 SP를 호출하는 것입니다 키와 업데이트 된 값을 SQL Server로 다시 전달합니다. 이러한 방식으로 집합 기반 접근 방식과 기존 접근 방식을 결합합니다.
Steve Mangiameli

@SteveMangiameli 안녕 스티브와 의견 주셔서 감사합니다. 나는 빨리 대답했지만 아 would을 것이다. 귀하의 아이디어가 내가 제안하는 것과 크게 다른 점이 궁금합니다. 모든 표시는 VBA를 실행하려면 Excel이 여전히 필요하다는 것입니다. 또는 PowerShell이 ​​ADO를 대체하고 I / O 속도가 훨씬 빠르면 I / O 만 교체하더라도 가치가 있다고 제안합니까?
Solomon Rutzky

1
걱정하지 마세요. 기분이 좋아집니다. 나는 그것이 더 나을 것이라는 것을 모른다. 우리는 우리가 모르는 것을 알지 못하고 훌륭한 분석을했지만 여전히 몇 가지 가정을해야합니다. I / O는 자체적으로 교체하기에 충분히 중요 할 수 있습니다. 우리는 모른다. 방금 제안한 것들에 도움이 될 수있는 다른 접근법을 제시하고 싶었습니다.
Steve Mangiameli

@SteveMangiameli 감사합니다. 명확하게 해주셔서 감사합니다. 나는 당신의 정확한 방향을 확신하지 못했고 가정하지 않는 것이 가장 좋습니다. 예, 변경할 수있는 제약이 무엇인지 알지 못하므로 더 많은 옵션을 갖는 것이 좋습니다.
Solomon Rutzky

srutzky, 자세한 생각에 감사드립니다! SQL 측에서 인덱스와 쿼리를 최적화하고 병목 현상을 찾으려고 다시 테스트했습니다. IO가 줄어들면서 36cores, 1TB의 1TB 스트립 PCIe SSD가 적절한 서버에 투자했습니다. 이제 병렬 실행을 위해 여러 스레드를 여는 것처럼 보이는 SSIS에서 VB 코드를 직접 호출합니다.
medwar19

2

IMHO와 VBA 하위를 SQL로 다시 코딩하는 것이 불가능하다는 가정에서 VBA 스크립트가 Excel 파일에서 평가를 마치고 SSIS를 통해 SQL 서버에 결과를 다시 쓰는 것을 고려 했습니까?

파일 시스템 객체 또는 서버에서 표시기를 뒤집어 VBA 하위 시작 및 종료를 수행 한 다음 (서버에 다시 쓰기 위해 연결을 이미 구성한 경우) SSIS 식을 사용 하여이 표시기를 검사 할 수 있습니다 disableSSIS 솔루션 내에서 주어진 작업의 속성 (따라서 가져 오기 프로세스는 VBA 하위가 일정 초과에 대해 걱정되는 경우 VBA 하위가 완료 될 때까지 대기합니다).

또한 VBA 스크립트를 프로그래밍 방식으로 시작할 수 있습니다 (약간 번거롭지만이 workbook_open()속성을 사용 하여 과거 에이 성격의 "화재 및 잊어 버리기"작업을 트리거했습니다).

VB 스크립트의 평가 시간이 문제가되기 시작하면 VB 개발자가 자신의 코드를 SSIS 솔루션 내의 VB 스크립트 작업에 기꺼이 이식 할 수 있는지 확인할 수 있습니다. 내 경험에 따르면 Excel 응용 프로그램에서 이 볼륨에서 데이터 작업.

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