SQL Server의 엄청난 데이터 및 성능


20

SQL Server 백엔드를 사용하여 매우 많은 양의 레코드를 수집하고 저장하는 응용 프로그램을 작성했습니다. 나는 최고점에서 평균 레코드 양이 하루에 약 3-4 억 (20 시간의 작동 시간) 정도 어딘가에 계산했습니다.

내 원래 솔루션 (실제로 데이터를 계산하기 전에)은 내 응용 프로그램이 클라이언트가 쿼리 한 동일한 테이블에 레코드를 삽입하는 것이 었습니다. 많은 레코드가 삽입 된 테이블을 쿼리 할 수 ​​없기 때문에 충돌이 발생했습니다.

두 번째 솔루션은 두 개의 데이터베이스를 사용하는 것이 었습니다. 하나는 응용 프로그램이 수신 한 데이터와 하나는 클라이언트 준비 데이터를위한 것입니다.

내 응용 프로그램은 데이터를 수신하여 ~ 100k 레코드의 배치로 청크하고 준비 테이블에 대량 삽입합니다. ~ 100k 레코드 후 응용 프로그램은 즉시 이전과 동일한 스키마를 사용하여 다른 준비 테이블을 만들고 해당 테이블에 삽입을 시작합니다. 100k 레코드가있는 테이블 이름으로 작업 테이블에 레코드를 작성하고 SQL Server 측의 스토어드 프로시 저는 스테이징 테이블에서 클라이언트 준비 프로덕션 테이블로 데이터를 이동 한 다음 삭제합니다. 내 응용 프로그램에서 만든 테이블 임시 테이블.

작업 테이블이있는 스테이징 데이터베이스를 제외하고 두 데이터베이스 모두 동일한 스키마를 가진 5 개의 테이블 세트가 동일합니다. 준비 데이터베이스에는 대량의 레코드가 상주 할 테이블에 무결성 제약 조건, 키, 인덱스 등이 없습니다. 아래에 표시된 테이블 이름은 SignalValues_staging입니다. 목표는 내 응용 프로그램이 가능한 한 빨리 SQL Server에 데이터를 넣도록하는 것이 었습니다. 쉽게 마이그레이션 할 수 있도록 테이블을 즉석에서 생성하는 워크 플로는 매우 잘 작동합니다.

다음은 내 준비 데이터베이스의 관련 테이블 5 개와 작업 테이블입니다.

스테이징 테이블 필자가 작성한 저장 프로시 저는 모든 준비 테이블에서 데이터를 이동하여 프로덕션에 삽입하는 작업을 처리합니다. 다음은 준비 테이블에서 프로덕션에 삽입하는 저장 프로 시저의 일부입니다.

-- Signalvalues jobs table.
SELECT *
      ,ROW_NUMBER() OVER (ORDER BY JobId) AS 'RowIndex'
INTO #JobsToProcess
FROM 
(
    SELECT JobId 
           ,ProcessingComplete  
           ,SignalValueStagingTableName AS 'TableName'
           ,(DATEDIFF(SECOND, (SELECT last_user_update
                              FROM sys.dm_db_index_usage_stats
                              WHERE database_id = DB_ID(DB_NAME())
                                AND OBJECT_ID = OBJECT_ID(SignalValueStagingTableName))
                     ,GETUTCDATE())) SecondsSinceLastUpdate
    FROM SignalValueJobs
) cte
WHERE cte.ProcessingComplete = 1
   OR cte.SecondsSinceLastUpdate >= 120

