スキーマの設計

このページでは、Cloud Bigtable テーブルのスキーマを設計する方法について説明します。このページを読む前に、Cloud Bigtable の概要を理解する必要があります。

一般的なコンセプト

Cloud Bigtable スキーマの設計は、リレーショナル データベースのスキーマ設計とは大きく異なります。Cloud Bigtable スキーマを設計する際には、以下の点に注意してください。

  • 各テーブルのインデックス(行キー)は 1 つのみです。 二次インデックスはありません。
  • 行は、行キーの(最小バイト文字列から最大バイト文字列まで)辞書的順番で並べ替えられます。行キーはビッグ エンディアン(ネットワーク)バイトオーダーで並べ替えられます。これはバイナリのアルファベット順に相当します。
  • 列は、列ファミリー別にグループ化され、列ファミリー内で辞書順に並び替えられます。
  • すべての操作は行レベルでアトミックに実行されます。 たとえば、テーブルの 2 つの行を更新する場合、一方の行の更新に成功し、他方の行の更新に失敗するという可能性があります。複数行にまたがるアトミック性を必要とするスキーマ設計は避けてください。
  • 読み取りと書き込みは(テーブルの行スペース全体に)均等に分散されるのが理想的です。
  • 一般に、特定のエンティティの情報はすべて 1 行にまとめるようにします。 アトミックな更新および読み取りを必要としないエンティティは、複数の行に分割してもかまいません。エンティティ データが大きい(数百 MB)場合は、複数行に分割することをおすすめします。
  • 関連するエンティティは隣接する行に格納するようにします。これにより、読み取りの効率が向上します。
  • Cloud Bigtable テーブルはスパースです。 空の列は領域を消費しません。このため、大半の行で大部分の列が空であっても、多数の列を作成することは多くの場合、合理的です。

サイズの制限

Cloud Bigtable は、テーブル内に格納するデータにサイズ制限を設定します。スキーマを設計する際には、この制限を考慮する必要があります。

Cloud Bigtable は行をアトミックに読み取ります。このため、1 行に格納するデータ量の合計を制限することは非常に重要です。1 つのセルに格納するデータを 10 MB 以下にし、1 行のデータを 100 MB 以下にするのがベスト プラクティスです。また、セルと行のデータサイズの厳密な制限を守る必要があります。

これらのサイズ制限は MB 単位で測定されます(1 MB は 220 バイト)。この測定単位はメビバイト(MiB)ともいいます。

行キーの選択

Cloud Bigtable で最高のパフォーマンスを得るには、行キーの構成について慎重に検討することが不可欠です。というのも、最も効率の高い Cloud Bigtable クエリは、行キー、行プレフィックス、または行範囲を使用してデータを取得するからです。その他の種類のクエリはテーブル全体をスキャンすることになり、効率は大幅に低下します。設計の段階で正しい行キーを選択すれば、後で面倒なデータ移行処理を行わずに済みます。

まず、格納するデータの用途について確認します。次に例を示します。

  • ユーザー情報: ユーザー間の接続に関する情報(たとえば、ユーザー A がユーザー B をフォローしているなど)に迅速にアクセスする必要がありますか?
  • ユーザー生成コンテンツ: 大容量のユーザー生成コンテンツ(ステータスの更新など)のサンプルをユーザーに表示する場合、特定のユーザーに表示するステータス更新をどのように決定しますか?
  • 時系列データ: 最新の N 個のレコードを検索する頻度と、特定の期間内のレコードを検索する頻度では、どちらが高くなりますか?さまざまなイベントのデータを格納する場合、イベントのタイプに応じてフィルタリングする必要がありますか?

事前にニーズを把握しておけば、行キーおよび全体的なスキーマ設計によって、データを効率的にクエリするための十分な柔軟性が得られます。

行キーのタイプ

このセクションでは、最もよく使用されるタイプの行キーと、ケースに応じた各タイプのキーの使い分けについて説明します。

だいたいの方向性として、行キーは適度に短くするようにします。長い行キーを使用すると、メモリとストレージが余分に消費されるだけでなく、Cloud Bigtable サーバーからのレスポンス返却に要する時間も長くなります。

リバース ドメイン名

ドメイン名として表現可能なエンティティに関するデータを格納する場合は、行キーとしてリバース ドメイン名(com.company.product など)を使用することを検討してください。リバース ドメイン名は、各行のデータが隣接する行と重なる傾向がある場合は特に有効です。そのような場合、Cloud Bigtable によるデータの圧縮効率が向上します。

このアプローチは、データが多数の異なるリバース ドメイン名間に分散している場合に、最も効果的です。大部分のデータが少数のリバース ドメイン名で格納されると予想される場合は、行キーにリバース ドメイン名以外を使用することを検討してください。そうしないと、クラスタ内の単一のノードに大半のデータが書き込まれ、テーブルが過負荷状態になる可能性があります。

文字列識別子

単純な文字列(たとえば、ユーザー ID)で識別できるエンティティについてのデータを保存する場合、文字列の識別子を行キーとして使用するか、行キーの一部として使用します。

