すべてのプロダクト
Search
ドキュメントセンター

Tablestore:テーブル操作のベストプラクティス

最終更新日:Apr 16, 2025

このトピックでは、テーブル操作のベストプラクティスについて説明します。

プライマリキーの設計

Tablestore は、パーティションキーに基づいてテーブルデータをパーティションに動的に分散します。各パーティションは 1 つのサーバノードでホストされます。パーティションキーは、最小のパーティション単位として使用されます。同じパーティションキー値を持つデータは、それ以上分割することはできません。

アプリケーションは、パーティション全体でデータ分散とアクセス分散のバランスを取る必要があります。バランスが取れていない場合、同じパーティションキー値を持つデータがホットスポットになり、パーティションをホストしているサーバーのワークロードが増加する可能性があります。

Tablestore は、プライマリキーによってテーブルの行をソートします。適切に設計されたプライマリキーは、パーティション全体でデータ分散のバランスをより適切に取ることができ、Tablestore の高いスケーラビリティを最大限に活用できます。

パーティションキーを選択する際には、以下の点に注意してください。

  • テーブル内の同じパーティションキー値を持つデータのサイズは、10 GB を超えないようにすることをお勧めします。

    説明

    この推奨事項は、アクセスホットスポットを防ぐために提供されており、ストレージ使用量の制限によるものではありません。

  • 同じテーブル内の異なるパーティションキー値を持つデータは、論理的に独立しています。

  • 連続した少数のパーティションキー値にアクセス負荷を集中させないでください。

パーティションキーの設計

スプライスされたパーティションキー

Tablestore では、テーブル内の同じパーティションキー値を持つデータのサイズは、10 GB を超えないようにすることをお勧めします。テーブル内の同じパーティションキー値を持つデータのサイズが 10 GB を超える場合は、テーブルを設計する際に、複数の元のプライマリキー列をパーティションキーにスプライスできます。

これにより、同じパーティションキー値を持つデータのサイズが上限に達するのを防ぎます。パーティションキーをスプライスする際には、以下の点に注意してください。

  • スプライスするプライマリキー列を選択する際には、スプライス後に元の行が異なるパーティションキー値を持つようにしてください。

  • Integer 型のプライマリキー列をスプライスする場合は、整数を長さが固定された文字列に変換します。整数の桁数が固定長よりも少ない場合は、先行ゼロを埋め込んで、テーブルデータの順序が変わらないようにすることができます。

  • コネクタを選択する際には、新しいパーティションキーの辞書式順序への影響を考慮してください。他のすべての使用可能な文字よりも ASCII コードが小さいコネクタを選択することをお勧めします。

パーティションキーへのハッシュプレフィックスの追加

値が連続して増加するプライマリキー列をパーティションキーとして使用しないでください。値が連続して増加するプライマリキー列をパーティションキーとして使用する必要がある場合は、パーティションキーにハッシュプレフィックスを追加して、パーティション全体でランダムなデータ分散を確保し、アクセス負荷のバランスを取ることをお勧めします。

ただし、GetRange 操作を呼び出して、プライマリキー値が特定の範囲内にある論理的に隣接したデータを読み取ることはできなくなります。

データ操作

データを並列で書き込む

Tablestore テーブルは、複数の Tablestore サーバーに分散された複数のパーティションに分割されます。

プライマリキーでソートされたデータを Tablestore にバッチで順次書き込むと、書き込み負荷が特定のパーティションに集中し、他のパーティションはアイドル状態のままになります。この場合、予約済みの読み取りスループットと書き込みスループットを十分に活用できず、データのインポートが遅くなります。データのインポートを高速化するには、次のいずれかの方法を使用します。

  • 元のデータ順序を変更してから、データをインポートします。書き込まれたデータが各パーティションに均等に分散されるようにしてください。

  • 複数のワーカースレッドを使用して、データを並列でインポートします。大きなデータセットを複数の小さなデータセットに分割し、ワーカースレッドを介してランダムにインポートします。

ホットデータとコールドデータを区別する

ほとんどの場合、データは時間依存です。最近生成されたデータは、以前に生成されたデータよりもアクセスされる頻度が高くなる可能性があります。以前に生成されたデータは徐々にコールドデータになりますが、それでもストレージ容量を占有します。

テーブルに大量のコールドデータが格納されていると、パーティション全体でアクセス負荷分散のバランスが崩れます。この場合、予約済みの読み取りスループットと書き込みスループットは十分に活用されません。