DECLARE @i INT = (SELECT COUNT(*) FROM #JobsToProcess)

DECLARE @jobParam UNIQUEIDENTIFIER
DECLARE @currentTable NVARCHAR(128) 
DECLARE @processingParam BIT
DECLARE @sqlStatement NVARCHAR(2048)
DECLARE @paramDefinitions NVARCHAR(500) = N'@currentJob UNIQUEIDENTIFIER, @processingComplete BIT'
DECLARE @qualifiedTableName NVARCHAR(128)

WHILE @i > 0
BEGIN

    SELECT @jobParam = JobId, @currentTable = TableName, @processingParam = ProcessingComplete
    FROM #JobsToProcess 
    WHERE RowIndex = @i 

    SET @qualifiedTableName = '[Database_Staging].[dbo].['+@currentTable+']'

    SET @sqlStatement = N'

        --Signal values staging table.
        SELECT svs.* INTO #sValues
        FROM '+ @qualifiedTableName +' svs
        INNER JOIN SignalMetaData smd
            ON smd.SignalId = svs.SignalId  


        INSERT INTO SignalValues SELECT * FROM #sValues

        SELECT DISTINCT SignalId INTO #uniqueIdentifiers FROM #sValues

        DELETE c FROM '+ @qualifiedTableName +' c INNER JOIN #uniqueIdentifiers u ON c.SignalId = u.SignalId

        DROP TABLE #sValues
        DROP TABLE #uniqueIdentifiers

        IF NOT EXISTS (SELECT TOP 1 1 FROM '+ @qualifiedTableName +') --table is empty
        BEGIN
            -- processing is completed so drop the table and remvoe the entry
            IF @processingComplete = 1 
            BEGIN 
                DELETE FROM SignalValueJobs WHERE JobId = @currentJob

                IF '''+@currentTable+''' <> ''SignalValues_staging''
                BEGIN
                    DROP TABLE '+ @qualifiedTableName +'
                END
            END
        END 
    '

    EXEC sp_executesql @sqlStatement, @paramDefinitions, @currentJob = @jobParam, @processingComplete = @processingParam;

    SET @i = @i - 1
END

DROP TABLE #JobsToProcess

내가 사용하는 sp_executesql스테이징 테이블에 대한 테이블 이름은 작업 테이블의 레코드에서 텍스트로 제공하기 때문이다.

이 저장 프로시 저는 이 dba.stackexchange.com post 에서 배운 트릭을 사용하여 2 초마다 실행됩니다 .

내가 평생 해결할 수없는 문제는 생산에 대한 인서트가 수행되는 속도입니다. 내 응용 프로그램은 임시 준비 테이블을 만들어 매우 빠르게 레코드로 채 웁니다. 프로덕션에 대한 삽입은 테이블의 양을 따라갈 수 없으며 결국 수천의 테이블에 잉여 테이블이 있습니다. 단지 내가 들어오는 데이터를 유지할 수 있었던 방법은 생산에 ... 모든 키, 인덱스, 제약 조건 등을 제거하는 것입니다 SignalValues테이블. 내가 직면 한 문제는 테이블이 너무 많은 레코드로 끝나서 쿼리 할 수 ​​없게된다는 것입니다.

[Timestamp]분할 열로 사용하여 테이블을 분할하려고했지만 아무 소용이 없습니다. 인덱싱의 모든 형태는 인서트를 너무 느리게 유지하여 유지할 수 없습니다. 또한 몇 년 전에 수천 분의 파티션 (1 분에 1 시간?)을 만들어야합니다. 나는 그것을 즉시 만드는 방법을 알 수 없었다

I 불리는 테이블에 계산 된 열에 추가하여 분할 만드는 시도 TimestampMinute값이고, 온 INSERT, DATEPART(MINUTE, GETUTCDATE()). 여전히 너무 느립니다.

이 Microsoft 기사에 따라 메모리 최적화 테이블로 만들려고했습니다 . 어쩌면 나는 그것을하는 방법을 이해하지 못하지만 MOT는 어떻게 든 삽입물을 느리게 만들었습니다.

저장 프로 시저의 실행 계획을 확인한 결과 가장 집중적 인 작업은 다음과 같습니다.

SELECT svs.* INTO #sValues
FROM '+ @qualifiedTableName +' svs
INNER JOIN SignalMetaData smd
    ON smd.SignalId = svs.SignalId

나에게 이것은 말이되지 않습니다 : 나는 다른 방법으로 입증 된 저장 프로 시저에 벽 시계 로깅을 추가했습니다.

시간 기록과 관련하여 위의 특정 명령문은 100k 레코드에서 ~ 300ms로 실행됩니다.

진술

INSERT INTO SignalValues SELECT * FROM #sValues

100k 레코드에서 2500-3000ms로 실행됩니다. 다음에 따라 영향을받은 레코드를 테이블에서 삭제합니다.

DELETE c FROM '+ @qualifiedTableName +' c INNER JOIN #uniqueIdentifiers u ON c.SignalId = u.SignalId

또 다른 300ms가 걸립니다.

어떻게하면 더 빨리 할 수 ​​있습니까? SQL Server는 매일 수십억 개의 레코드를 처리 할 수 ​​있습니까?

관련이있는 경우 이는 SQL Server 2014 Enterprise x64입니다.

하드웨어 구성 :

이 질문의 첫 번째 단계에서 하드웨어를 포함하는 것을 잊었습니다. 내 잘못이야.

다음과 같이이 머리말을 붙 입니다. 하드웨어 구성으로 인해 성능이 저하되는 것을 알고 있습니다. 나는 여러 번 시도했지만 예산, C 레벨, 행성의 정렬 등으로 인해 불행히도 더 나은 설정을하기 위해 할 수있는 일은 없습니다. 서버가 가상 머신에서 실행 중이며 더 이상 메모리가 없기 때문에 메모리를 늘릴 수도 없습니다.

내 시스템 정보는 다음과 같습니다.

시스템 정보

스토리지는 iSCSI 인터페이스를 통해 VM 서버에 NAS 상자에 연결됩니다 (이로 인해 성능이 저하됩니다). NAS 상자에는 RAID 10 구성에 4 개의 드라이브가 있습니다. 6GB / s SATA 인터페이스를 갖춘 4TB WD WD4000FYYZ 회전 디스크 드라이브입니다. 서버에는 하나의 데이터 저장소 만 구성되어 tempdb와 내 데이터베이스가 동일한 데이터 저장소에 있습니다.

최대 DOP는 0입니다. 이것을 상수 값으로 변경하거나 SQL Server가 처리하도록해야합니까? RCSI에 대해 읽었습니다. RCSI의 유일한 이점이 행 업데이트와 함께 제공된다고 가정하면 정확합니까? 이러한 특정 기록의 업데이트가 없을 것, 그들은됩니다 INSERT에드와 SELECT에드. RCSI가 여전히 나에게 도움이됩니까?

내 tempdb는 8mb입니다. jyao의 아래 답변에 따라 tempdb를 완전히 피하기 위해 #sValues를 일반 테이블로 변경했습니다. 그래도 성능은 거의 동일했습니다. tempdb의 크기와 성장을 늘리려 고 노력하지만 #sValues의 크기가 항상 거의 같은 크기가 될 것이므로 많은 이득을 기대하지는 않습니다.

아래 첨부 된 실행 계획을 세웠습니다. 이 실행 계획은 준비 테이블 (100k 레코드)의 반복입니다. 쿼리 실행은 2 초 정도 걸리지 만 상당히 빠르지 만 SignalValues테이블에 인덱스가없고 SignalValues테이블의 대상인 테이블에 INSERT레코드가 없습니다.

실행 계획


3
이미 지연된 내구성을 실험 했습니까?
Martin Smith

2
생산 인서트가 느린 인덱스는 무엇입니까?
paparazzo

지금까지 실제로 많은 시간을 소비하는 것을 찾기에 충분한 데이터가 없다고 생각합니다. CPU입니까? IO입니까? 초당 30k 행을 얻는 것처럼 보이므로 IO처럼 보이지 않습니다. 나는 당신이 당신의 perf 목표를 달성하는 데 아주 가깝다는 것을 이해하고 있습니까? 초당 50k 개의 행이 필요하므로 2 초마다 100k 개의 배치 하나면 충분합니다. 현재 한 배치에는 3 초가 소요되는 것 같습니다. 하나의 대표 실행에 대한 실제 실행 계획을 게시합니다. 가장 많은 시간이 걸리는 작업을 공격하지 않는 제안은 무례합니다.
usr

집행 계획을 게시했습니다.
Brandon

답변:


7

나는 최고점에서 평균 레코드 양이 하루에 약 3-4 억 (20 시간의 작동 시간) 정도 어딘가에 계산했습니다.

스크린 샷에서 8GB 메모리 총 RAM과 6GB 만 SQL Server에 할당되어 있습니다. 이것은 당신이 달성하려고하는 것에 너무 낮습니다.

메모리를 더 높은 값인 256GB로 업그레이드하고 VM CPU를 강화하는 것이 좋습니다.

이 시점에서 워크로드를 위해 하드웨어에 투자해야합니다.

또한 데이터로드 성능 안내서를 참조하십시오. 데이터 를 효율적으로로드하는 현명한 방법에 대해 설명합니다.

내 tempdb는 8mb입니다.

편집 한 내용에 따라 .. TF 1117 및 1118을 사용하는 인스턴스 전체와 동일한 크기의 합리적인 tempdb-바람직하게는 여러 tempdb 데이터 파일이 있어야합니다.

전문적인 건강 검진을 받고 거기서부터 시작하는 것이 좋습니다.

매우 추천하는

  1. 서버 사양을 향상 시키십시오.

  2. 전문가 *가 데이터베이스 서버 인스턴스 상태를 점검하고 권장 사항을 따르십시오.

  3. 한 번 그리고 b. 그런 다음 쿼리 튜닝 및 대기 통계, 쿼리 계획 등의 기타 최적화에 몰두하십시오.

참고 : 나는 오전 전문 SQL 서버 전문가hackhands.com - 인 Pluralsight 회사를,하지만 수단에 도움을 나를 고용을 제안. 편집 한 내용 만 바탕으로 전문가의 도움을받을 것을 제안합니다.

HTH.


더 많은 하드웨어를 제안 (읽기 : 구걸)하려고합니다. 그것을 염두에두고 여기에 당신의 대답을, SQL Server 구성 또는 쿼리 최적화 관점에서 이것을 더 빨리 만들 것을 제안하는 것이 있습니까?
Brandon

1

벽을 향하고 아무것도 작동하지 않을 때 빅 데이터와 같은 문제에 대한 일반적인 조언 :

달걀 한 개를 5 분 정도 요리합니다. 전기와 물이 충분하면 10 개의 계란이 동시에 요리됩니다.

또는 다른 말로하면 :

먼저 하드웨어를 살펴보십시오. 둘째, 프로세스 로직 (데이터 리모델링)을 분리하고 병렬로 수행하십시오.

테이블 수 및 테이블 크기별로 동적 및 자동화 된 사용자 정의 수직 파티셔닝을 작성할 수 있습니다. Quarter_1_2017, Quarter_2_2017, Quarter_3_2017, Quarter_4_2017, Quarter_1_2018 ...을 보유하고 있고 내 레코드의 위치와 파티션 수를 모르는 경우, 동시에 모든 사용자 정의 파티션에 대해 동일한 쿼리를 실행하여 별도의 세션 및 어셈블리 결과는 내 논리를 위해 처리됩니다.


OP의 문제는 몇 주 또는 몇 달 전의 데이터를 처리하는 것 이상으로 새로 입력 한 데이터의 삽입 및 액세스를 처리하는 것으로 보입니다. OP는 타임 스탬프에서 분 단위로 데이터 분할을 언급합니다 (따라서 60 개의 분할, 현재 데이터를 별도의 버킷으로 분할). 분기별로 나누는 것이 큰 도움이되지 않을 것입니다. 당신의 요점은 일반적으로 잘 받아 들여지지 만,이 특정한 상황에서 누군가를 도울 수는 없습니다.
RDFozz

-1

나는 다음 점검 / 최적화를 할 것입니다 :

  1. 삽입 조작 중에 프로덕션 데이터베이스의 데이터 및 로그 파일이 모두 증가하지 않도록하십시오 (필요한 경우 사전 증가)

  2. 사용하지 마세요

    select * into [dest table] from [source table];

    대신 [대상 테이블]을 미리 정의하십시오. 또한 [대상 테이블]을 삭제하고 다시 만드는 대신 테이블을 자릅니다. 필요한 경우 임시 테이블을 사용하는 대신 일반 테이블을 사용합니다. (또한 조인 쿼리의 성능을 향상시키기 위해 [대상 테이블]에 인덱스를 만들 수도 있습니다)

  3. 동적 SQL을 사용하는 대신 코딩 논리가있는 하드 코딩 된 테이블 이름을 사용하여 작동 할 테이블을 선택하고 싶습니다.

  4. 또한 메모리, CPU 및 디스크 I / O 성능을 모니터링하여 큰 워크로드 중에 리소스 부족이 있는지 확인합니다.

  5. 프로덕션 측에서 인덱스를 삭제하여 삽입을 처리 할 수 ​​있다고 언급 했으므로 페이지 분할이 많이 발생하는지 확인하고, 그렇다면 인덱스의 채우기 비율을 줄이고 드롭을 고려하기 전에 인덱스를 다시 작성합니다. 색인.

행운을 빌어 질문을 사랑하십시오.


답변 해주셔서 감사합니다. 데이터베이스 크기를 1gb로 설정하고 성장 작업에 다소 시간이 걸릴 것으로 예상하여 1gb 증가하여 초기 속도에 도움이되었습니다. 나는 오늘 사전 성장을 구현하려고 노력할 것입니다. [dest] 테이블을 일반 테이블로 구현했지만 성능이 크게 향상되지는 않았습니다. 나는 지난 몇 일 동안 많은 시간을 보지 못했지만 오늘 다른 시간에 가려고 노력할 것입니다.
Brandon
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.