このページではこれまで、実際の文字列識別子よりも文字列識別子のハッシュを使用することが推奨されてきました。現在では、ハッシュの使用は推奨されません。ハッシュされた行キーは具体的な意味を表すものではないため、ハッシュされた行キーが使用されていると、Cloud Bigtable に関する問題のトラブルシューティングが非常に困難になります。たとえば、行キーがユーザー ID のハッシュである場合、どのユーザー ID が行キーに関連付けられているかを特定することは難しく、場合によっては不可能です。

行キーのハッシュ値ではなく、人が読める値を使用してください。また、行キーに複数の値が含まれている場合は、これらの値を区切り文字で区切るようにしてください。これにより、Key Visualizer ツールでの Cloud Bigtable のトラブルシューティングが容易になります。

タイムスタンプ

データを記録日時に基づいて検索する頻度が高い場合は、行キーの一部にタイムスタンプを含める方法をおすすめします。ただし、タイムスタンプを単独で行キーにする方法は、大半の書き込みが特定の 1 ノードに集中してしまうため、おすすめできません。同じ理由で、タイムスタンプを行キーの先頭に配置するのも避けてください。

たとえば、アプリケーションで、CPU とメモリの使用状況などのパフォーマンス関連のデータを、多数のマシン上で毎秒記録する必要があるとします。このようなケースでは、パソコンの識別子とデータのタイムスタンプを連結したもの(例: machine_4223421#1425330757685)をデータの行キーとして使用します。

最新のレコードを最初に検索することが多い場合は、行キーでリバース タイムスタンプを使用できます。リバース タイムスタンプを得るには、お使いのプログラミング言語の長整数の最大値(Java の場合は java.lang.Long.MAX_VALUE)からタイムスタンプの値を引きます。リバース タイムスタンプを使用すると、新しいレコードが最初にくるようにレコードが並べ替えられます。

単一の行キーを構成する複数の値

行キーは、Cloud Bigtable を効率的に検索する唯一の方法であるため、行キーに複数の識別子を含めると有用な場合がよくあります。行キーに複数の値が含まれている場合は、データの使い方を明確に理解しておくことがとりわけ重要となります。

たとえば、アプリケーションで、ユーザーがメッセージを投稿でき、投稿内でユーザー同士が互いに言及できるとします。このとき、投稿内で特定のユーザーをタグ付けしたすべてのユーザーを、効率的な方法でリスト表示する必要があるとします。それを実現するための 1 つの手段と考えられるのが、タグ付けされたユーザーの名前とタグ付けを行ったユーザーの名前を区切り文字で区切ったもの(たとえば wmckinley#gwashington)を行キーとして使用するという方法です。特定のユーザー名をタグ付けしたユーザーの名前を見つける場合でも、そのユーザー名がタグ付けされているすべての投稿を表示する場合でも、行キーがそのユーザー名で始まる行を検索するだけで済みます。

重要なのは、明確に定義された範囲の行を検索できるような行キーを作成するということです。そのような行キーを作成しないと、クエリの実行時にテーブル スキャンが必要になり、特定の行を検索する場合に比べて効率が大幅に低下してしまいます。たとえば、パフォーマンス関連データを毎秒格納するとします。タイムスタンプの後にマシン識別子を続けたもの(例: 1425330757685#machine_4223421)を行キーにした場合、クエリを特定のマシンに絞り込む効率的な方法はなく、タイムスタンプによる絞り込みしかできないことになります。

行キー接頭辞

複数値の行キーに含まれる最初の値のことを行キー接頭辞と呼びます。行キー接頭辞を巧みに計画すれば、Cloud Bigtable の並べ替え順を利用して関連データを連続した行に格納できます。関連データを連続した行に格納すると、行の範囲として関連データにアクセスでき、効率の悪いテーブル スキャンを行わなくて済みます。

行キー接頭辞をマルチテナンシーのために使用する

行キー接頭辞は、「マルチテナンシー」ユースケースに対するスケーラブルなソリューションとなります。マルチテナンシーとは、複数のクライアントのために同じデータモデルを使用して類似したデータを格納するシナリオを指します。すべてのテナントに対して 1 つのテーブルを使用することは、通常、マルチテナント データの保存およびアクセス方法として最も効率的です。

たとえば、多くの会社のために購入履歴を保存し、履歴を追跡するとします。この場合は、各会社の固有の ID を行キー接頭辞として使用できます。あるテナントのすべてのデータは同じテーブルの連続した行に格納され、行キー接頭辞を使用して照会またはフィルタリングできます。また、ある会社がお客様でなくなり、その会社のために保存していた購入履歴データを削除する場合は、その会社の行キー接頭部を使用している行範囲を削除すれば済みます。

それに対して、会社ごとに別々のテーブルにデータを保存すると、パフォーマンスやスケーラビリティの問題が起こる可能性が高くなり、さらにはインスタンスあたり 1,000 テーブルという Cloud Bigtable の制限を超えてしまう可能性もあります。

避けたい行キー

行キーのタイプによっては、データのクエリが難しくなったり、パフォーマンスが低下する可能性があります。このセクションでは、Cloud Bigtable で使用すべきではない行キーのタイプについて説明します。

ドメイン名

標準の非リバース ドメイン名を行キーとして使用しないでください。標準のドメイン名はドメインの一部に属するすべての行を検索するには非効率的です(たとえば、company.com に関連するすべての行が、services.company.comproduct.company.com などの異なる行範囲に属するといった場合)。また、標準のドメイン名を使用すると、関連するデータが 1 か所にまとめられない形で行が並べ替えられることになるため、結果として圧縮効率が低下します。

シーケンシャル数値 ID

お使いのシステムで、アプリケーションの各ユーザーに数値 ID が割り当てられているとします。このような場合には、テーブルの行キーとしてユーザーの数値 ID を使いたくなるかもしれません。しかし、新規のユーザーのほうがアクティブなユーザーになる可能性が高いため、このような方法では、大半のトラフィックがごく少数のノードに集中してしまいます。

より安全な方法として、ユーザーの数値 ID のリバース版を使用する方法があります。この方法なら、Cloud Bigtable テーブルのすべてのノードに均等にトラフィックが分散されます。

頻繁に更新される識別子

頻繁に更新する必要のある値を識別するために単一の行キーを使用しないでください。たとえば、メモリ使用状況データを毎秒格納する場合、memusage という名前の単一の行キーを使用して行を繰り返し更新するようなオペレーションは避けてください。このようなオペレーションを実行すると、使用頻度の高い行が格納されているテーブルが過負荷状態になります。また、しばらくの間セルの直前の値が領域を消費したままになるため、行のサイズが上限を超えてしまう可能性もあります。

このような場合は、指標のタイプ、区切り文字、タイムスタンプで構成される行キーを使用して、行ごとに 1 つの値を格納します。たとえば、一定期間にわたってメモリの使用状況を追跡するには、memusage#1423523569918 のような行キーを使用します。この方法は効率的です。なぜなら、Cloud Bigtable では、新しい行を作成するのに要する時間は、新しいセルを作成するのに要する時間と大差ないからです。また、この方法を使用すると、適切な開始キーと終了キーを計算することで、特定の日付範囲から迅速にデータを読み取ることができます。

毎分数百回更新されるカウンタなど、頻繁に変化する値の場合は、単純にデータをアプリケーション層でメモリ内に保持し、新しい行を定期的に Cloud Bigtable に書き込むのが最善の方法です。

ハッシュ値

上述したように、このページではこれまで、行キーにハッシュ値を使用することが推奨されていました。現在、この方法は推奨されません。基本的に行キーに意味がないため、Key Visualizer ツールで Cloud Bigtable のトラブルシューティングを行う場合に作業が難しくなります。

ハッシュ値ではなく、人が読める値を使用してください。行キーに複数の値が含まれている場合は、これらの値を区切り文字で区切るようにしてください。

列ファミリーと列修飾子

このセクションでは、テーブル内の列ファミリーと列修飾子についての考え方を説明します。

列ファミリー

HBase とは異なり、Cloud Bigtable では、優れたパフォーマンスを維持しながら 100 個までの列ファミリーを使用できます。したがって、互いに関連する複数の値を行に格納する場合はいつでも、それらの値を 1 つの列ファミリーにまとめることをおすすめします。列ファミリー内にデータをまとめることにより、単一または複数のファミリーからデータを取得できます。各行内のデータをすべて取得する必要はありません。データをできるだけ近接した場所にまとめることで、最も頻繁に呼び出される API で必要な情報のみを取得できます。

また、列ファミリー名はリクエストごとに転送データに含まれるため、列ファミリーには短い名前を付けるようにしてください。

列修飾子

Cloud Bigtable テーブルはスパースであるため、行ごとに必要なだけ列修飾子を作成できます。行内の空のセルによって領域が消費されることはありません。このため、列修飾子をデータのように扱うのが理にかなっていることがよくあります。たとえば、テーブルにユーザーの投稿を格納する場合、各投稿の一意の識別子を列修飾子として使用できます。

データは必要以上に多くの列修飾子に分割しないようにしてください。空でないセルを大量に含む行が増える可能性があります。Cloud Bigtable が行内の各セルを処理するため時間がかかります。セルが多くなると、テーブルにデータを格納する際やネットワーク経由でのデータを送信する際のオーバーヘッドも増加します。たとえば、1 KB(1,024 バイト)のデータを格納する場合、1 バイトずつ 1,024 個のセルに分散させるのではなく、データを 1 つのセルに格納したほうがスペース効率が良くなります。通常、関連する少数の値を同時に読み書きする場合は、個々の値を後で簡単に抽出できる方法ですべての値を 1 つのセルにまとめて格納することを検討してください(プロトコル バッファのバイナリ形式など)。

また、列修飾子の名前を短くすると、各リクエストで転送されるデータ量を減らすことができます。

次のステップ

このページは役立ちましたか?評価をお願いいたします。

フィードバックを送信...

Cloud Bigtable ドキュメント