この問題を解決するには、異なるテーブルを使用してコールドデータとホットデータを分離し、テーブルごとに異なる予約済みの読み取りスループットと書き込みスループットを設定します。

シナリオベースのケーススタディ

このセクションでは、特定のシナリオに基づいてテーブルを設計する方法について説明します。

シナリオ

テーブルには、大学生の消費記録が格納されます。各消費記録は、学生証を使用して学生に関連付けられます。プライマリキー列には、CardID、SellerID、DeviceID、OrderNumber 列が含まれます。CardID 列には学生証 ID、SellerID 列には販売者 ID、DeviceID 列には POS デバイス ID、OrderNumber 列には注文番号が含まれます。テーブルには、次のルールが適用されます。

  • 各学生証は 1 つの CardID に対応し、各販売者は 1 つの SellerID に対応します。

  • 各 POS デバイスは、グローバルに一意の DeviceID に対応します。

  • POS デバイスによって生成された各消費記録には、OrderNumber があります。デバイスによって生成された OrderNumber は、そのデバイスに対してのみ一意です。たとえば、異なる POS デバイスが同じ OrderNumber を使用して 2 つの異なる消費記録を生成する場合があります。

  • 同じ POS デバイスによって生成された OrderNumber は、タイムスタンプによってソートされます。新しい消費記録は、以前の消費記録よりも大きな順次 OrderNumber を持ちます。

  • すべての消費記録は、リアルタイムでテーブルに書き込まれます。

