インメモリテーブルのパフォーマンスがディスクベースのテーブルよりも悪い


10

SQL Server 2014に次のようなテーブルがあります。

CREATE TABLE dbo.MyTable
(
[id1] [bigint] NOT NULL,
[id2] [bigint] NOT NULL,
[col1] [int] NOT NULL default(0),
[col2] [int] NOT NULL default(0)
)

(id1、id2)はPKです。基本的に、id1は結果のセット(id2、col1、col2)をグループ化する識別子であり、pkはid2です。

私のボトルネックである既存のディスクベースのテーブルを取り除くために、メモリ内のテーブルを使用しようとしています。

  • テーブル内のデータが書き込まれます->読み取り->一度削除されます。
  • 各id1値には、数千(数十/数十万)のid2があります。
  • データは、非常に短時間、たとえば20秒の間、テーブルに保存されます。

このテーブルで実行されるクエリは次のとおりです。

-- INSERT (can vary from 10s to 10,000s of records):
INSERT INTO MyTable
  SELECT @fixedValue, id2, col1, col2 FROM AnotherTable

-- READ:
SELECT id2, col1
FROM MyTable INNER JOIN OtherTbl ON MyTable.id2 = OtherTbl.pk
WHERE id1 = @value
ORDER BY col1

-- DELETE:
DELETE FROM MyTable WHERE id1 = @value

これが、テーブルに使用した現在の定義です。

CREATE TABLE dbo.SearchItems
(
  [id1] [bigint] NOT NULL,
  [id2] [bigint] NOT NULL,
  [col1] [int] NOT NULL default(0),
  [col2] [int] NOT NULL default(0)

  CONSTRAINT PK_Mem PRIMARY KEY NONCLUSTERED (id1,id2),
  INDEX idx_Mem HASH (id1,id2) WITH (BUCKET_COUNT = 131072)
) WITH (MEMORY_OPTIMIZED = ON, DURABILITY = SCHEMA_ONLY)

残念ながら、この定義では、ディスクベースのテーブルに関する以前の状況と比較してパフォーマンスが低下します。大きさの順序は多かれ少なかれ10%高くなります(場合によっては100%に達するため、2倍の時間になります)。

何よりも、マイクロソフトがアドバタイズしたロックフリーアーキテクチャを考えると、同時実行性の高いシナリオで非常に有利になると期待していました。代わりに、最悪のパフォーマンスは、テーブルに対して複数のクエリを実行している複数の同時ユーザーがいる場合です。

質問:

  • 設定する正しいBUCKET_COUNTはいくつですか?
  • どのようなインデックスを使用する必要がありますか?
  • なぜディスクベースのテーブルよりもパフォーマンスが悪いのですか?

sys.dm_db_xtp_hash_index_statsのクエリは次を返します:

total_bucket_count = 131072
empty_bucket_count = 0
avg_chain_len = 873
max_chain_length = 1009

sys.dm_db_xtp_hash_index_statsからの出力が次のようになるようにバケット数を変更しました。

total_bucket_count = 134217728
empty_bucket_count = 131664087
avg_chain_len = 1
max_chain_length = 3

それでも、結果は悪くはないとしても、ほとんど同じです。


パラメータスニッフィングに遭遇していないのですか?でクエリを実行してみましたかOPTION(OPTIMIZE FOR UNKNOWN)表のヒントを参照)?
TT。

私はあなたが行チェーンの問題に遭遇していると思います。の出力を教えていただけますselect * from sys.dm_db_xtp_hash_index_stats か?また、このリンクは、あなたの質問のすべて/ほとんどに答える必要があります。msdn.microsoft.com/en-us/library/...
ショーンGallardy

4
ハッシュインデックスは、含まれている両方の列の述語にのみ役立ちます。テーブルのハッシュインデックスなしで試しましたか?
ミカエルエリクソン

インメモリテクノロジーによる最高のパフォーマンス向上は、ネイティブにコンパイルされたストアドプロシージャを使用してのみ達成できることがわかりました。
Daniel Hutmacher

@DanielHutmacher FWIWラッチを削除し、ネイティブにコンパイルされたプロシージャを追加することの利点がすべてゼロまたは無視できるほどの改善である反例を見たことがあります。包括的陳述の余地はないと思います(この場合はあなたが正しいかもしれませんが、私は詳細を見さえしていません)。
アーロンバートランド

回答:


7

情報が不足しているため、この投稿は完全な答えにはなりませんが、適切な方向性を示すことができるか、または後でコミュニティと共有できる洞察を得ることができるはずです。

残念ながら、この定義では、ディスクベースのテーブルに関する以前の状況と比較してパフォーマンスが低下します。大きさの順序は多かれ少なかれ10%高くなります(場合によっては100%に達するため、2倍の時間になります)。

何よりも、マイクロソフトがアドバタイズしたロックフリーアーキテクチャを考えると、同時実行性の高いシナリオで非常に有利になると期待していました。代わりに、最悪のパフォーマンスは、テーブルに対して複数のクエリを実行している複数の同時ユーザーがいる場合です。

それは間違いなくそうであるべきではないので、これは厄介です。特定のワークロードはメモリ内テーブル(SQL 2014)向けではなく、一部のワークロードはそれに適しています。ほとんどの状況では、適切なインデックスを移行して選択するだけで、パフォーマンスの低下を最小限に抑えることができます。

もともと私はこれに関するあなたの質問について非常に狭く考えていました:

