SQL Serverの膨大なデータとパフォーマンス


20

非常に大量のレコードを収集して保存するSQL Serverバックエンドを使用してアプリケーションを作成しました。ピーク時の平均レコード量は、1日あたり30〜40億(20時間の操作)の範囲にあると計算しました。

私の元の解決策(データの実際の計算を行う前)は、クライアントが照会する同じテーブルにアプリケーションがレコードを挿入することでした。明らかに、多くのレコードが挿入されているテーブルをクエリすることは不可能だからです。

2番目のソリューションは、2つのデータベースを使用することでした。1つはアプリケーションが受信したデータ用で、もう1つはクライアント対応データ用です。

私のアプリケーションはデータを受け取り、それを〜10万レコードのバッチにチャンクし、ステージングテーブルに一括挿入します。〜100kの記録後、アプリケーションはその場で、以前と同じスキーマで別のステージングテーブルを作成し、そのテーブルへの挿入を開始します。それは、100kレコードを持つテーブルの名前でジョブテーブルにレコードを作成し、SQL Server側のストアドプロシージャは、ステージングテーブルからクライアント対応の本番テーブルにデータを移動してから、アプリケーションによって作成された一時テーブル。

両方のデータベースには、同じスキーマを持つ5つのテーブルの同じセットがありますが、ジョブテーブルがあるステージングデータベースは例外です。ステージングデータベースには、大量のレコードが存在するテーブルに整合性の制約、キー、インデックスなどがありません。以下に示すように、テーブル名はSignalValues_stagingです。目標は、できるだけ早くデータをSQL Serverにバタンと置くことでした。簡単に移行できるようにテーブルをオンザフライで作成するワークフローは非常にうまく機能します。

以下は、ステージングデータベースからの5つの関連テーブルと、jobsテーブルです。

ステージングテーブル 私が作成したストアドプロシージャは、すべてのステージングテーブルからのデータの移動と本番環境への挿入を処理します。以下は、ステージングテーブルからプロダクションに挿入するストアドプロシージャの一部です。

-- 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の投稿から学んだトリックを使用して2秒ごとに実行されます。

私の人生で解決できない問題は、生産への挿入が実行される速度です。私のアプリケーションは、一時的なステージングテーブルを作成し、それらを非常に迅速にレコードで満たします。実稼働環境への挿入では、テーブルの量に対応できず、最終的には数千に及ぶテーブルの余剰があります。唯一私が今まで入ってくるデータについていくことができました方法は、生産上...すべてのキー、インデックス、制約などを削除することですSignalValuesテーブル。私が直面する問題は、テーブルが非常に多くのレコードで終わるため、クエリが不可能になることです。

を使用してテーブルを[Timestamp]パーティション分割列として使用してみましたが、役に立ちませんでした。どんな形の索引付けでも、挿入が非常に遅くなり、追いつかなくなります。さらに、数千年前に何千ものパーティションを作成する必要があります(1分ごとに1時間?)。それらをその場で作成する方法がわかりませんでした

TimestampMinute値がon INSERTであるテーブルに計算列を追加して、パーティションを作成しようとしましたDATEPART(MINUTE, GETUTCDATE())。まだ遅すぎる。

このMicrosoftの記事に従って、メモリ最適化テーブルにしようとしました。どうすればいいのか分からないかもしれませんが、MOTによって挿入が遅くなりました。

ストアドプロシージャの実行プランを確認しましたが、最も集中的な操作は

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

私にはこれは意味がありません。それ以外の場合に証明されたストアドプロシージャにウォールクロックロギングを追加しました。

タイムロギングに関しては、上記の特定のステートメントは、10万レコードで約300ミリ秒で実行されます。

声明

INSERT INTO SignalValues SELECT * FROM #sValues

10万レコードで2500〜3000ミリ秒で実行されます。以下から影響を受けるレコードをテーブルから削除します。

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

さらに300msかかります。

どうすればこれを速くできますか?SQL Serverは1日に数十億件のレコードを処理できますか?

関連する場合、これはSQL Server 2014 Enterprise x64です。

ハードウェア構成:

この質問の最初のパスにハードウェアを含めるのを忘れました。私の悪い。

これには、これらのステートメントを前置きします。ハードウェア構成が原因でパフォーマンスが低下していることは知っています。私は何度も試しましたが、予算、Cレベル、惑星の配置などのために、残念ながら、より良いセットアップを得るためにできることは何もありません。サーバーは仮想マシン上で実行されており、メモリが増えないため、メモリを増やすことさえできません。

システム情報は次のとおりです。

システム情報

ストレージは、NASボックスへのiSCSIインターフェイスを介してVMサーバーに接続されます(これによりパフォーマンスが低下します)。NASボックスには、RAID 10構成の4つのドライブがあります。6GB /秒のSATAインターフェースを備えた4TB WD WD4000FYYZ回転ディスクドライブです。サーバーには1つのデータストアのみが構成されているため、tempdbとデータベースは同じデータストアにあります。

最大DOPはゼロです。これを定数値に変更するか、SQL Serverに処理させるだけですか?RCSIを読みます:RCSIからの唯一の利点は行の更新にあると仮定して正しいですか?これらの特定のレコードは更新されず、INSERT編集およびSELECT編集されます。RCSIには引き続きメリットがありますか?

私のtempdbは8MBです。jyaoからの以下の回答に基づいて、#sValuesを通常のテーブルに変更して、tempdbを完全に回避しました。ただし、パフォーマンスはほぼ同じでした。tempdbのサイズと成長を増やしてみますが、#sValuesのサイズが常にほぼ同じサイズであることを考えると、あまり期待できません。

