LindormTable は分散データエンジンであり、データはプライマリキーに基づいて分散されます。テーブルのプライマリキーに複数の列が含まれている場合、LindormTable は左から右の順序で列を使用してデータをクエリします。不適切に設計されたプライマリキーは、クエリで効果的に使用できません。この場合、少量のホットスポットデータに対して多数のクエリが実行される可能性があり、クエリのパフォーマンスが低下します。したがって、プライマリキーの設計は、Lindorm のデータパーティショニングとデータクエリにおいて重要な役割を果たします。このトピックでは、プライマリキー設計の考慮事項について説明し、例を示します。
考慮事項
プライマリキー値は一意ですか?
行の異なるバージョンは、同じプライマリキー値を使用します。デフォルトでは、クエリの実行時に最新バージョンが返されます。ほとんどの場合、マルチバージョンが実装されていない限り、プライマリキーは一意である必要があります。
ベストプラクティス:プライマリキーは、列または列のセットです。各プライマリキー値はレコードに対応します。
[userid]:1 つの列のみがプライマリキーとして指定されています。各ユーザーに対して 1 つのレコードのみが生成されます。[userid][orderid]:2 つの列がプライマリキーとして指定されています。各ユーザーに対して複数のレコードが生成されます。
さまざまなシナリオでプライマリキーを設計するにはどうすればよいですか?
プライマリキーの設計により、使用できるクエリメソッドが制限されます。Lindorm は、次のメソッドを使用する SELECT ステートメントをサポートしています。
GET メソッド:プライマリキーを使用してデータをクエリします。例:
SELECT * FROM table WHERE userid='abc' AND orderid=123。説明GET メソッドを使用するには、すべてのプライマリキー列を指定する必要があります。すべてのプライマリキー列の値は明示的でなければなりません。
SCAN メソッド:プライマリキー範囲に基づいてデータをクエリします。例:
SELECT * FROM table WHERE userid='abc' AND 123<orderid<456。説明SCAN メソッドを使用するには、最初のプライマリキー列でスキャンする範囲を指定する必要があります。デフォルトでは、範囲を指定しない場合、フルテーブルスキャンは非効率的であるため、Lindorm はテーブル内のすべてのデータをスキャンする必要があるクエリを拒否します。詳細については、「SELECT」をご参照ください。
ベストプラクティス:上記のクエリメソッドを使用して複雑なクエリを送信するにはどうすればよいですか?
インデックステーブルを作成します。
クエリ条件で、プライマリキー列以外のスキャンする列を指定します。Lindorm は、無関係なデータを自動的に除外します。
セカンダリインデックスを使用します。
ORDER BYを使用して、データを降順にソートします。このようにして、新しいレコードがテーブルの先頭行にソートされます。例:SELECT * FROM table WHERE userid='abc' AND 123<orderid<456 ORDER BY orderid DESC。説明ほとんどのクエリが最新のデータを取得するために送信されるシナリオでは、データを降順にソートするために、プライマリキーを
[userid][orderid DESC]として設計できます。
プライマリキーを設計する際に考慮すべき要素は何ですか?
プライマリキーを設計する場合は、プライマリキー列の値の長さとプライマリキー列の数を考慮する必要があります。
プライマリキー列の値の長さ:プライマリキー列の値は、長さが短い必要があります。プライマリキー列として、長整数などの固定長の値を格納する列を選択することをお勧めします。プライマリキー列の値の長さが固定されていない場合は、ストレージコストを削減し、書き込みパフォーマンスを向上させるために、列の値の長さを 2 KB 以内に制限することをお勧めします。
プライマリキー列の数:プライマリキー列が少ないほど、書き込みパフォーマンスが向上し、ストレージコストが削減されます。プライマリキー列の数を 1 ~ 3 の範囲に制限することをお勧めします。
プライマリキーを設計する際に避けるべきことは何ですか?
Lindorm に格納されているデータは、プライマリキーに基づいて分散されます。テーブルのプライマリキーに複数の列が含まれている場合、データは左から右の順序で列に基づいて分散されます。少量のホットスポットデータに対して多数の書き込み操作が実行されないようにするために、テーブルのプライマリキーを定義する際には、次の項目に注意してください。
最初のプライマリキー列の値は分散されている必要があります。
自動インクリメンタルデータを含む列、またはタイムスタンプ列など、値に同じプレフィックスがある列を、最初のプライマリキー列またはインデックス列として指定しないでください。
注文タイプなどの列挙データを含む列、または値に明らかなプレフィックスがある列を、最初のプライマリキー列として指定しないでください。
上記のタイプの列を最初のプライマリキー列として指定する必要がある場合は、ハッシュメソッドを使用して列内のデータを分散します。
たとえば、自動インクリメンタル文字列を含む列 pk を最初のプライマリキー列として指定する必要がある場合は、次のアルゴリズムを使用して、pk 列に基づいて pk1 という名前の新しい列を作成できます。pk1 = hash(pk).substring(0,4)+pk。pk1 列は、pk 列と、pk 列に基づくハッシュメソッドによって返された結果の最初の 4 桁であるプレフィックスを連結したものです。
完全に分散されたデータに対してスタックホットスポットが発生しますか?
ハッシュメソッドを使用して、データを異なるパーティションに分散します。これにより、サーバーがホットスポットによって終了したり、他のサーバーがアイドル状態になったりするのを防ぎます。このようにして、分散アーキテクチャと同時処理が効率的に利用されます。
ベストプラクティス:
MD5 ハッシュアルゴリズムを設計します。プライマリキーは
[md5(userid).subStr(0,4)][userId][orderid]です。逆インデックスを設計します。プライマリキーは
[reverse(userid)][orderid]です。モジュロ演算を設計します。プライマリキーは
[bucket][timestamp][hostname][log-event]; long bucket = timestamp % numBucketsです。乱数を追加します。プライマリキーは
[userId][orderid][random(100)]です。
プライマリキーを簡略化できますか?
プライマリキー列の数を減らして、スキャンされるデータ量を減らし、クエリと挿入操作の効率を向上させることができます。
ベストプラクティス:
STRING データ型を LONG または INT データ型に置き換えます。例:
'2015122410' => Long(2015122410)。名前をコードに置き換えます。例:
'taobao'=> 'tb'。
一般的な設計
ログデータと時系列データのプライマリキー設計。
一定期間にわたって生成されたメトリックのデータをクエリします。プライマリキーは
[hostname][log-event][timestamp]として設計されています。メトリックの最新レコードをクエリします。プライマリキーは
[hostname][log-event][timestamp DESC]として設計されています。時間ディメンションのみを含むデータ、または特定のディメンションでデータ量が大きいデータをクエリします。プライマリキーを
long bucket = timestamp % numBuckets; [bucket][timestamp][hostname][log-event]として設計します。
トランザクションデータのプライマリキー設計。
特定の期間内の販売者のトランザクションレコードをクエリします。プライマリキーを
[seller_id][timestmap][order_number]として設計します。特定の期間内の購入者のトランザクションレコードをクエリします。プライマリキーを
[buyer_id][timestmap][order_number]として設計します。注文 ID に基づいてデータをクエリします。プライマリキーを
[order_number]として設計します。3 つのテーブルを結合してクエリを実行します。購入者データを格納するテーブルのプライマリキーは
[buyer_id][timestmap][order_number]として設計されています。販売者データを格納するテーブルのプライマリキーは[seller id][timestmap][order number]として設計されています。注文 ID を格納するテーブルのプライマリキーは[order_number]として設計されています。