質問:

  • 設定する正しいBUCKET_COUNTはいくつですか?
  • どのようなインデックスを使用する必要がありますか?
  • なぜディスクベースのテーブルよりもパフォーマンスが悪いのですか?

最初は、メモリ内の実際のテーブルとインデックスが最適でないことに問題があると思っていました。メモリ最適化されたハッシュインデックス定義にはいくつかの問題がありますが、実際の問題は使用されたクエリにあると考えています。

-- INSERT (can vary from 10s to 10,000s of records):
INSERT INTO MyTable
  SELECT @fixedValue, id2, col1, col2 FROM AnotherTable

この挿入は、メモリ内のテーブルのみを含む場合、非常に高速である必要があります。ただし、これにはディスクベースのテーブルも含まれ、それに関連するすべてのロックとブロックの影響を受けます。したがって、ここでのリアルタイムの無駄はディスクベースのテーブルにあります。

データをメモリに読み込んだ後、ディスクベースのテーブルから100,000行の挿入に対して簡単なテストを行ったところ、応答時間は1秒未満でした。ただし、ほとんどのデータは、20秒未満の非常に短い時間しか保持されません。これは、実際にキャッシュに住んでいる時間を与えません。さらに、AnotherTable実際の大きさが不明で、値がディスクから読み取られているかどうかがわかりません。私たちはこれらの答えをあなたに頼らなければなりません。

選択クエリの場合:

SELECT id2, col1
FROM MyTable INNER JOIN OtherTbl ON MyTable.id2 = OtherTbl.pk
WHERE id1 = @value
ORDER BY col1

繰り返しますが、我々は相互運用とディスクベースのテーブルのパフォーマンスに翻弄されています。さらに、ソートはHASHインデックスでは安価ではないため、非クラスター化インデックスを使用する必要があります。これは、コメントでリンクした索引ガイドで呼び出されています。

実際の調査に基づいた事実を示すために、実際のサイズや統計情報がわからなかったためSearchItems、メモリ内テーブルに1000万行、AnotherTable100,000 行をロードしました。次に、上記の選択クエリを使用して実行しました。さらに、wait_completedで拡張イベントセッションを作成し、それをリングバッファーに入れました。それは各実行の後に掃除されました。またDBCC DROPCLEANBUFFERSすべてのデータがメモリに常駐していない可能性がある環境をシミュレートするために実行しました。

それらを真空で見ると、結果は目を見張るものはありませんでした。これをテストしているラップトップはより高いグレードのSSDを使用しているため、使用しているVMのディスクベースのパフォーマンスを人為的に下げました。

結果は、メモリ内ベーステーブルのみでクエリを5回実行した後、待機情報なしで発生しました(結合を削除し、サブクエリを削除しました)。これは予想通りです。

ただし、元のクエリを使用した場合、待機がありました。この場合、データがディスクから読み取られているので意味のあるのはPAGEIOLATCH_SHでした。私はこのシステムの唯一のユーザーであり、結合されたテーブルに対する挿入、更新、削除のための大規模なテスト環境を作成するのに時間を費やしなかったので、ロックやブロックが有効になるとは思いませんでした。

この場合も、時間のかなりの部分がディスクベースのテーブルに費やされました。

最後に、クエリを削除します。ID1のみに基づいて行を検索することは、hasインデックスでは非常に効率的ではありません。等価述語がハッシュインデックスに適していることは事実ですが、データが分類されるバケットは、ハッシュされた列全体に基づいています。したがって、id1 = 1、id2 = 2、およびid1 = 1、id2 = 3のid1、id2は、ハッシュが(1,2)と(1,3)にまたがるため、異なるバケットにハッシュされます。ハッシュインデックスは同じように構造化されていないため、これは単純なBツリー範囲スキャンではありません。次に、これがこの操作の理想的なインデックスではないことを期待しますが、経験したように、桁違いに長くかかることはないと思います。これについてwait_infoを見てみたいと思います。

何よりも、マイクロソフトがアドバタイズしたロックフリーアーキテクチャを考えると、同時実行性の高いシナリオで非常に有利になると期待していました。代わりに、最悪のパフォーマンスは、テーブルに対して複数のクエリを実行している複数の同時ユーザーがいる場合です。

論理的な一貫性のためにロックが使用されることは事実ですが、操作は依然としてアトミックである必要があります。これは、特別なCPUベースの比較演算子を介して行われます(これが、インメモリが特定の[過去4年間に作成されたほとんどすべてのCPUであるにもかかわらず]プロセッサでのみ機能する理由です)。したがって、すべてを無料で入手できるわけではありません。これらの操作を完了するにはまだ時間がかかります。

もう1つの重要な点は、ほとんどすべてのクエリで使用されるインターフェイスがT-SQL(ネイティブにコンパイルされたSPROCではない)であり、すべてが少なくとも1つのディスクベースのテーブルにアクセスするということです。これが、ディスクベースのテーブルのパフォーマンスにまだ制約があるため、結局のところ、パフォーマンスが向上しているわけではないと私が思う理由です。

ファローアップ:

  1. wait_completedの拡張イベントセッションを作成し、既知のSPIDを指定します。クエリを実行して出力を提供するか、内部で使用します。

  2. #1の出力の更新をお知らせください。

  3. ハッシュインデックスのバケット数を決定するためのマジックナンバーはありません。基本的に、バケットが完全にいっぱいにならず、行チェーンが3または4未満に留まる限り、パフォーマンスは許容範囲に留まるはずです。これは、「ログファイルを何に設定すればよいですか」と尋ねるようなものです。-プロセスごと、データベースごと、使用タイプごとに異なります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.