以下に添付した実行計画を取りました。この実行計画は、ステージングテーブルの1回の繰り返し、つまり100,000レコードです。クエリの実行は約2秒でかなり高速でしたが、これにはSignalValuesテーブルのインデックスがなくSignalValues、のターゲットであるテーブルにはINSERTレコードがないことに注意してください。

実行計画


3
すでに遅延耐久性を実験しましたか?
マーティンスミス

2
低速の実動挿入ではどのようなインデックスが適切でしたか?
パパラッチ

これまでのところ、実際にこれほど多くの時間を消費しているものを見つけるのに十分なデータがあるとは思いません。CPUですか?IOですか?あなたは毎秒3万行を取得しているように見えるので、私にはIOのようには見えません。あなたがパフォーマンス目標を達成するのに非常に近いというこの権利を理解していますか?1秒あたり5万行必要なので、2秒ごとに1つの100kバッチで十分です。現在、1つのバッチには3秒かかるようです。1つの代表的な実行の実際の実行計画を投稿します。最も時間のかかる操作を攻撃しないという提案は議論の余地があります。
usr

実行計画を掲載しました。
ブランドン

回答:


7

ピーク時の平均レコード量は、1日あたり30〜40億(20時間の操作)の範囲にあると計算しました。

スクリーンショットから、合計RAMが8GB、SQL Serverに6GBしか割り当てられていません。これはあなたが達成しようとしているものにとってはあまりにも低い方法です。

メモリをより高い値(256 GB)にアップグレードし、VM CPUも増やすことをお勧めします。

この時点で、ワークロードのハードウェアに投資する必要があります。

データロードパフォーマンスガイドも参照してください。データを効率的にロードするスマートな方法について説明しています。

私のtempdbは8MBです。

編集内容に基づいて、賢明なtempdbが必要です。できれば、TF 1117および1118が有効なインスタンス幅と同じサイズの複数のtempdbデータファイルが必要です。

プロの健康診断を受けて、そこから始めることをお勧めします。

強くお勧めします

  1. サーバーの仕様を強化します。

  2. 専門家*にデータベースサーバーインスタンスのヘルスチェックを行い、推奨事項に従ってください。

  3. 一度 およびb。完了したら、クエリの調整や、待機統計やクエリプランなどの最適化に没頭します。

注:私はプロのSQL Serverの専門家hackhands.com - pluralsight会社が、決してに助けのために私を雇うためにあなたを示唆しています。編集のみに基づいて専門家の助けを借りることをお勧めします。

HTH。


私は、このためのハードウェアを提案する(読む:懇願する)ハードウェアをまとめようとしています。これを念頭に置いて、ここであなたの答えを考えると、SQL Server構成またはクエリ最適化の観点から、これをより高速にすることを提案するものは他にありませんか?
ブランドン

1

ビッグデータに関するこのような問題に関する一般的なアドバイスは、壁に直面しても何も機能しない場合です:

1個の卵は約5分で調理されます。十分な電気と水があれば、10個の卵が同時に調理されます。

または、言い換えれば:

まず、ハードウェアを見てください。次に、プロセスロジックを分離し(データの再構築)、並行して実行します。

テーブル数およびテーブルサイズごとに、動的かつ自動化されたカスタムの垂直分割を作成することは非常に可能です。Quarter_1_2017、Quarter_2_2017、Quarter_3_2017、Quarter_4_2017、Quarter_1_2018があり、自分のレコードがどこにあり、どのくらいのパーティションがあるかがわからない場合、すべてのカスタムパーティションに対して同じクエリを実行します、別々のセッションとアセンブリロジック用に処理される結果。


OPの問題は、数週間または数か月前のデータを処理する以上の、新たに入力されたデータへの挿入とアクセスを処理しているようです。OPは、タイムスタンプでデータを1分ごとに分割することに言及しています(60分割、現在のデータを個別のバケットに分割)。四半期ごとに分割することはあまり役に立ちません。一般的にあなたの意見はよく理解されていますが、この特定の状況で誰かを助けることはまずありません。
RDFozz

-1

次のチェック/最適化を行います。

  1. 本番データベースのデータとログファイルの両方が挿入操作中に拡大しないことを確認します(必要に応じて事前に拡大します)

  2. 使ってはいけません

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

    ただし、代わりに[dest table]を事前定義します。また、[dest table]をドロップして再作成する代わりに、テーブルを切り捨てます。このように、必要に応じて、一時テーブルを使用する代わりに、通常のテーブルを使用します。(結合クエリのパフォーマンスを向上させるために、[dest table]にインデックスを作成することもあります)

  3. 動的SQLを使用する代わりに、ハードコーディングされたテーブル名とコーディングロジックを使用して、操作するテーブルを選択します。

  4. また、メモリ、CPU、およびディスクI / Oパフォーマンスを監視して、大きなワークロード中にリソース不足が発生しているかどうかを確認します。

  5. 実稼働側でインデックスをドロップすることで挿入を処理できると述べたので、ページ分割が多数発生しているかどうかを確認します。発生している場合は、インデックスのfillfactorを減らし、インデックスを再構築してから削除を検討しますインデックス。

頑張って、あなたの質問が大好きです。


答えてくれてありがとう。データベースサイズを1ギガバイトに設定し、成長操作に時間がかかると予想して1ギガバイト増加しました。今日、事前成長の実装を試みます。[dest]テーブルを通常のテーブルとして実装しましたが、パフォーマンスはそれほど向上しませんでした。私はここ数日あまり時間がありませんでしたが、今日は他の場所に行こうとします。
ブランドン
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.