問題
この記事では、テーブルのパーティション分割のデモンストレーションに焦点を当てます。テーブルのパーティション分割の最も簡単な説明は、大きなテーブルを小さなテーブルに分割することと呼ぶことができます。このトピックは、スケーラビリティと管理性を提供します。
SQL Serverのテーブルパーティショニングとは何ですか?
テーブルがあり、それが日々成長するとします。この場合、テーブルによっていくつかの問題が発生する可能性があり、以下に定義する手順で解決する必要があります。
- このテーブルを維持します。時間がかかり、より多くのリソース(CPU、IOなど)を消費します。
- バックアップします。
- ロックの問題。
上記の理由により、テーブルのパーティション分割が必要です。このアプローチには次の利点があります。
- 管理機能:テーブルを分割すると、テーブルの各パーティションを管理できます。たとえば、テーブルのパーティションは1つしか作成できません。
- アーカイブ機能:テーブルの一部のパーティションは、この理由でのみ使用されます。テーブルのこのパーティションをバックアップする必要はありません。ファイルグループのバックアップを使用して、テーブルのパーティションを変更するだけでバックアップできます。
- クエリのパフォーマンス:SQLServerクエリオプティマイザーはパーティションの削除を使用することを決定します。これは、SQLServerがテーブルの無関係なパーティションを検索しないことを意味します。
SQLServerでの垂直および水平テーブルのパーティション分割
テーブルのパーティション分割は一般的な概念です。特定の場合に機能するいくつかのパーティショニングタイプがあります。最も重要で広く使用されているのは、垂直分割と水平分割の2つのアプローチです。
各タイプの特異性は、列と行で構成される構造としてのテーブルの本質を反映しています。
•垂直分割は、テーブルを列に分割します。
•水平分割は、テーブルを行に分割します。
最も一般的な例を分割する垂直テーブルは、名前、電子メール、電話番号、住所、誕生日、職業、給与、およびその他の必要なすべての情報などの詳細を含む従業員のテーブルです。そのようなデータの一部は機密です。さらに、ほとんどの場合、オペレーターは名前やメールアドレスなどの基本的なデータのみを必要とします。
垂直分割により、必要なデータが手元にあるいくつかの「より狭い」テーブルが作成されます。クエリは特定の部分のみを対象としています。このようにして、企業は負荷を軽減し、タスクを加速し、機密データが公開されないようにします。
水平方向のテーブル分割により、1つの一般的なテーブルがいくつかの小さなテーブルに分割されます。各パーティクルテーブルの列数は同じですが、行数は少なくなります。これは、時系列データを含む過剰なテーブルの標準的なアプローチです。
たとえば、年間のデータを含むテーブルを、月または週ごとに小さなセクションに分割できます。次に、クエリは1つの特定の小さなテーブルのみに関係します。水平分割により、データボリュームの拡張性が向上します。パーティション化されたテーブルは小さくなり、処理が簡単になります。
SQLサーバーでのテーブルのパーティション分割は、慎重に検討する必要があります。複数のパーティションテーブルから一度にデータをリクエストする必要がある場合は、クエリでJOINが必要になります。その上、垂直分割は依然として大きなテーブルをもたらす可能性があり、それらをさらに分割する必要があります。あなたの仕事では、あなたはあなたの特定のビジネス目的のための決定に頼るべきです。
SQL Serverでのテーブルのパーティション分割の概念が明確になったので、次はデモに進みます。
T-SQLスクリプトを回避し、SQLServerパーティション分割ウィザードを使用してすべてのテーブルパーティション手順を処理します。
SQLデータベースパーティションを作成するには何が必要ですか?
- WideWorldImportersサンプルデータベース
- SQL Server 2017 Developer Edition
次の画像は、テーブルパーティションの設計方法を示しています。年ごとにテーブルパーティションを作成し、さまざまなファイルグループを見つけます。
このステップでは、2つのファイルグループ(FG_2013、FG_2014)を作成します。データベースを右クリックし、[ファイルグループ]タブをクリックします。
次に、ファイルグループを新しいPDFファイルに接続します。
データベースストレージ構造は、テーブルのパーティション分割の準備ができています。パーティション化するテーブルを見つけて、パーティションの作成ウィザードを開始します。
以下のスクリーンショットでは、パーティション関数を適用する列を選択します。選択した列は「InvoiceDate」です。
次の2つの画面で、パーティション関数とパーティションスキームに名前を付けます。
パーティション関数は、InvoiceDate列に基づいて[Sales]。[Invoices]行のパーティションを作成する方法を定義します。
パーティションスキームは、Sales.Invoices行のファイルグループへのマップを定義します。
パーティションをファイルグループに割り当て、境界を設定します。
左/右境界は、左または右の各境界値間隔の辺を定義します。このように境界を設定し、[ストレージの見積もり]をクリックします。このオプションは、境界に配置される行数に関する情報を提供します。
最後に、[すぐに実行]を選択して、[次へ]をクリックします。
操作が成功したら、[閉じる]をクリックします。
ご覧のとおり、Sales.Invoicesテーブルは分割されています。このクエリは、パーティション化されたテーブルの詳細を表示します。
SELECT
OBJECT_SCHEMA_NAME(pstats.object_id) AS SchemaName
,OBJECT_NAME(pstats.object_id) AS TableName
,ps.name AS PartitionSchemeName
,ds.name AS PartitionFilegroupName
,pf.name AS PartitionFunctionName
,CASE pf.boundary_value_on_right WHEN 0 THEN 'Range Left' ELSE 'Range Right' END AS PartitionFunctionRange
,CASE pf.boundary_value_on_right WHEN 0 THEN 'Upper Boundary' ELSE 'Lower Boundary' END AS PartitionBoundary
,prv.value AS PartitionBoundaryValue
,c.name AS PartitionKey
,CASE
WHEN pf.boundary_value_on_right = 0
THEN c.name + ' > ' + CAST(ISNULL(LAG(prv.value) OVER(PARTITION BY pstats.object_id ORDER BY pstats.object_id, pstats.partition_number), 'Infinity') AS VARCHAR(100)) + ' and ' + c.name + ' <= ' + CAST(ISNULL(prv.value, 'Infinity') AS VARCHAR(100))
ELSE c.name + ' >= ' + CAST(ISNULL(prv.value, 'Infinity') AS VARCHAR(100)) + ' and ' + c.name + ' < ' + CAST(ISNULL(LEAD(prv.value) OVER(PARTITION BY pstats.object_id ORDER BY pstats.object_id, pstats.partition_number), 'Infinity') AS VARCHAR(100))
END AS PartitionRange
,pstats.partition_number AS PartitionNumber
,pstats.row_count AS PartitionRowCount
,p.data_compression_desc AS DataCompression
FROM sys.dm_db_partition_stats AS pstats
INNER JOIN sys.partitions AS p ON pstats.partition_id = p.partition_id
INNER JOIN sys.destination_data_spaces AS dds ON pstats.partition_number = dds.destination_id
INNER JOIN sys.data_spaces AS ds ON dds.data_space_id = ds.data_space_id
INNER JOIN sys.partition_schemes AS ps ON dds.partition_scheme_id = ps.data_space_id
INNER JOIN sys.partition_functions AS pf ON ps.function_id = pf.function_id
INNER JOIN sys.indexes AS i ON pstats.object_id = i.object_id AND pstats.index_id = i.index_id AND dds.partition_scheme_id = i.data_space_id AND i.type <= 1 /* Heap or Clustered Index */
INNER JOIN sys.index_columns AS ic ON i.index_id = ic.index_id AND i.object_id = ic.object_id AND ic.partition_ordinal > 0
INNER JOIN sys.columns AS c ON pstats.object_id = c.object_id AND ic.column_id = c.column_id
LEFT JOIN sys.partition_range_values AS prv ON pf.function_id = prv.function_id AND pstats.partition_number = (CASE pf.boundary_value_on_right WHEN 0 THEN prv.boundary_id ELSE (prv.boundary_id+1) END)
WHERE pstats.object_id = OBJECT_ID('Sales.Invoices')
ORDER BY TableName, PartitionNumber;
MS SQLServerのパーティショニングパフォーマンス
同じテーブルのパーティション化されたテーブルとパーティション化されていないテーブルのパフォーマンスを比較します。これを行うには、以下のクエリを使用して、[実際の実行プランを含める]をアクティブにします。
DECLARE @Dt as date = '20131231'
SELECT COUNT(InvoiceDate)
FROM [Sales].[Invoices]
where InvoiceDate < @Dt
実行プランを調べると、「Partitioned」、「Actual Partition Count」、および「ActualPartitionedAccessed」プロパティが含まれていることがわかります。
パーティション化されたプロパティ このテーブルでパーティションが有効になっていることを示します。
実際のパーティション数 プロパティは、SQLServerエンジンによって読み取られるパーティションの総数です。
実際のパーティションアクセス プロパティは、SQLServerエンジンによって評価されるパーティション番号です。 SQL Serverは、パーティションの削除と呼ばれるため、他のパーティションへのアクセスを削除し、クエリのパフォーマンスを向上させます。
次に、パーティション化されていないテーブル実行プランを見てください。
これら2つの実行プランの主な違いは、読み取られた行数です。 このプロパティは、このクエリで読み取られる行数を示すためです。以下の圧縮チャートからわかるように、パーティション化されたテーブルの値が低すぎます。このため、消費するIOは低くなります。
次に、クエリを実行して実行プランを調べます。
DECLARE @DtBeg as date = '20140502'
DECLARE @DtEnd as date = '20140701'
SELECT COUNT(InvoiceDate)
FROM [Sales].[Invoices]
where InvoiceDate between @DtBeg and @DtEnd
パーティションレベルのロックエスカレーション
ロックエスカレーションは、SQL ServerLockManagerで使用されるメカニズムです。オブジェクトのレベルをロックするように調整します。ロックする行数が増えると、ロックマネージャはロックオブジェクトを変更します。これは、ロックエスカレーションの階層レベル「行->ページ->テーブル->データベース」です。ただし、パーティションテーブルでは、同時実行性とパフォーマンスが向上するため、1つのパーティションをロックできます。ロックエスカレーションのデフォルトレベルは、SQLServerでは「TABLE」です。
以下のUPDATEステートメントを使用してクエリを実行します。
BEGIN TRAN
DECLARE @Dt as date = '20131221'
UPDATE [Sales].[Invoices] SET CreditNoteReason = 'xxx' where InvoiceDate < @Dt
SP_LOCK
赤いボックスは、同じリソースに対して同時に複数の更新を行うことができないようにする排他ロックを定義します。請求書テーブルで発生します。
次に、Sales.Invoicesテーブルのエスカレーションモードを設定して自動化し、クエリを再実行します。
ALTER TABLE Sales.Invoices SET (LOCK_ESCALATION = AUTO)
ここで、赤いボックスは、階層の下位にある一部のリソースで要求または取得された排他ロックを保護するインデント排他ロックを定義します。間もなく、このロックレベルにより、テーブルの他のパーティションを更新または削除できます。つまり、別の更新を開始したり、テーブルの他のパーティションを挿入したりできます。
以前の投稿では、テーブルパーティションの切り替えの問題についても説明し、ウォークスルーを提供しました。これらのケースに対処する場合、この情報が役立つ場合があります。詳細については、記事を参照してください。
参照
- ロックモード
- パーティション化されたテーブルとインデックス