設計プロセス

  1. 適切なプライマリキー列をパーティションキーとして指定します。

    Tablestore を効率的に使用するには、テーブルのプライマリキーを設計する際にパーティションキーの設定を考慮する必要があります。次の表は、さまざまなパーティションキー設定を分析したものです。次の表に基づいて、テーブルのパーティションキーとして SellerID 列または OrderNumber 列ではなく、CardID 列または DeviceID 列を使用することをお勧めします。ビジネス要件に基づいて他のプライマリキー列を設計できます。

    パーティション分割方法

    分析

    結論

    CardID 列をテーブルのパーティションキーとして使用する

    ほとんどの場合、各カードの 1 日あたりの消費記録数は同じくらいです。その結果、各パーティションのアクセス負荷のバランスが取れます。

    CardID 列をテーブルのパーティションキーとして使用すると、予約済みの読み取りスループットと書き込みスループットをより適切に活用できます。

    CardID 列をテーブルのパーティションキーとして使用することをお勧めします。

    SellerID 列をテーブルのパーティションキーとして使用する

    大学の販売者数は比較的少なく、多くの消費記録が特定の販売者に集中する可能性があります。この場合、少数の販売者がホットスポットになります。これにより、パーティション全体でアクセス負荷分散のバランスが崩れます。

    SellerID 列をテーブルのパーティションキーとして使用しないことをお勧めします。

    DeviceID 列をテーブルのパーティションキーとして使用する

    1 日あたりの各販売者の消費記録数が異なる場合でも、1 日あたりに各 POS デバイスによって生成される消費記録数を推定できます。推定値は、レジ係員の注文処理速度に基づいて計算されます。これにより、1 日あたりに POS デバイスによって生成できる消費記録数が決まります。したがって、DeviceID 列をテーブルのパーティションキーとして使用して、パーティション全体でアクセス負荷分散のバランスを保証できます。

    DeviceID 列をテーブルのパーティションキーとして使用することをお勧めします。

    OrderNumber 列をテーブルのパーティションキーとして使用する

    OrderNumber 列には、連続して増加する注文番号が格納されます。同じ期間内に生成された注文番号は狭い範囲に収まり、特定のパーティションにクラスター化されます。この場合、予約済みの読み取りスループットと書き込みスループットは十分に活用されません。

    OrderNumber 列をパーティションキーとして使用する必要がある場合は、注文番号のハッシュ値を計算し、プレフィックスとして注文番号に追加して、パーティション全体で均等なアクセス負荷分散を確保することをお勧めします。

    OrderNumber 列をテーブルのパーティションキーとして使用しないことをお勧めします。

  2. テーブル内の同じパーティションキー値を持つデータのサイズが 10 GB を超える可能性がある場合は、テーブルを設計する際に、複数のプライマリキー列をパーティションキーにスプライスできます。

    たとえば、学生の消費記録を含むテーブルのプライマリキー列は、DeviceID、SellerID、CardID、OrderNumber 列であり、その中で DeviceID 列がテーブルのパーティションキーとして指定されています。DeviceID 列の値が 10 GB を超える可能性のある行。この場合、DeviceID、SellerID、CardID 列をパーティションキーにスプライスします。

    次の表は、元のテーブルのスキーマを示しています。

    DeviceID

    SellerID

    CardID

    OrderNumber

    attrs

    16

    'a100'

    66661

    200001

    ...

    54

    'a100'

    6777

    200003

    ...

    54

    'a1001'

    6777

    200004

    ...

    167

    'a101'

    283408

    200002

    ...

    次の表は、DeviceID、SellerID、CardID 列がパーティションキーにスプライスされた新しいテーブルのスキーマを示しています。

    CombineDeviceIDSellerIDCardID

    OrderNumber

    attrs

    '16:a100:66661'

    200001

    ...

    '167:a101:283408'

    200002

    ...

    '54:a1001:6777'

    200004

    ...

    '54:a100:6777'

    200003

    ...

    元のテーブルでは、DeviceID 列の値が 54 である 2 つの行には、同じパーティションキー値を持つ 2 つの消費記録が含まれています。新しいテーブルでは、2 つの消費記録は異なるパーティションキー値を持ちます。これにより、複数のプライマリキー列をスプライスしてパーティションキーを形成することにより、同じパーティションキー値を持つデータの総量が削減されます。

    学生の消費記録を含むテーブルでは、DeviceID 列の値が同じである 2 つの行は、SellerID 列の値も同じです。DeviceID 列と SellerID 列のみをパーティションキーにスプライスすると、同じパーティションキー値を持つデータのサイズが大きくなります。この場合、DeviceID、SellerID、CardID 列をパーティションキーにスプライスできます。

    その結果、新しい問題が発生します。たとえば、DeviceID 列は Integer 型のプライマリキー列です。元のテーブルでは、DeviceID 列の値が 54 である行は、DeviceID 列の値が 167 である行の前に表示されます。DeviceID、SellerID、CardID 列をパーティションキーにスプライスすると、DeviceID 列の値が 54 である行は、DeviceID 列の値が 167 である行の後に表示されます。この場合、アプリケーションが DeviceID 列の値が 15 以上 100 未満である行を読み取ろうとすると、新しいテーブルは要件を満たしていません。

    この問題を解決するには、DeviceID 列の整数を長さが固定された文字列に変換します。整数の桁数が固定長よりも少ない場合は、固定長に基づいて先行ゼロを埋め込むことができます。固定長は、DeviceID 列の整数の最大桁数によって異なります。たとえば、DeviceID 列の値が 0 から 999999 の範囲である場合は、整数の桁数が 6 になるまで、列の整数に先行ゼロを埋め込みます。次に、DeviceID、SellerID、CardID 列をパーティションキーにスプライスします。次の表は、スプライス後の新しいテーブルのスキーマを示しています。

    CombineDeviceIDSellerIDCardID

    OrderNumber

    attrs

    '000016:a100:66661'

    200001

    ...

    '000054:a1001:6777'

    200004

    ...

    '000054:a100:6777'

    200003

    ...

    '000167:a101:283408'

    200002

    ...

    別の問題が発生します。たとえば、DeviceID 列の値が 54 で SellerID 列の値が 'a1001' である行は、DeviceID 列の値が 54 で SellerID 列の値が 'a100' である行の後に表示されます。この不一致は、: コネクタが原因で発生します。これは辞書式順序に影響します。その結果、'000054:a1001' は辞書式に '000054:a100:' よりも小さいですが、'a1001' は辞書式に 'a100' よりも大きくなります。

    すべての使用可能な文字よりも ASCII コードが小さいコネクタを選択することをお勧めします。テーブルでは、SellerID 列の各値は文字と数字で構成されています。分析に基づいて、, は、SellerID 列の値のすべての使用可能な文字よりも ASCII コードが小さくなっています。したがって、, をコネクタとして使用できます。

    , をコネクタとして使用した後の新しいテーブルのスキーマを次の表に示します。

    CombineDeviceiDSellerIDCardID

    OrderNumber

    attrs

    '000016,a100,66661'

    200001

    ...

    '000054,a100,6777'

    200003

    ...

    '000054,a1001,6777'

    200004

    ...

    '000167,a101,283408'

    200002

    ...

    前の表の行は、元のテーブルの行と同じ順序でソートされています。

  3. 値が連続して増加するプライマリキー列をパーティションキーとして使用する場合は、パーティションキーにハッシュプレフィックスを追加します。

    OrderNumber 列には、連続して増加する値が格納されます。新しい消費記録が書き込まれると、常に最新の注文番号範囲になり、古い注文番号を持つ消費記録は変更されません。これにより、書き込みが最新のパーティションのみに集中するため、書き込みワークロードのバランスが崩れます。その結果、予約済みの読み取りスループットと書き込みスループットは十分に活用されません。したがって、テーブルを設計する際には、OrderNumber 列をパーティションキーとして使用しないでください。

    OrderNumber 列をテーブルのパーティションキーとして使用する必要がある場合は、パーティションキーにハッシュプレフィックスを追加して、OrderNumber 列の値が同じである行がテーブルにランダムに分散されるようにします。これにより、パーティション全体でアクセス負荷のバランスが取れます。

    次の表は、OrderNumber 列をパーティションキーとして使用する場合のテーブルのスキーマを示しています。

    OrderNumber

    DeviceID

    SellerID

    CardID

    attrs

    200001

    16

    'a100'

    66661

    ...

    200002

    167

    'a101'

    283408

    ...

    200003

    54

    'a100'

    6777

    ...

    200004

    54

    'a1001'

    6777

    ...

    200005

    66

    'b304'

    178994

    ...

    MD5 アルゴリズムなどのハッシュアルゴリズムを使用して、OrderNumber 列の値のハッシュプレフィックスを計算して追加し、パーティションキーを形成します。MD5 アルゴリズムを使用して計算されたハッシュプレフィックスは、長すぎる場合があります。OrderNumber 列の値が同じである行がテーブルにランダムに分散されるように、最初の数文字を使用できます。

    この例では、ハッシュプレフィックスの最初の 4 文字が使用されます。次の表は、OrderNumber 列の値にハッシュプレフィックスを追加してパーティションキーを形成する場合のテーブルのスキーマを示しています。

    HashOrderNumber

    DeviceID

    SellerID

    CardID

    attrs

    '2e38200004'

    54

    'a1001'

    6777

    ...

    'a5a9200003'

    54

    'a100'

    6777

    ...

    'c335200005'

    66

    'b304'

    178994

    ...

    'db6e200002'

    167

    'a101'

    283408

    ...

    'ddba200001'

    16

    'a100'

    66661

    ...

    後で消費記録にアクセスする場合は、同じアルゴリズムを使用して OrderNumber 列の値のハッシュプレフィックスを計算し、各消費記録のパーティションキーを取得します。ただし、GetRange 操作を呼び出して、プライマリキー値が特定の範囲内にある論理的に隣接したデータを読み取ることはできなくなります。

  4. データの時間liness に基づいて、コールドデータとホットデータを個別に保存します。

    アプリケーションは、消費記録を処理して統計情報を収集するか、最新の消費記録を時間内にクエリする必要があります。テーブル内の最新の消費記録は、アクセスされる可能性が高くなります。以前の消費記録はめったにクエリされず、最終的にはコールドデータになります。卒業した学生の学生証は、消費記録を生成しなくなります。

    CardID 列の値は、カード ID の申請日によって増加します。CardID 列をパーティションキーとして使用すると、学生が卒業したためにアクセスされなくなった CardID 列の値を持つ行に、予約済みの読み取りスループットと書き込みスループットが割り当てられます。これはリソースの無駄につながります。この場合、消費記録を月ごとにテーブルに分割し、新しい暦月のたびに新しいテーブルを使用できます。データの時間liness に基づいて、次の操作を実行できます。

    • 当月の消費記録テーブル: 新しい消費記録が継続的にテーブルに書き込まれ、消費記録がクエリされます。テーブルに大きな予約済みの読み取りスループットと書き込みスループットを割り当てることができます。

    • 前月の消費記録テーブル: 新しいデータはテーブルに書き込まれないか、ほとんど書き込まれませんが、消費記録をクエリするために多くのクエリリクエストが開始されます。テーブルにより小さい予約済みの書き込みスループットとより大きい予約済みの読み取りスループットを割り当てることができます。

    • 1 年以上前に生成された消費記録テーブル: テーブルにアクセスされることはほとんどありません。テーブルにより小さい予約済みの読み取りスループットと書き込みスループットを指定できます。

    • 保守されなくなった消費記録テーブル: テーブルにアクセスされなくなりました。データを Object Storage Service (OSS) にエクスポートしてアーカイブするか、データを削除できます。