web-dev-qa-db-ja.com

クラスター化列ストアインデックスのパフォーマンスSQL Server 2014

SQL Server 2014を使用してOLAPデータベースを設定しています。コアファクトテーブルは約40,000,000行、225列で、平均行サイズは181バイトです。私は、クラスター化された列ストアインデックスは、あまり運がよかったのですが、一般的に、新しいテクノロジではクエリのパフォーマンスが4倍以上遅くなります。

1つの特定の例-int32主キーを使用して単一の行を選択するのに12秒かかります...これは行ストアテーブルに対する1秒未満の操作です(もちろん、PKに一意のインデックスがあり、これと組み合わせて使用​​することはできません)クラスター化列ストアインデックス)。

私は私が間違っていることを理解しようとしています-MSドキュメントから、これはこのタスクに理想的なテクノロジーのようです。多分私は何かが欠けています。

128 GB RAMおよびデータストレージ用SSDを搭載したWindows 8.1 64ビットでSQL 2014 Enterpriseを実行しています。データはこのアプリでのみ読み取り可能です。

6

使用している特定のデータとクエリを投稿できる場合は、おそらくそれが、特定のケースのコンテキストで質問に回答できる唯一の方法です。実際の例とほぼ同じスケールで匿名データを生成するスクリプトを使用できます。

しかし、私は先に進んで、同様の種類のスクリプトを自分で作成しました。簡単にするために、225カラム未満を使用しています。しかし、私は同じ数の行とランダムデータ(列ストアには好ましくない)を使用しているため、実際の結果とはかなり異なります。ですから、私の最初の考えは、はい、構成またはテストクエリのいずれかに何らかの問題があるということです。

重要なポイントのいくつか:

  • 列ストアは、列内のすべての行にまたがる単純な集計の場合、行ストアよりも劇的に高速です。
  • 注意深くロードすると、カラムストアはシングルトンシークで驚くほどうまく機能します。 I/Oヒットがありますが、ウォームキャッシュでのパフォーマンスは非常に良好でした。もちろん、この使用例では、行ストアほど優れていません。
  • シングルトンシークと大規模な集計クエリの両方を実行できるようにする必要がある場合は、標準のBツリーテーブルの上に非クラスター化列ストアインデックスを使用することを検討してください。
  • 列数は225ですが、平均行数は181バイトです。これは少し変わっているようです。テーブルは主にBIT列ですか?それはさらに検討する必要があるかもしれません。単純なBIT列の列ストアで非常に優れた圧縮率(99%以上)を確認しましたが、その多くは行のオーバーヘッドがないためであり、この利点は多くのBIT単一行の列。
  • 列ストアについて(もっと)学びたい場合は、Nikoの66部(および数え上げ) ブログシリーズ が私が出会った中で最も価値のあるリファレンスです。

次に、詳細に進みます。

行ストアデータセットを作成します

ここではあまりエキサイティングなことはありません。擬似ランダムデータの40MM行を作成します。

SELECT @@VERSION
--Microsoft SQL Server 2014 - 12.0.4213.0 (X64) 
--  Jun  9 2015 12:06:16 
--  Copyright (c) Microsoft Corporation
--  Developer Edition (64-bit) on Windows NT 6.1 <X64> (Build 7601: Service Pack 1)
GO

-- Create a rowstore table with 40MM rows of pseudorandom data
;WITH E1(N) AS (
    SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 
    UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
)
, E2(N) AS (SELECT 1 FROM E1 a CROSS JOIN E1 b)
, E4(N) AS (SELECT 1 FROM E2 a CROSS JOIN E2 b)
, E8(N) AS (SELECT 1 FROM E4 a CROSS JOIN E4 b)
SELECT TOP 40000000 ISNULL(ROW_NUMBER() OVER (ORDER BY (SELECT NULL)), 0) AS id
    , ISNULL((ABS(CAST(CAST(NEWID() AS VARBINARY) AS INT)) % 5) + 1, 0) AS col1
    , ISNULL(ABS(CAST(CAST(NEWID() AS VARBINARY) AS INT)) * Rand(), 0) AS col2
    , ISNULL(ABS(CAST(CAST(NEWID() AS VARBINARY) AS INT)) * Rand(), 0) AS col3
    , ISNULL(ABS(CAST(CAST(NEWID() AS VARBINARY) AS INT)) * Rand(), 0) AS col4
    , ISNULL(ABS(CAST(CAST(NEWID() AS VARBINARY) AS INT)) * Rand(), 0) AS col5
