適切なテーブルスキーマ設計により、豊富な機能をサポートでき、データベースシステムのパフォーマンス、保守性、およびスケーラビリティが大幅に向上します。そのため、データベースとテーブルスキーマの設計は非常に重要です。このトピックでは、ApsaraDB for SelectDB でテーブルスキーマを設計する際に注目すべきテーブルプロパティについて説明します。この情報は、ビジネス要件により良く適合するようにテーブルを適切に設計するのに役立ちます。
重要なテーブルプロパティ
ApsaraDB for SelectDB にビジネスデータを保存する場合、ビジネス要件に基づいて重要なテーブルプロパティを設計することが非常に重要です。これは、パフォーマンスが高く、メンテナンスが容易なテーブルスキーマを作成するのに役立ちます。次の表に、ApsaraDB for SelectDB の重要なテーブルプロパティを示します。
テーブルプロパティ | 必須 | 説明 | 参照 |
データモデル | はい | 異なるデータモデルは異なるビジネスシナリオに適しています。Unique モデルは、プライマリキーの一意性制約をサポートし、柔軟で効率的なデータ更新要件を満たすために使用されます。 Duplicate モデルは、追加データ書き込みモードを使用し、詳細データの高性能分析に適しています。 Aggregate モデルはデータの事前集計をサポートし、データの集計と統計のシナリオに適しています。 | |
タブレット | はい | タブレットは、分散システムの機能を活用して大量のデータを管理およびクエリするために、クラスタ内の異なるノードにデータを分散するために使用されます。 | |
パーティション | いいえ | パーティション分割により、時間や地域などの指定されたフィールドに基づいて、生テーブルを複数の子テーブルに分割できます。パーティション分割により、データ管理とクエリが容易になり、クエリが高速化されます。 | |
インデックス | いいえ | インデックスに基づいてデータをすばやくフィルタリングまたは特定できます。これにより、クエリのパフォーマンスが大幅に向上します。 |
データモデル
データ分析シナリオの機能要件とパフォーマンス要件に基づいて、適切なデータモデルを選択できます。異なるデータモデルは異なるビジネスシナリオに適しています。このセクションでは、ビジネス要件に基づいてデータモデルを理解し、選択するのに役立つように、データモデルについて簡単に説明します。詳細については、「データモデル」をご参照ください。
基本
ApsaraDB for SelectDB では、データは論理レイヤーでテーブルの形式で編成および管理されます。各テーブルは行と列で構成されます。行は、テーブル内のデータの行を示します。列は、行内のフィールドを記述するために使用されます。
列は次のタイプに分類できます。
キー列:CREATE TABLE 文でキーワード
UNIQUE KEY、AGGREGATE KEY、およびDUPLICATE KEYによって変更される列は、キー列です。値列:その他すべての列は値列です。
モデルを選択する
ApsaraDB for SelectDB では、テーブルに 3 つのタイプのデータモデル(Unique、Duplicate、および Aggregate)を使用できます。
データモデルはテーブルの作成時に決定され、変更することはできません。
テーブルの作成時にデータモデルが指定されていない場合、デフォルトで Duplicate モデルが使用され、最初の 3 つの列がキー列として自動的に選択されます。
Unique、Duplicate、および Aggregate モデルでは、データはキー列に基づいてソートされます。
データモデル | 特性 | シナリオ | 短所 |
Unique | 各行のキー列の値は一意です。 キー列に複数の行で同じ値がある場合、テーブルに後で書き込まれた行が前の行を上書きします。 | このモデルは、一意のプライマリキーまたは効率的な更新が必要なシナリオに適しています。たとえば、e コマースの注文やユーザー属性データの分析などのデータ分析シナリオで Unique モデルを使用できます。 |
|
Duplicate | 複数の行のキー列の値は同じにすることができます。 キー列の値が同じ複数の行を、システムに同時に保存できます。 | このモデルは、データの書き込みとクエリの効率が高く、すべての生データレコードが保持されるシナリオに適しています。たとえば、ログ分析や請求分析などの詳細データ分析シナリオで Duplicate モデルを使用できます。 |
|
Aggregate | 各行のキー列の値は一意です。 複数の行のキー列の値が同じ場合、行の値列は、テーブルの作成時に指定された集計タイプに基づいて事前に集計されます。 | 従来のデータウェアハウスの Cube モデルと同様に、Aggregate モデルは、事前集計によってクエリのパフォーマンスを向上させる集計統計シナリオに適しています。たとえば、このモデルは、Web サイトのトラフィック分析やカスタムレポートなどのデータ分析シナリオで使用できます。 |
|
モデルを使用する
Unique モデルを使用する
Unique モデルでは、キー列に複数の行で同じ値がある場合、テーブルに後で書き込まれた行が前の行を上書きします。Unique モデルには、Merge on Read(MoR)と Merge on Write(MoW)の 2 つの実装方法があります。
MoW メソッドは成熟して安定しており、優れたクエリパフォーマンスを提供します。そのため、Unique モデルでは MoW メソッドを使用することをお勧めします。次の例では、MoW メソッドを使用して Unique モデルを実装する方法について説明します。MoR メソッドの詳細については、「データモデル」トピックの「MoR」セクションをご参照ください。
使用上の注意
Unique モデルを選択し、MoW メソッドを使用する場合、テーブルを作成する際に次の点に注意してください。
UNIQUE KEYキーワードを使用して、一意のフィールドをプライマリキーとして指定します。PROPERTIES セクションで MoW を有効にします。
"enable_unique_key_merge_on_write" = "true"
例
次のサンプルコードは、orders テーブルを作成するための SQL 文を示しています。この例では、orders テーブルに Unique モデルが選択され、order_id フィールドと order_time フィールドが複合プライマリキーとして使用され、MoW メソッドが有効になっています。
CREATE TABLE IF NOT EXISTS orders
(
`order_id` LARGEINT NOT NULL COMMENT "注文 ID。",
`order_time` DATETIME NOT NULL COMMENT "注文時間。",
`customer_id` LARGEINT NOT NULL COMMENT "ユーザー ID。",
`total_amount` DOUBLE COMMENT "注文の合計金額。",
`status` VARCHAR(20) COMMENT "注文ステータス。",
`payment_method` VARCHAR(20) COMMENT "支払い方法。",
`shipping_method` VARCHAR(20) COMMENT "配送方法。",
`customer_city` VARCHAR(20) COMMENT "ユーザーが居住する都市。",
`customer_address` VARCHAR(500) COMMENT "ユーザーの住所。"
)
UNIQUE KEY(`order_id`, `order_time`)
PARTITION BY RANGE(`order_time`) ()
DISTRIBUTED BY HASH(`order_id`)
PROPERTIES (
"enable_unique_key_merge_on_write" = "true",
"dynamic_partition.enable" = "true",
"dynamic_partition.time_unit" = "DAY",
"dynamic_partition.start" = "-7",
"dynamic_partition.end" = "3",
"dynamic_partition.prefix" = "p",
"dynamic_partition.create_history_partition" = "true",
"dynamic_partition.buckets" = "16"
);Duplicate モデルを使用する
Duplicate モデルでは、キー列の値が同じ複数の行がシステムに同時に保存されます。このモデルは、事前集計をサポートしておらず、一意のプライマリキーを必要としません。
たとえば、このモデルを使用して、業務システムによって生成されたログデータを記録および分析し、ログ時間、ログタイプ、およびエラーコードでデータをソートできます。 次のサンプルコードは、log テーブルを作成するための SQL 文を示しています。この例では、log テーブルに Duplicate モデルが選択され、データは log_time、log_type、および error_code フィールドでソートされます。
CREATE TABLE IF NOT EXISTS log
(
`log_time` DATETIME NOT NULL COMMENT "ログが生成された時間。",
`log_type` INT NOT NULL COMMENT "ログのタイプ。",
`error_code` INT COMMENT "エラーコード。",
`error_msg` VARCHAR(1024) COMMENT "エラーメッセージ。",
`op_id` BIGINT COMMENT "オーナー ID。",
`op_time` DATETIME COMMENT "エラーが処理された時間。"
)
DUPLICATE KEY(`log_time`, `log_type`, `error_code`)
PARTITION BY RANGE(`log_time`) ()
DISTRIBUTED BY HASH(`log_type`)
PROPERTIES (
"dynamic_partition.enable" = "true",
"dynamic_partition.time_unit" = "DAY",
"dynamic_partition.start" = "-7",
"dynamic_partition.end" = "3",
"dynamic_partition.prefix" = "p",
"dynamic_partition.create_history_partition" = "true",
"dynamic_partition.buckets" = "16"
);Aggregate モデルを使用する
使用上の注意
Aggregate モデルでは、複数の行のキー列の値が同じ場合、行の値列は、テーブルの作成時に指定された集計タイプに基づいて事前に集計されます。Aggregate モデルのテーブルを作成する際には、次の点に注意する必要があります。
AGGREGATE KEYキーワードを使用して、1 つ以上のキー列を指定します。キー列の値が同じ行が集計されます。値列の集計タイプを指定します。次の表に、集計タイプを示します。
集計タイプ
説明
SUM
複数の行の値の合計を計算します。このタイプは数値に適用できます。
MIN
最小値を計算します。このタイプは数値に適用できます。
MAX
最大値を計算します。このタイプは数値に適用できます。
REPLACE
以前の値を新しくインポートされた値に置き換えます。ディメンション列に同じデータが含まれている行の場合、メトリック列の値は、値がインポートされた順序に基づいて、新しくインポートされた値に置き換えられます。
REPLACE_IF_NOT_NULL
null 値以外の値を、新しくインポートされた値に置き換えます。REPLACE タイプとは異なり、このタイプは null 値を置き換えません。このタイプを使用する場合、フィールドのデフォルト値として空の文字列ではなく null 値を指定する必要があります。フィールドのデフォルト値として空の文字列を指定すると、このタイプは空の文字列を別の文字列に置き換えます。
HLL_UNION
HLL アルゴリズムを使用して、HyperLogLog(HLL)タイプの列を集計します。
BITMAP_UNION
BITMAP タイプの列を集計します。これは、ビットマップの和集合計を実行します。
例
たとえば、Aggregate モデルを使用して、ユーザーの行動に関する統計分析を実行し、最後の訪問時間、合計消費量、最大滞在時間、および最小滞在時間を記録できます。次のサンプルコードは、user_behavior テーブルを作成するための SQL 文を示しています。 この例では、次のキー列に複数の行で同じ値がある場合、値列は事前に集計されます:user_id、date、city、age、およびsex。データは次のルールに基づいて集計されます。
ユーザーの最後の訪問時間:last_visit_date フィールドの最大値を使用します。
合計消費量:複数のデータレコードの合計を計算します。
最大滞在時間:max_dwell_time フィールドの最大値を使用します。max_dwell_time フィールド。
最小滞留時間: min_dwell_time フィールドの最小値を使用します。
CREATE TABLE IF NOT EXISTS user_behavior
(
`user_id` LARGEINT NOT NULL COMMENT "ユーザー ID。",
`date` DATE NOT NULL COMMENT "テーブルにデータが書き込まれる日付。",
`city` VARCHAR(20) COMMENT "ユーザーが居住する都市。",
`age` SMALLINT COMMENT "ユーザーの年齢。",
`sex` TINYINT COMMENT "ユーザーの性別。",
`last_visit_date` DATETIME REPLACE DEFAULT "1970-01-01 00:00:00" COMMENT "ユーザーが最後に訪問した時間。",
`cost` BIGINT SUM DEFAULT "0" COMMENT "ユーザーが費やした金額。",
`max_dwell_time` INT MAX DEFAULT "0" COMMENT "ユーザーの最大滞在時間。",
`min_dwell_time` INT MIN DEFAULT "99999" COMMENT "ユーザーの最小滞在時間。"
)
AGGREGATE KEY(`user_id`, `date`, `city`, `age`, `sex`)
PARTITION BY RANGE(`date`) ()
DISTRIBUTED BY HASH(`user_id`)
PROPERTIES (
"dynamic_partition.enable" = "true",
"dynamic_partition.time_unit" = "DAY",
"dynamic_partition.start" = "-7",
"dynamic_partition.end" = "3",
"dynamic_partition.prefix" = "p",
"dynamic_partition.create_history_partition" = "true",
"dynamic_partition.buckets" = "16"
);データ分割
ApsaraDB for SelectDB は、次の図に示すように、2 つのレイヤーのデータ分割をサポートしています。テーブルは、最初のレイヤーでパーティションに論理的に分割されます。パーティションは、データ管理の最小単位です。テーブルは、2 番目のレイヤーでタブレットに物理的に分割されます。タブレットは、データ分散やデータ移行などのデータ操作の最小単位です。
パーティションとタブレットの関連付け
タブレットは 1 つのパーティションにのみ属しますが、パーティションには複数のタブレットが含まれます。
テーブルの作成時にパーティション分割が有効になっている場合、テーブルはパーティション分割ルールに基づいてパーティションに分割され、次にテーブル分割ルールに基づいてタブレットに分割されます。パーティション分割が有効になっていない場合、テーブルはテーブル分割ルールに基づいて直接タブレットに分割されます。
データ書き込み中、データは最初にパーティションに書き込まれ、次にテーブル分割ルールに基づいてパーティション内の異なるタブレットに書き込まれます。テーブル分割は、パーティション分割されたデータをさらに分割して、データをより均等に分散し、クエリ効率を向上させるためのものです。
パーティション
ApsaraDB for SelectDB のストレージエンジンでは、パーティション分割は、カスタムルールに基づいてテーブル内のデータを複数の独立した部分に分割するデータ編成方法です。パーティション分割は、データの論理的な分割を実装します。これにより、クエリの効率が向上し、データ管理がより柔軟で便利になります。このセクションでは、ビジネス要件に基づいてパーティション分割モードを理解し、選択するのに役立つように、パーティションについて簡単に説明します。詳細については、「パーティション分割とバケット分割」トピックのパーティション分割セクションと、動的パーティション分割トピックをご参照ください。
パーティション分割モードを選択する
ApsaraDB for SelectDB は、範囲パーティション分割とリストパーティション分割の 2 つのパーティション分割モードをサポートしています。 ApsaraDB for SelectDB は、自動化されたパーティション管理を実装するための使いやすい動的パーティション分割機能も提供しています。パーティション分割モードは、さまざまなビジネスシナリオに適しています。
パーティション分割モード | サポートされている列のデータ型 | パーティション情報を指定するために使用されるメソッド | シナリオ |
範囲 | DATE、DATETIME、TINYINT、SMALLINT、INT、BIGINT、および LARGEINT | 次の 4 つのメソッドがサポートされています。
| 範囲パーティション分割は、データ分割範囲の管理に適しています。このメソッドの典型的なシナリオは、時間ベースのパーティション分割です。 |
リスト | BOOLEAN、TINYINT、SMALLINT、INT、BIGINT、LARGEINT、DATE、DATETIME、CHAR、および VARCHAR |
| リストパーティション分割は、既存の型または固定された特性に基づくデータ管理に適しています。パーティションキー列には、通常、列挙値があります。たとえば、ユーザーが属する地域に基づいてデータを分割および管理できます。 |
使用上の注意
ApsaraDB for SelectDB では、テーブルはパーティションテーブルと非パーティションテーブルに分類されます。テーブルを作成するときに、パーティション分割を有効にするかどうかを決定できます。このプロパティはオプションであり、一度決定すると変更できません。具体的には、パーティションテーブルのパーティションを作成または削除できます。非パーティションテーブルのパーティションを作成または削除することはできません。
1 つ以上の列をパーティションキー列として指定できます。パーティションキー列はキー列である必要があります。
パーティションキー列のデータ型に関係なく、パーティションキー値を二重引用符(")で囲む必要があります。
理論的には、パーティションの数に制限はありません。
パーティションを作成するときは、パーティションの範囲が重複しないようにしてください。
パーティション分割の使用
範囲パーティション分割の使用
範囲パーティション分割は、フィールドの範囲に基づいてデータを管理するために最も一般的に使用されるパーティション分割方法です。典型的なシナリオでは、大量の時系列データが時間でパーティション分割され、管理を容易にし、クエリを最適化します。
パーティション分割とタブレット化の最終的な目標は、データを合理的に分割することです。パーティション分割ルールを指定するときは、次の基準に従ってください。
各タブレットのデータ量は 1 GB ~ 10 GB の範囲内である必要があります。
パーティションの粒度は、管理するデータ量に基づいて決定する必要があります。たとえば、履歴ログデータを日単位で削除する場合、日単位のパーティション粒度が適切です。
次のサンプルコードは、パーティションテーブルを作成する方法を示しています。このテーブルのデータは時間範囲でフィルタリングされ、履歴ログデータは時間で削除されます。この例では、log_time フィールドがパーティションキー列として使用されています。
CREATE TABLE IF NOT EXISTS log
(
`log_time` DATETIME NOT NULL COMMENT "ログが生成された時間", /* The time when the log was generated. */
`log_type` INT NOT NULL COMMENT "ログのタイプ", /* The type of the log. */
`error_code` INT COMMENT "エラーコード", /* The error code. */
`error_msg` VARCHAR(1024) COMMENT "エラーメッセージ", /* The error message. */
`op_id` BIGINT COMMENT "オーナー ID", /* The owner ID. */
`op_time` DATETIME COMMENT "エラーが処理された時間" /* The time when the error was handled. */
)
DUPLICATE KEY(`log_time`, `log_type`, `error_code`)
PARTITION BY RANGE(`log_time`)
(
PARTITION `p20240201` VALUES [("2024-02-01"), ("2024-02-02")),
PARTITION `p20240202` VALUES [("2024-02-02"), ("2024-02-03")),
PARTITION `p20240203` VALUES [("2024-02-03"), ("2024-02-04"))
)
DISTRIBUTED BY HASH(`log_type`)
PROPERTIES ();テーブルが作成された後、次の SQL 文を実行して、テーブルのパーティション情報を表示できます。
SHOW partitions FROM log;p20240201: [("2024-02-01"), ("2024-02-02"))
p20240202: [("2024-02-02"), ("2024-02-03"))
p20240203: [("2024-02-03"), ("2024-02-04"))次の文を実行してデータをクエリすると、パーティション p20240202: [("2024-02-02"), ("2024-02-03")) がヒットします。システムは他の 2 つのパーティションのデータをスキャンしません。これにより、データクエリが高速化されます。
SELECT * FROM orders WHERE order_time = '2024-02-02';リストパーティション分割の使用
リストパーティションは、パーティションキー列の列挙値に基づいてデータを分割および管理します。リストパーティションテーブルからデータをクエリする場合、フィルター条件に基づいてパーティションをプルーニングして、クエリのパフォーマンスを向上させることができます。
ビジネスデータの管理に一般的に使用されるフィールドに基づいて、リストパーティションキー列を選択できます。深刻なデータスキューを防ぐために、データはパーティションに均等に分散される必要があります。
たとえば、e コマースのシナリオでは、大量の注文データが存在します。一部のシナリオでは、ユーザーが属する都市に基づいて注文データをクエリおよび分析する必要があります。データ管理とクエリを容易にするために、customer_city フィールドをパーティションキー列として指定できます。この例では、注文データは次の都市に分散されています。
中国の北京、上海、香港:6 GB
ニューヨークとサンフランシスコ:5 GB
東京:5 GB
次のサンプルコードは、注文データのリストパーティションテーブルを作成する方法を示しています。
CREATE TABLE IF NOT EXISTS orders
(
`order_id` LARGEINT NOT NULL COMMENT "注文 ID", /* The order ID. */
`order_time` DATETIME NOT NULL COMMENT "注文時間", /* The order time. */
`customer_city` VARCHAR(20) COMMENT "ユーザーが居住する都市", /* The city in which the user resides. */
`customer_id` LARGEINT NOT NULL COMMENT "ユーザー ID", /* The user ID. */
`total_amount` DOUBLE COMMENT "注文の合計金額", /* The total amount of the order. */
`status` VARCHAR(20) COMMENT "注文ステータス", /* The order status. */
`payment_method` VARCHAR(20) COMMENT "支払い方法", /* The payment method. */
`shipping_method` VARCHAR(20) COMMENT "配送方法", /* The shipping method. */
`customer_address` VARCHAR(500) COMMENT "ユーザーの住所" /* The address of the user. */
)
UNIQUE KEY(`order_id`, `order_time`, `customer_city`)
PARTITION BY LIST(`customer_city`)
(
PARTITION `p_cn` VALUES IN ("Beijing", "Shanghai", "Hong Kong"),
PARTITION `p_usa` VALUES IN ("New York", "San Francisco"),
PARTITION `p_jp` VALUES IN ("Tokyo")
)
DISTRIBUTED BY HASH(`order_id`) BUCKETS 16
PROPERTIES (
"enable_unique_key_merge_on_write" = "true"
);テーブルが作成された後、次の SQL 文を実行して、テーブルのパーティション情報を表示できます。テーブルには 3 つのパーティションが自動的に生成されます。
SHOW partitions FROM orders;p_cn: ("Beijing", "Shanghai", "Hong Kong")
p_usa: ("New York", "San Francisco")
p_jp: ("Tokyo")次の文を実行してデータをクエリすると、パーティション p_jp: ("Tokyo") がヒットします。システムは他の 2 つのパーティションのデータをスキャンしません。これにより、データクエリが高速化されます。
SELECT * FROM orders WHERE customer_city = 'Tokyo';動的パーティション分割の使用
本番環境では、データテーブルに多数のパーティションが存在する可能性があり、手動パーティション管理の作業負荷が大きくなります。これにより、データベース管理者のメンテナンスコストが増加します。ApsaraDB for SelectDB では、テーブルの作成中に動的パーティション分割ルールを構成して、自動パーティション管理を実装できます。
たとえば、e コマースのシナリオでは、注文情報テーブルのデータを時間範囲でクエリし、履歴注文をアーカイブ用にダンプすることがよくあります。 order_time フィールドをパーティションキー列として指定し、PROPERTIES セクションで動的パーティション分割を有効にできます。次のサンプルコードは、動的にパーティション分割されたテーブルを作成する方法を示しています。この例では、PROPERTIES セクションで dynamic_partition.time_unit、dynamic_partition.start、および dynamic_partition.end パラメーターを指定して、データを日単位でパーティション分割し、過去 180 日間のパーティションのみを保持し、次の 3 日間のパーティションを事前に作成します。
PARTITION BY RANGE('order_time') () 文の末尾にある括弧 () は構文エラーではありません。動的パーティション分割を使用する場合は、括弧が必要です。
CREATE TABLE IF NOT EXISTS orders
(
`order_id` LARGEINT NOT NULL COMMENT "注文 ID", /* The order ID. */
`order_time` DATETIME NOT NULL COMMENT "注文時間", /* The order time. */
`customer_id` LARGEINT NOT NULL COMMENT "ユーザー ID", /* The user ID. */
`total_amount` DOUBLE COMMENT "注文の合計金額", /* The total amount of the order. */
`status` VARCHAR(20) COMMENT "注文ステータス", /* The order status. */
`payment_method` VARCHAR(20) COMMENT "支払い方法", /* The payment method. */
`shipping_method` VARCHAR(20) COMMENT "配送方法", /* The shipping method. */
`customer_city` VARCHAR(20) COMMENT "ユーザーが居住する都市", /* The city in which the user resides. */
`customer_address` VARCHAR(500) COMMENT "ユーザーの住所" /* The address of the user. */
)
UNIQUE KEY(`order_id`, `order_time`)
PARTITION BY RANGE(`order_time`) ()
DISTRIBUTED BY HASH(`order_id`)
PROPERTIES (
"enable_unique_key_merge_on_write" = "true",
"dynamic_partition.enable" = "true",
"dynamic_partition.time_unit" = "DAY",
"dynamic_partition.start" = "-180",
"dynamic_partition.end" = "3",
"dynamic_partition.prefix" = "p",
"dynamic_partition.create_history_partition" = "true",
"dynamic_partition.buckets" = "16"
);テーブルに多数のパーティションが含まれることが予想される場合は、動的パーティションについて学習することを強くお勧めします。詳細については、「動的パーティション」をご参照ください。
タブレット
ApsaraDB for SelectDB のストレージエンジンは、指定された列のハッシュ値に基づいてデータを異なるタブレットに分割します。タブレットは、クラスタ内の異なるノードによって管理されます。分散システムの機能を活用して、大量のデータを管理およびクエリします。テーブルを作成する場合は、DISTRIBUTED BY HASH('<タブレットキー列>') BUCKETS <タブレット数> 句を使用してタブレットを構成します。詳細については、「パーティションとバケット」トピックのバケットセクションをご参照ください。
使用上の注意
パーティショニングが有効になっている場合、
DISTRIBUTED...句は、各パーティションのデータ分割ルールを記述します。 パーティショニングが有効になっていない場合、この句はテーブルの完全データの分割ルールを記述します。複数の列をタブレットキー列として指定できます。
Aggregate モデルと Unique モデルでは、タブレットキー列はキー列である必要があります。 Duplicate モデルでは、タブレットキー列はキー列または値列にすることができます。
カーディナリティの高い列をタブレットキー列として選択してデータを分散し、データスキューを防ぐことをお勧めします。
理論的には、タブレットの台数に制限はありません。
理論的には、1 つのタブレットに保存できるデータ量に制限はありません。ただし、1 GB から 10 GB のデータをタブレットに保存することをお勧めします。
各タブレットに含まれるデータ量が少量の場合、タブレットの数が増加し、メタデータ管理ワークロードが増大します。
各タブレットに大量のデータが含まれている場合、レプリカの移行に悪影響を及ぼし、分散クラスターを十分に活用できません。また、スキーマの変更やインデックスの作成などの失敗した操作の再試行コストも増加します。これらの操作はタブレットごとに行われます。
タブレットキー列を選択
テーブルを設計するときに選択するタブレットキー列は、クエリのパフォーマンスとクエリの同時実行性に重要な影響を与えます。次の表に、タブレットキー列を選択するためのルールを示します。ビジネスに複数のクエリ要件がある場合は、複数のタイプのタブレットキー列が必要になる場合があります。この場合、主要なクエリ要件に基づいてタブレット列を優先的に選択します。
ルール | メリット |
カーディナリティの高い列、または複数の列の組み合わせを選択して、均等なデータ分散を優先的に保証します | データはクラスタノード全体に均等に分散されます。分散システムのリソースをフルに活用して、フィルタリング効果が低く、大量のデータをスキャンする必要があるクエリのクエリパフォーマンスを向上させることができます。 |
フィルター条件で頻繁に使用される列を選択して、データのプルーニングとクエリの高速化のバランスを取ります | タブレットキー列の値が同じデータが集約されます。指定されたタブレットキー列をフィルター条件として使用するポイントクエリの場合、データプルーニングが高速化され、クエリの同時実行性が向上します。 説明 ポイントクエリは、多くの場合、特定の条件に基づいてデータベースから少量のデータを取得するために使用されます。このクエリメソッドは、プライマリキーやカーディナリティの高い列によるフィルタリングなど、特定の条件を満たす少量のデータをデータベースから正確に特定して取得します。 |
例
e コマースのシナリオでは、ほとんどの場合、注文ごとにデータをクエリする必要があります。場合によっては、完全な注文データに基づいて統計分析を実行する必要があります。この場合、注文情報テーブルのキー列からカーディナリティの高い列 order_id をタブレットキー列として選択して、データが複数のタブレットに均等に分散されるようにすることができます。 order_id 列のデータが集約され、上記のクエリに必要なパフォーマンスが提供されます。次のサンプルコードは、このようなシナリオのテーブルを作成する方法を示しています。
CREATE TABLE IF NOT EXISTS orders
(
`order_id` LARGEINT NOT NULL COMMENT "注文ID",
`order_time` DATETIME NOT NULL COMMENT "注文時間",
`customer_id` LARGEINT NOT NULL COMMENT "ユーザーID",
`total_amount` DOUBLE COMMENT "注文の合計金額",
`status` VARCHAR(20) COMMENT "注文ステータス",
`payment_method` VARCHAR(20) COMMENT "支払い方法",
`shipping_method` VARCHAR(20) COMMENT "配送方法",
`customer_city` VARCHAR(20) COMMENT "ユーザーが居住する都市",
`customer_address` VARCHAR(500) COMMENT "ユーザーの住所"
)
UNIQUE KEY(`order_id`, `order_time`)
PARTITION BY RANGE(`order_time`) ()
DISTRIBUTED BY HASH(`order_id`)
PROPERTIES (
"enable_unique_key_merge_on_write" = "true",
"dynamic_partition.enable" = "true",
"dynamic_partition.time_unit" = "DAY",
"dynamic_partition.start" = "-7",
"dynamic_partition.end" = "3",
"dynamic_partition.prefix" = "p",
"dynamic_partition.create_history_partition" = "true",
"dynamic_partition.buckets" = "16"
);インデックス
インデックスはデータベース設計において非常に重要であり、適切なインデックスを設定することでクエリのパフォーマンスを大幅に向上させることができます。インデックスは追加のストレージ容量を占有し、書き込みパフォーマンスを低下させる可能性があります。このセクションでは、ビジネス要件に基づいてインデックスを理解し、選択するのに役立つ、一般的に使用されるインデックスについて簡単に説明します。詳細については、「インデックスベースのアクセラレーション」をご参照ください。
インデックス作成のルール
ほとんどの場合、特定のキーを使用してプレフィックスインデックスが自動的に作成されます。これは最適なフィルタリング効果を提供します。ただし、テーブルにはプレフィックスインデックスを1つだけ設定できます。フィルター条件として最も頻繁に使用されるキーを選択することをお勧めします。
その他の高速フィルタリング要件には、転置インデックスが推奨されます。転置インデックスは幅広い適用範囲を持ち、複数の列の組み合わせをフィルター条件としてサポートします。軽量 Bloom フィルターインデックスと NGram Bloom フィルターインデックスを使用して、equivalent クエリおよび LIKE クエリで文字列を照合できます。
インデックスを選択する
ApsaraDB for SelectDB では、テーブルは組み込みインデックスまたはカスタムインデックスを使用できます。組み込みインデックスはシステムによって自動的に作成されます。カスタムインデックスは、テーブルの作成時または作成後に、ビジネス要件に基づいて作成できます。
メソッド | インデックスの種類 | サポートされているクエリのタイプ | サポートされていないクエリのタイプ | 利点 | 欠点 |
組み込み | プレフィックスインデックス |
|
| プレフィックスインデックスは比較的少量のストレージ容量を占有し、メモリに完全にキャッシュできます。これにより、システムはデータブロックをすばやく特定し、クエリ効率を大幅に向上させることができます。 | テーブルにはプレフィックスインデックスを1つしか設定できません。 |
カスタム | 転置インデックス(推奨) |
| 該当なし | 豊富なクエリタイプがサポートされています。ビジネス要件に基づいて、テーブルの作成時または作成後にインデックスを作成できます。インデックスを削除することもできます。 | これらのインデックスは大量のストレージ容量を占有します。 |
Bloom フィルターインデックス | 同等クエリ |
| これらのインデックスは、計算リソースとストレージリソースを少量しか占有しません。 | 同等クエリのみが Bloom フィルターインデックスをサポートします。 | |
NGram Bloom フィルターインデックス | Like クエリ |
| これらのインデックスは LIKE クエリを高速化し、計算リソースとストレージリソースを少量しか占有しません。 | NGram Bloom フィルターインデックスは LIKE クエリのみを高速化できます。 |
インデックスを使用する
転置インデックスを使用する
ApsaraDB for SelectDB は転置インデックスをサポートしています。転置インデックスを使用して、TEXT タイプのデータに対して全文検索を実行したり、通常のフィールドのデータに対して同等クエリまたは範囲クエリを実行したりできます。このようにして、大量のデータから特定の条件を満たすデータをすばやく取得できます。このセクションでは、転置インデックスを作成する方法について説明します。詳細については、「転置インデックス」をご参照ください。
テーブルの作成時に転置インデックスを作成する
e コマースのシナリオでは、ユーザー ID やユーザーアドレスなどのキーワードに基づいて注文情報を頻繁にクエリする必要があります。この場合、customer_id フィールドと customer_address フィールドに転置インデックスを作成して、クエリを高速化できます。次のサンプルコードは、このようなシナリオのテーブルを作成する方法を示しています。
CREATE TABLE IF NOT EXISTS orders
(
`order_id` LARGEINT NOT NULL COMMENT "注文ID", /* The order ID. */
`order_time` DATETIME NOT NULL COMMENT "注文時間", /* The order time. */
`customer_id` LARGEINT NOT NULL COMMENT "ユーザーID", /* The user ID. */
`total_amount` DOUBLE COMMENT "注文の合計金額", /* The total amount of the order. */
`status` VARCHAR(20) COMMENT "注文ステータス", /* The order status. */
`payment_method` VARCHAR(20) COMMENT "支払い方法", /* The payment method. */
`shipping_method` VARCHAR(20) COMMENT "配送方法", /* The shipping method. */
`customer_city` VARCHAR(20) COMMENT "ユーザーが居住する都市", /* The city in which the user resides. */
`customer_address` VARCHAR(500) COMMENT "ユーザーの住所", /* The address of the user. */
INDEX idx_customer_id (`customer_id`) USING INVERTED,
INDEX idx_customer_address (`customer_address`) USING INVERTED PROPERTIES("parser" = "chinese")
)
UNIQUE KEY(`order_id`, `order_time`)
PARTITION BY RANGE(`order_time`) ()
DISTRIBUTED BY HASH(`order_id`)
PROPERTIES (
"enable_unique_key_merge_on_write" = "true",
"dynamic_partition.enable" = "true",
"dynamic_partition.time_unit" = "DAY",
"dynamic_partition.start" = "-7",
"dynamic_partition.end" = "3",
"dynamic_partition.prefix" = "p",
"dynamic_partition.create_history_partition" = "true",
"dynamic_partition.buckets" = "16"
);既存のテーブルの列に転置インデックスを作成する
e コマースのシナリオでは、ユーザー ID に基づいて注文情報を頻繁にクエリする必要があります。ただし、orders テーブルの作成時に customer_id フィールドに転置インデックスは作成されません。この場合、次のステートメントを実行して、テーブルにインデックスを追加できます。
ALTER TABLE orders ADD INDEX idx_customer_id (`customer_id`) USING INVERTED;プレフィックスインデックスを使用する
プレフィックスインデックスは、キー列でソートされた基になるデータの1つ以上のキー列に作成されます。基本的に、プレフィックスインデックスは、データソート機能に基づくバイナリ検索です。 プレフィックスインデックスは組み込みインデックスです。テーブルの作成後に SelectDB によって自動的に作成されます。
プレフィックスインデックスを定義するための専用の構文はありません。システムは、テーブルの最初の1つ以上のキー列フィールドをプレフィックスインデックスとして選択します。プレフィックスインデックスの全長は 36 バイトを超えることはできません。VARCHAR タイプのフィールドの後のキー列フィールドは、プレフィックスインデックスに追加されません。
テーブル内のフィールドの順序は特に重要です。プレフィックスインデックスで使用されるフィールドが決まります。次のルールに基づいてキー列フィールドの順序を決定することを 強くお勧めします。
フィルター条件として頻繁に使用されるカーディナリティの高いキー列フィールドは、他のフィールドの前に配置されます。たとえば、「重複モデルを使用する」セクションでは、
log_timeフィールドはerror_codeフィールドの前に配置されます。同等フィルター条件として使用されるキー列フィールドは、範囲フィルター条件として使用されるキー列フィールドの前に配置されます。たとえば、「転置インデックスを使用する」セクションでは、範囲フィルタリング用の
order_timeフィールドはorder_idフィールドの後に配置されます。通常タイプのフィールドは VARCHAR タイプのフィールドの前に配置されます。たとえば、INT タイプのキー列フィールドは、VARCHAR タイプのキー列フィールドの前に配置されます。
例
転置インデックスを使用する セクションでは、注文情報テーブルはプレフィックスインデックス order_id+order_time を使用します。クエリ条件に order_id フィールドが含まれている場合、または order_id フィールドと order_time フィールドの両方が含まれている場合、クエリ速度が大幅に向上します。例1のクエリ速度は例2よりも高速です。
例 1
SELECT * FROM orders WHERE order_id = 1829239 and order_time = '2024-02-01';例 2
SELECT * FROM orders WHERE order_time = '2024-02-01';次のステップ
このチュートリアルの最初の 3 つのステップを学習すると、SelectDB for ApsaraDB の基本的な理解が得られ、ビジネス要件を満たすデータベーステーブルを設計できるようになります。次に、データの移行、外部データソースからのデータのクエリ、カーネルバージョンの更新など、実行できる詳細な操作について学習できます。詳細については、「次のステップ」をご参照ください。