良好的表結構設計不僅能支持豐富的功能需求,還能大幅提升數據庫系統的性能、可維護性和可擴展性等,因此數據庫的表結構設計至關重要。本文為您介紹在云數據庫 SelectDB 版中,設計表結構時需要重點關注的表屬性,并幫助您快速掌握如何根據業務場景選擇合適的表設計,從而更好的滿足業務需求。
重要表屬性概覽
在業務接入SelectDB時,根據業務場景做好重要表屬性的設計,對于構建出能滿足業務需求且高性能、易維護的表結構至關重要。以下是SelectDB重要表屬性的快速概覽。
表屬性 | 是否必選 | 屬性關鍵作用 | 詳情鏈接 |
數據模型 | 是 | 不同的數據模型適用于不同的業務場景:Unique模型支持主鍵唯一性約束,可滿足靈活高效的數據更新需求。 Duplicate模型采用追加寫模式,適用于明細數據的高性能分析場景。 Aggregate模型支持數據預聚合,專注于數據聚合統計場景。 | |
分桶 | 是 | 分桶用于將數據分散到集群中的不同節點,以充分利用分布式系統的分而治之的能力來管理和查詢海量數據。 | |
分區 | 否 | 分區能夠根據指定字段(如時間、地域等)將原始表劃分為多個子表,以便于對數據進行分區管理和查詢,同時利用分區裁剪來提升查詢速度。 | |
索引 | 否 | 索引能夠快速地過濾或定位數據,從而大幅提升查詢性能。 |
數據模型
數據模型的合理選擇,對于能否滿足數據分析場景的功能需求和性能要求具有決定性影響。不同的模型適用于不同的業務場景。此處僅對各個模型進行簡要介紹,旨在幫助您快速了解數據模型,以便于您進行模型選擇。更多詳情,請參見專題介紹文章數據模型。
基礎概念
在SelectDB中,數據通過表(Table)的形式在邏輯層面進行組織和管理。每張表由行(Row)和列(Column)組成。行表示數據表中的一行數據,而列用于描述該行數據中的不同字段。
列可以分為以下兩大類:
Key列:Key列是指建表語句中被關鍵字
UNIQUE KEY
、AGGREGATE KEY
或DUPLICATE KEY
修飾的列。Value列:除Key列外,其余列均為Value列。
模型選擇指導
在SelectDB中,表的數據模型分為三種,分別為Unique模型、Duplicate模型和Aggregate模型。
數據模型在建表時已確定且不可修改。
如果在建表時未指定數據模型,將默認采用Duplicate模型,并自動選擇前三列作為Key列。
Unique模型、Duplicate模型和Aggregate模型中,數據均按照Key列進行排序存儲。
模型類型 | 模型特點 | 適用場景 | 模型不足 |
Unique | 每一行的Key值唯一。 Key列值相同時,多行數據的Value列會按寫入的先后順序進行覆蓋。 | 適用于對數據有唯一主鍵要求或高效更新要求的場景。例如電商訂單、用戶屬性信息等數據分析場景。 |
|
Duplicate | 允許多行的Key值相同。 Key列值相同時,多行數據同時存儲在系統中。 | 數據的寫入和查詢效率極高,適用于保留所有原始數據記錄的場景。例如日志、賬單等明細數據分析場景。 |
|
Aggregate | 每一行的Key值唯一。 Key列值相同時,多行數據的Value列會按照建表時指定的聚合方式進行預聚合。 | 類似于傳統數據倉庫的Cube模型,適用于通過預聚合提升查詢性能的聚合統計場景。例如網站流量分析、定制化報表等數據分析場景。 |
|
快速使用模型
Unique模型
在Unique模型中,Key列值相同時,多行數據的Value列會按寫入的先后順序進行覆蓋。Unique模型提供了兩種實現方式:讀時合并(MOR,merge-on-read)和寫時合并(MOW,merge-on-write)。
由于寫時合并技術已非常成熟且穩定,并且能夠提供優異的查詢性能,因此建議您優先采用寫時合并的實現方式。此處僅簡要介紹Unique模型的寫時合并。有關讀時合并的詳細信息,請參見讀時合并(MOR)。
注意事項
創建Unique模型且為寫時合并實現方式的表時,須在建表時注意以下事項。
通過
UNIQUE KEY
指定主鍵唯一的字段。在PROPERTIES中添加開啟寫時合并的屬性。
"enable_unique_key_merge_on_write" = "true"
示例
創建orders表語句如下。其表示將orders表指定為Unique模型,同時將orders表主鍵設定為由order_id和order_time組成的聯合主鍵,并啟用寫時合并模式。
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模型中,Key列值相同時,多行數據同時存儲在系統中,沒有預聚合、主鍵唯一性約束等特點。
例如,您希望記錄并分析業務系統產生的日志數據,且期望數據按照日志時間、日志類型、錯誤碼進行排序存儲,您可以選擇此模型。具體創建log表的語句如下,其表示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模型中,Key列值相同時,多行數據的Value列會按照建表時指定的聚合方式進行預聚合。因此,在創建Aggregate模型的表時,需特別關注以下事項。
通過
AGGREGATE KEY
指定Key列,相同Key列的數據行將進行聚合。指定Value的聚合方式。目前支持的聚合方式如下:
聚合方式參數
參數說明
SUM
求和。適用數值類型。
MIN
求最小值。適合數值類型。
MAX
求最大值。適合數值類型。
REPLACE
替換。對于維度列相同的行,指標列會按照導入的先后順序,后導入的替換先導入的。
REPLACE_IF_NOT_NULL
非空值替換。和REPLACE的區別在于對于null值,不做替換。這里要注意的是字段默認值要給NULL,而不能是空字符串,如果是空字符串,會給你替換成空字符串。
HLL_UNION
HLL類型的列的聚合方式,通過HyperLogLog算法聚合。
BITMAP_UNION
BITMAP類型的列的聚合方式,進行位圖的并集聚合。
示例
例如,您需要對用戶行為進行統計分析,記錄其最后訪問時間、總消費額、最大停留時間和最短停留時間,您可以選擇此模型。具體創建user_behavior表的語句如下。其表示當多條數據的Key列(用戶ID、數據寫入日期、用戶所在城市、用戶年齡和用戶性別)相同時,用戶的Value列進行預聚合。聚合規則如下:
用戶最后一次訪問時間:取多條用戶行為數據中last_visit_date字段的最大值。
用戶總消費:多條數據中用戶消費的總和。
用戶最大停留時間:取多條用戶行為數據中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"
);
數據劃分概述
SelectDB支持兩層數據劃分,如下圖所示。第一層是分區(Partition),用于對數據進行邏輯劃分,分區是用戶進行數據管理的最小單元。第二層是分桶(Tablet),用于對數據進行物理劃分,分桶是系統進行數據打散、移動等操作的最小單元。
分區與分桶的關聯
一個分桶僅屬于一個分區,而一個分區則包含多個分桶。
在建表過程中,如果采用了分區(Partition),則表先按分區規則進行劃分,分區內再按指定的分桶規則進行劃分;如果未使用分區,則表直接按指定的分桶規則進行劃分。
在數據寫入過程中,數據首先被劃分至相應的分區,隨后在分區內依據分桶規則進一步寫入至不同的分桶中。分桶是對分區數據的進一步細分,其目的是為了更加均勻地分布數據,從而提升查詢效率。
分區(Partition)
在SelectDB的存儲引擎中,分區是一種數據組織方式,用于將表中的數據按照用戶定義的規則劃分為多個獨立的部分,從而實現數據的邏輯劃分。這有助于提升查詢效率,同時也使得數據管理更加靈活和便捷。此處僅對分區進行簡要介紹,旨在幫助您快速了解分區,以便于您進行分區選型。更多詳情,請參見分區和動態分區。
分區選擇指導
SelectDB支持兩種分區方式:Range分區和List分區。同時,也提供了簡單易用的動態分區功能,以實現對分區的自動化管理。不同的分區方式,適用于不同的業務場景。
分區方式 | 支持的列類型 | 指定分區信息方式 | 適用場景 |
Range | 列類型:DATE、DATETIME、TINYINT、SMALLINT、INT、BIGINT、LARGEINT | 支持四種寫法:
| 適用于對數據劃分區間進行管理,典型的場景是按時間進行分區。 |
List | 列類型:BOOLEAN、TINYINT、SMALLINT、INT、BIGINT、LARGEINT、DATE、DATETIME、CHAR、VARCHAR | 支持通過 | 適用于依據數據的既有類別或固定特性進行數據管理,分區列通常為枚舉值,例如根據用戶所屬地域進行數據劃分管理。 |
注意事項
SelectDB的表可分為分區表和無分區表。該屬性可選,在建表時確定是否進行分區,之后不可更改。具體而言,對于分區表,您可以在后續使用過程中對分區進行增刪操作;而對于無分區表,則無法再進行增加分區等操作。
分區列必須為Key列,可指定一列或多列。
不論分區列是什么類型,在寫分區值時,都需要加雙引號。
分區的數量在理論上并沒有上限。
在創建分區時,必須確保每個分區的取值范圍不重疊。
快速使用分區
Range分區
Range分區是指按照分區字段范圍,對數據進行劃分管理,是最常用的分區方式。典型的使用場景是按照時間進行數據分區,方便對海量的時間序數據進行管理、查詢優化等。
分區分桶的最終目的是合理地劃分數據,分區規則設置合理性的主要標準是:
在分區及分桶規則下,每個分桶(Tablet)的數據量在1~10 GB的范圍內;
根據您管理數據的粒度確定分區粒度(例如,在日志場景下,您通常需要按天淘汰歷史數據,此時選擇以天為分區粒度比較合適)。
在日志場景下,經常按照時間范圍過濾查詢數據,且需要按照時間淘汰歷史分區,您可以將log_time
字段指定為分區列,并采取按天分區的方式,示例如下。
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`)
(
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"))
,系統不會掃描剩余兩個分區的數據,從而提高了查詢數據的速度。
SELECT * FROM orders WHERE order_time = '2024-02-02';
List分區
List分區是按照分區字段的枚舉值,對數據進行劃分管理。當對采用List分區的表進行查詢時,可結合過濾條件快速進行分區裁剪,提升查詢性能。
您可以根據操作業務數據時常用的字段來選擇List分區列。需要注意的是,各個分區之間的數據量要均勻,避免嚴重的數據傾斜。
例如,在電商場景中,訂單數據量通常非常龐大,且某些場景經常需要根據訂單用戶所屬城市來查詢分析此類數據。因此,為了更方便地管理和查詢數據,可以將customer_city
字段指定為分區列。假如數據量按地域的分布如下:
北京、上海和中國香港預計有6GB。
紐約、舊金山預計有5GB。
東京預計有5GB。
此時您可以按照以下示例進行分區。
CREATE TABLE IF NOT EXISTS orders
(
`order_id` LARGEINT NOT NULL COMMENT "訂單id",
`order_time` DATETIME NOT NULL COMMENT "訂單時間",
`customer_city` VARCHAR(20) 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_address` VARCHAR(500) COMMENT "用戶地址"
)
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")
,系統不會掃描剩余兩個分區的數據,從而提高了查詢數據的速度。
SELECT * FROM orders WHERE customer_city = 'Tokyo';
使用動態分區
實際生產環境中,數據表的分區數量可能比較多,此時手動管理分區的工作將變得十分繁瑣,這為數據庫管理人員帶來了額外的維護成本。SelectDB允許在建表時設定動態分區規則以進行自動化分區管理。
例如,在電商中,針對訂單信息表,經常按照時間范圍過濾查詢數據,且需要對歷史訂單進行轉儲歸檔的場景。您可以將order_time
字段指定為分區列,并在PROPERTIES中設置動態分區屬性。比如在PROPERTIES中設置該分區采取按天分區(dynamic_partition.time_unit)的方式,只保留最近180天(dynamic_partition.start)的分區,并且預先創建未來3天(dynamic_partition.end)的分區,示例如下。
下述語句中PARTITION BY RANGE(`order_time`) ()
末尾的()
并非語法錯誤,如果您要使用動態分區,此括號為固定語法,必不可少。
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" = "-180",
"dynamic_partition.end" = "3",
"dynamic_partition.prefix" = "p",
"dynamic_partition.create_history_partition" = "true",
"dynamic_partition.buckets" = "16"
);
當您預計表的分區數量較多時,強烈建議您學習動態分區的相關內容,詳情請參見動態分區。
分桶(Tablet)
在SelectDB的存儲引擎中,數據根據指定列的Hash值劃分到不同的分桶(Tablet),分桶則由集群中的不同節點進行管理,從而利用分布式系統的能力來進行管理、查詢海量數據。在建表時,通過DISTRIBUTED BY HASH(`<分桶列>`) BUCKETS <分桶數量>
進行分桶的設置。有關分桶的詳細信息,請參見分桶。
注意事項
如果建表時使用了分區(Partition),則
DISTRIBUTED...
語句所描述的是數據在各個分區內的劃分規則;如果未使用分區,則所描述的是對整個表的數據劃分規則。分桶列可以包含多個。
對于Aggregate和Unique模型,分桶列必須為Key列;而對于Duplicate模型,分桶列則沒有限制。
分桶列建議選擇唯一值多的高基數列,以便打散數據,避免數據傾斜。
分桶(Tablet)的數量理論上沒有上限。
單個分桶(Tablet)的數據量理論上沒有上下界,但建議在1~10 GB的范圍內。
如果單個分桶(Tablet)數據量過小,容易導致分桶過多,元數據管理壓力增大。
如果單個分桶(Tablet)數據量過大,不利于副本的遷移、分布式集群的充分利用,增加Schema變更或者索引創建等操作失敗重試的代價(這些操作失敗重試的粒度是Tablet)。
分桶列選擇指導
在表設計過程中,分桶列的選擇對查詢的性能和并發量有著重要影響,分桶列選擇的原則如下所示。當業務中存在多種查詢需求時,會產生多種分桶列的期望,此時應優先根據最主要查詢的需求進行選擇。
選擇原則 | 作用 |
優先保障數據均勻打散,選擇高基數列或多列組合。 | 數據在集群節點上分布更均衡。對于過濾效果不佳、進行大量數據掃描的查詢,可充分利用分布式系統的資源提升查詢性能。 |
選擇經常用于查詢過濾條件的列,兼顧數據裁剪加速查詢。 | 相同分桶列的數據聚集在一起。對于指定分桶列作為過濾條件的點查詢,可快速進行數據裁剪提高查詢并發。 說明 點查詢通常用于從數據庫中檢索特定條件下的少量數據。這種查詢通過指定特定的條件(例如通過主鍵、高基數列進行過濾),來精確確定位并獲取數據庫中符合條件的少量數據。 |
使用示例
在電商場景中,大量查詢按照訂單維度進行過濾查詢,也有部分查詢對全量訂單數據進行統計分析。此時,您可以選擇訂單信息表Key列中的高基數列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"
);
索引
索引在數據庫設計中至關重要,合適的索引可大幅提高查詢性能。創建索引存在一定的成本,可能導致額外的存儲空間占用以及寫入性能的下降。此處僅對常用索引進行簡要介紹,以幫助您快速了解索引,以便于您進行索引選型。更多詳情,請參見索引加速。
設計指南
最常使用的過濾條件應指定為Key自動建立前綴索引,因為其過濾效果最佳。然而,一個表只能擁有一個前綴索引,因此建議將其應用于最頻繁的過濾條件上。
對于其他過濾加速需求,首選創建倒排索引,因其適用范圍廣泛,支持多條件組合。對于字符串的等值、LIKE匹配查詢場景,可考慮輕量級的BloomFilter、NGram BloomFilter索引。
索引選擇指導
在SelectDB中,表的索引的構建方式有內建和自定義兩種。對于內建索引,系統會自動創建。而對于自定義索引,需要您在建表時或者建表后,根據需要自行創建。
構建方式 | 索引類型 | 支持的查詢類型 | 不支持的查詢類型 | 優勢 | 劣勢 |
內建 | 前綴索引 |
|
| 前綴索引占用的空間相對較小,能夠在內存中進行全量緩存,從而實現快速定位數據塊,顯著提升查詢效率。 | 一個表只能有一個前綴索引。 |
自定義 | 倒排索引(推薦) |
| 無 | 支持的查詢類型豐富。支持在建表時、建表后按需創建索引,并支持索引刪除。 | 索引存儲空間相對較大。 |
BloomFilter索引 | 等于查詢 |
| 索引構建占用的計算和存儲資源少。 | 支持的查詢類型少,只支持等于查詢。 | |
NGram BloomFilter索引 | LIKE查詢 |
| 提高LIKE查詢速度,索引構建占用的計算和存儲資源少。 | 只支持LIKE加速。 |
快速使用索引
倒排索引
SelectDB支持倒排索引,可用于滿足文本字段的全文檢索、以及普通字段的等值或范圍查詢,能夠快速從大量數據中篩選出滿足條件的數據。此處僅簡紹如何創建倒排索引,更多信息,請參見倒排索引。
建表時創建索引
在電商場景中,根據用戶ID、用戶地址關鍵詞查詢訂單信息是高頻操作,此時可以在customer_id
和customer_address
字段上建立倒排索引來提高查詢速度。具體建表語句如下。
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 "用戶地址",
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"
);
已有表創建索引
在電商場景中,根據用戶ID查詢訂單信息數據是一個高頻操作,但如果建表時沒有為customer_id
字段創建倒排索引,可以使用以下語句為其增加索引。
ALTER TABLE orders ADD INDEX idx_customer_id (`customer_id`) USING INVERTED;
前綴索引
前綴索引是在底層數據按照Key列排序的基礎上,選擇前綴的一個或多個Key列構建的索引,其本質是基于數據排序特性進行二分查找。前綴索引屬于內建索引,建表后SelectDB自動創建。
前綴索引沒有專門的語法去定義,系統會根據建表的Key列字段定義順序,選取前36個字節所能覆蓋的字段作為前綴索引,但當遇到VARCHAR類型時,前綴索引會直接截斷,后面的Key列不再加入前綴索引。
建表時字段定義的順序尤為重要,它決定了哪些字段會被用作前綴索引,強烈建議您參考如下原則確定Key列順序:
高頻用于過濾條件的、高基數的Key列放在其他字段之前。如Duplicate模型章節的日志場景中,日志時間
log_time
放在錯誤碼error_code
之前。等值過濾條件的Key列放在區間過濾條件的Key列之前。如倒排索引章節的電商場景中,時間
order_time
通常按照區間過濾,放在訂單號order_id
之后。普通類型字段放在VARCHAR類型字段之前。如INT類型的Key列放在VARCHAR類的KEY列之前。
使用示例
在倒排索引章節的電商場景中,訂單信息表的前綴索引為order_id+order_time
,當查詢條件是前綴索引的前綴時(即查詢條件包含order_id或同時包含order_id和order_time),可以極大的加快查詢速度。如以下兩個示例,示例一的查詢速度會遠高于示例二的查詢速度。
示例一
SELECT * FROM orders WHERE order_id = 1829239 and order_time = '2024-02-01';
示例二
SELECT * FROM orders WHERE order_time = '2024-02-01';
下一步
完成本教程的前三個步驟后,您已初步了解SelectDB,并具備設計符合業務需求的數據庫表的基本能力。下一步,您可以根據后續指引,了解您業務涉及的具體功能,例如數據遷移、查詢外部數據源、版本升級等。更多功能,請參見后續指引。