INTO dbo.test_row
FROM E8
GO
ALTER TABLE test_row
ADD CONSTRAINT PK_test_row PRIMARY KEY (id)
GO

列ストアデータセットを作成

Nikoのブログで データをロードしてセグメントを削除する に説明されている手法を使用して、CLUSTERED COLUMNSTOREと同じデータセットを作成してみましょう。

-- Create a columnstore table with the same 40MM rows
-- The data is first ordered by id and then a single thread
-- use to build the columnstore for optimal segment elimination
SELECT *
INTO dbo.test_column
FROM dbo.test_row
GO
CREATE CLUSTERED INDEX cs_test_column
ON dbo.test_column (id)
GO
CREATE CLUSTERED COLUMNSTORE INDEX cs_test_column 
ON dbo.test_column WITH (DROP_EXISTING = ON, MAXDOP = 1)
GO

サイズ比較

ランダムデータを読み込んでいるので、列ストアはテーブルサイズのわずかな削減のみを実現します。データがランダムでない場合、列ストアの圧縮により、列ストアインデックスのサイズが劇的に減少します。この特定のテストケースは実際には列ストアには非常に好ましくありませんが、少し圧縮されていることを確認することは依然として良いことです。

-- Check the sizes of the two tables
SELECT t.name, ps.row_count, (ps.reserved_page_count*8.0) / (1024.0) AS sizeMb
FROM sys.tables t WITH (NOLOCK)
JOIN sys.dm_db_partition_stats ps WITH (NOLOCK)
    ON ps.object_id = t.object_id
WHERE t.name IN ('test_row','test_column')
--name          row_count   sizeMb
--test_row      40000000    2060.6328125
--test_column   40000000    1352.2734375
GO

パフォーマンス比較

次の2つのテストケースでは、2つの非常に異なる使用例を試します。

1つ目は、質問で述べたシングルトンシークです。コメント投稿者が指摘するように、これは列ストアのユースケースではありません。列ごとにセグメント全体を読み取る必要があるため、コールドキャッシュからの読み取りの数は非常に多くなり、パフォーマンスが低下します(0ms行ストアと273ms列ストア)。ただし、列ストアはウォームキャッシュを使用して2msに達しています。シークするBツリーがないことを考えると、これは実際には非常に印象的な結果です。

2番目のテストでは、すべての行の2つの列の集計を計算します。これは、列ストアの設計目的に沿ったものであり、列ストアでは読み取りが少なく(圧縮のため、すべての列にアクセスする必要がないため)、劇的に高速なパフォーマンス(主にバッチモードの実行による)であることがわかります。コールドキャッシュから、列ストアは4s vs 15sで実行されます。ウォームキャッシュを使用した場合の違いは、282ms2.8sで1桁違います。

SET STATISTICS TIME, IO ON
GO

-- Clear cache; don't do this in production!
-- I ran this statement between each set of trials to get a fresh read
--CHECKPOINT
--DBCC DROPCLEANBUFFERS
GO

-- Trial 1: CPU time = 0 ms,  elapsed time = 0 ms.
    -- logical reads 4, physical reads 4, read-ahead reads 0
-- Trial 2: CPU time = 0 ms,  elapsed time = 0 ms
    -- logical reads 4, physical reads 0, read-ahead reads 0
SELECT *
FROM dbo.test_row
WHERE id = 12345678
GO 2
-- Trial 1: CPU time = 15 ms,  elapsed time = 273 ms..
    -- lob logical reads 9101, lob physical reads 1, lob read-ahead reads 25756
-- Trial 2: CPU time = 0 ms,  elapsed time = 2 ms.  
    -- lob logical reads 9101, lob physical reads 0, lob read-ahead reads 0
SELECT *
FROM dbo.test_column
WHERE id = 12345678
GO 2

-- Trial 1: CPU time = 8441 ms,  elapsed time = 14985 ms.
    -- logical reads 264733, physical reads 3, read-ahead reads 263720
-- Trial 2: CPU time = 9733 ms,  elapsed time = 2776 ms.
    -- logical reads 264883, physical reads 0, read-ahead reads 0
SELECT AVG(id), SUM(col3)
FROM dbo.test_row
GO 2
-- Trial 1: CPU time = 1233 ms,  elapsed time = 3992 ms.
    -- lob logical reads 207778, lob physical reads 1, lob read-ahead reads 341196
-- Trial 2: CPU time = 1030 ms,  elapsed time = 282 ms. 
    -- lob logical reads 207778, lob physical reads 0, lob read-ahead reads 0
SELECT AVG(id), SUM(col3)
FROM dbo.test_column
GO 2
7
Geoff Patterson