執行查詢語句時,您可以通過列存索引的pruner功能,過濾掉數據庫中不需要訪問的數據塊,以提高SQL語句的查詢速度。本文介紹pruner的適用場景、注意事項、語法和相關參數等內容。
簡介
列存索引數據以單列數據塊(默認包含64K行,可以通過表的總行數/64K
來估算數據塊數量)粒度存儲,掃描數據時需要遍歷指定列的所有數據塊,并根據過濾條件來獲取滿足條件的數據。查詢數據量比較大的表時,掃描代價很大,如果表中的數據不能全部放在內存中,掃描代價會進一步加大。事實上,您可以通過訪問統計信息,再結合特定的過濾條件來過濾掉不需要訪問的數據塊以加快查詢速度。在PolarDB列存索引中,這種方法稱之為pruner。目前,Pruner有以下四種類型:
bloom filter
利用BIT數組表示一個集合,并且可以判斷某個元素是否屬于該集合。
minmax indexes
用于統計數據塊的最值。利用過濾條件與最值進行比較,從而判斷是否需要掃描數據塊。
token bloom filter
用于過濾字符串,按照非字母和非數字字符進行分割。如“I am IMCI”,將被分割為
I |am|IMCI
來存儲,適用于LIKE模糊查詢。ngram bloom filter
用于過濾字符串,按照指定長度進行分割。如“我是列存數據庫”,在指定ngram大小為3時將被分割為
我是列|是列存|列存數|存數據|數據庫
來存儲,適用于LIKE模糊查詢。
適用場景
Bloom filter:適用于等值條件以及IN條件,對于過濾性較強的等值條件,具有較好的過濾效果。如使用字符串ID進行等值過濾。
Minmax indexes:適用于對列數據分布有較好的局部性的場景,對于范圍過濾條件和等值過濾條件具有較好的過濾效果。如WHERE條件中帶有日期或排序字段。
Token bloom filter和ngram bloom filter適用于LIKE模糊查詢,來快速過濾未命中的數據塊。
存儲開銷
對于字符串類型的數據,開啟列存索引pruner查詢優化功能后會帶來一定的存儲開銷和占用一定的內存空間,您可以根據使用場景,選擇為指定的列構建bloom filter、minmax indexes、token bloom filter或ngram bloom filter。內存占用計算公式如下:
bloom filter/token bloom filter/ngram bloom filter
默認數據塊為64K,distinct值占總行數的比例大于3%時,計算公式如下:
占用內存=1.2*構建bloom filter的列數*表行數(單位:Byte)
默認數據塊為64K,distinct值占總行數的比例小于等于3%時,計算公式如下:
占用內存=1.2*構建bloom filter的列數*distinct值個數(單位:Byte)
該場景下,bloom filter過濾效果依賴數據的局部性,對于數據均勻分布的場景效果較差。
minmax indexes
minmax indexes內存占用計算公式如下:
占用內存=2*構建minmax indexes的列數*(表行數/數據塊大小)*前綴長度*字符集編碼長度
例如,表行數為20億,對其中10列構建minmax indexes,前綴長度為20,數據塊大小為64K,采用默認字符集utf8mb4(編碼長度為4),則占用的內存約為46 MB。
注意事項
集群版本為PolarDB MySQL版8.0.1.1.32及以下或8.0.2.2.13及以下的版本時,對包含NULL值的數據塊不會構建任何類型的pruner,并且不支持
IS NULL
或IS NOT NULL
過濾條件。集群版本為PolarDB MySQL版8.0.1.1.35及以上或8.0.2.2.16及以上的版本時,在創建列存索引時字符串類型的字段會默認構建pruner,且bloom filter使用LRU Cache(Least Recently Used Cache)進行內存管理。對于從低版本升級到8.0.1.1.35及以上或8.0.2.2.16及以上版本的集群,需要重建列存索引才會為字符串列構建pruner。
集群版本為PolarDB MySQL版8.0.1.1.34及以下或8.0.2.2.15及以下的版本時,構建的pruner會常駐內存。且在創建列存索引時字符串類型的字段不會默認構建pruner。
如果需要為字符串類型的字段構建pruner,則需要確保字符串中不包含'\0',如'polar\0db'。
系統會默認為int、decimal和datetime等數值類型的數據構建minmax indexes。
不支持為JSON類型以及GEOMETRY類型的字段構建minmax。
不支持為數值類型字段(例如INT、DECIMAL、DATETIME等)、JSON、BLOB、TEXT類型的字段構建bloomfilter。
語法說明
您可以在創建表的同時構建pruner,也可以在已創建的表上構建或刪除pruner。在已創建的表上構建或刪除pruner時,需要先刪除列索引,然后重建列索引。語法如下:
您可以通過DDL語句修改表的Schema中的COMMENT來為表中字符串類型的字段構建或刪除pruner。
所有的列級別的COMMENT屬性優先級均大于表級別的COMMENT屬性。
創建表的同時構建pruner
構建pruner(bloom filter)
為表中所有支持的字段構建bloom filter。示例如下:
CREATE TABLE t1 ( id INT PRIMARY KEY, str_col1 char(10), str_col2 varchar(10) ) ENGINE InnoDB COMMENT "COLUMNAR=1 PRUNER_BLOOM=1"; /*comment中帶pruner_bloom屬性*/
為表中的某列構建bloom filter。示例如下:
CREATE TABLE t1 ( id INT PRIMARY KEY, str_col1 char(10) "PRUNER_BLOOM=1", /*comment中帶pruner_bloom屬性*/ str_col2 varchar(10) ) ENGINE InnoDB COMMENT "COLUMNAR=1";
說明集群版本為PolarDB MySQL版8.0版本且修訂版本為8.0.1.1.32及以上或8.0.2.2.13及以上的版本時,支持使用
PRUNER_BLOOM
屬性。構建pruner(minmax indexes)
由于字符串類型的字段長度可能會非常長,為了減少minmax占用的內存空間,系統默認截取字符串類型字段的前20個字符,最多255個字符與最值進行比較。您可以通過PRUNER_MINMAX屬性控制是否構建字符串minmax,通過PREFIX_LEN控制前綴長度。
說明字符個數與編碼長度無關,例如:“阿里云PolarDB”的前2個字符為”阿里“,前5個字符為”阿里云Po"。
為表中所有字符串類型的字段構建minmax indexes。示例如下:
CREATE TABLE t1 ( id INT PRIMARY KEY, str_col1 char(10), str_col2 varchar(10) ) ENGINE InnoDB COMMENT "COLUMNAR=1 PRUNER_MINMAX=1 PREFIX_LEN=30"; /*comment中帶pruner_minmax屬性,并指定前綴長度為30個字符*/
為表中的某列構建minmax indexes。示例如下:
CREATE TABLE t1 ( id INT PRIMARY KEY, str_col1 char(10) "PRUNER_MINMAX=1 PREFIX_LEN=30", /*comment中帶pruner_minmax屬性,并指定前綴長度為30*/ str_col2 varchar(10) "PRUNER_MINMAX=1 PREFIX_LEN=10" /*comment中帶pruner_minmax屬性,并指定前綴長度為10*/ ) ENGINE InnoDB COMMENT "COLUMNAR=1";
說明集群版本為PolarDB MySQL版8.0版本且修訂版本為8.0.1.1.32及以上或8.0.2.2.13及以上的版本時,支持使用
PRUNER_MINMAX
和PREFIX_LEN
屬性。構建token bloom filter
為表中所有支持的字段構建token bloom filter。示例如下:
CREATE TABLE t1 ( id INT PRIMARY KEY, str_col1 char(10), str_col2 varchar(10) ) ENGINE InnoDB COMMENT "COLUMNAR=1 PRUNER_TOKEN_BLOOM=1";
為表中的某列構建token bloom filter。示例如下:
CREATE TABLE t1 ( id INT PRIMARY KEY, str_col1 char(10) "PRUNER_TOKEN_BLOOM=1", str_col2 varchar(10) ) ENGINE InnoDB COMMENT "COLUMNAR=1";
說明當集群版本滿足以下條件時,支持使用
PRUNER_TOKEN_BLOOM
屬性:集群版本為PolarDB MySQL版8.0.1版本,且修訂版本為8.0.1.1.39及以上。
集群版本為PolarDB MySQL版8.0.2版本,且修訂版本為8.0.2.2.20及以上。
構建ngram bloom filter
為表中所有支持的字段構建ngram bloom filter。示例如下:
CREATE TABLE t1 ( id INT PRIMARY KEY, str_col1 char(10), str_col2 varchar(10) ) ENGINE InnoDB COMMENT "COLUMNAR=1 PRUNER_NGRAM_BLOOM=2";
為表中的某列構建ngram bloom filter。示例如下:
CREATE TABLE t1 ( id INT PRIMARY KEY, str_col1 char(10) "PRUNER_NGRAM_BLOOM=3", str_col2 varchar(10) ) ENGINE InnoDB COMMENT "COLUMNAR=1";
說明當集群版本滿足以下條件時,支持使用
PRUNER_TOKEN_BLOOM
屬性:集群版本為PolarDB MySQL版8.0.1版本,且修訂版本為8.0.1.1.39及以上。
集群版本為PolarDB MySQL版8.0.2版本,且修訂版本為8.0.2.2.20及以上。
PRUNER_NGRAM_BLOOM=N
,當N大于等于2時,為分割長度。N一般推薦不小于LIKE "%字符串%"
中的字符長度。當LIKE "%字符串%"
中的字符長度小于N時,則無法使用ngram bloom filter。
在已創建的表上構建或刪除pruner
在已創建的表上構建或刪除pruner,都需要重新構建列索引,即先刪除列索引,再重新構建列索引。在構建列索引之前,需要先增加或刪除COMMENT中的pruner屬性,此處以PRUNER_MINMAX
屬性為例進行說明。
構建pruner
假設原表結構如下:
Table: t1 Create Table: CREATE TABLE `t1` ( `id` int(11) NOT NULL, `str_col1` char(10) DEFAULT NULL, `str_col2` varchar(10) DEFAULT NULL, PRIMARY KEY (`id`), COLUMNAR INDEX (`id`,`str_col1`,`str_col2`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='COLUMNAR=1'
為表
t1
中所有字符串類型的字段構建minmax pruner,操作步驟如下:執行以下命令,刪除表
t1
上的列索引。ALTER TABLE t1 COMMENT = "COLUMNAR=0";
執行以下命令,為表
t1
中所有字符串類型的字段構建minmax pruner。ALTER TABLE t1 COMMENT = "COLUMNAR=1 PRUNER_MINMAX=1";
(可選)執行以下命令,查看構建pruner后的表結構。
SHOW CREATE TABLE t1 FULL \G
查詢結果如下:
*************************** 1. row *************************** Table: t1 Create Table: CREATE TABLE `t1` ( `id` int(11) NOT NULL, `str_col1` char(10) DEFAULT NULL, `str_col2` varchar(10) DEFAULT NULL, PRIMARY KEY (`id`), COLUMNAR INDEX (`id`,`str_col1`,`str_col2`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='COLUMNAR=1 PRUNER_MINMAX=1'
由以上表結構可以看出,表
t1
中已添加PRUNER_MINMAX
屬性。
為表
t1
中的str_col1
列構建minmax pruner,操作步驟如下:執行以下命令,為
str_col1
列構建pruner。ALTER TABLE t1 MODIFY COLUMN str_col1 char(10) COMMENT 'PRUNER_MINMAX=1';
按順序執行以下命令,為表
t1
重建列索引。ALTER TABLE t1 COMMENT = "COLUMNAR=0"; ALTER TABLE t1 COMMENT = "COLUMNAR=1";
(可選)執行以下命令,查看重建列索引后的表結構。
SHOW CREATE TABLE t1 FULL \G
查詢結果如下:
*************************** 1. row *************************** Table: t1 Create Table: CREATE TABLE `t1` ( `id` int(11) NOT NULL, `str_col1` char(10) DEFAULT NULL COMMENT 'PRUNER_MINMAX=1', `str_col2` varchar(10) DEFAULT NULL, PRIMARY KEY (`id`), COLUMNAR INDEX (`id`,`str_col1`,`str_col2`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='COLUMNAR=1'
由以上表結構可以看出,已為
str_col1
列添加PRUNER_MINMAX
屬性。
刪除pruner
刪除表上的
PRUNER_MINMAX
屬性。假設表
t1
的表結構如下:SHOW CREATE TABLE t1 full \G *************************** 1. row *************************** Table: t1 Create Table: CREATE TABLE `t1` ( `id` int(11) NOT NULL, `str_col1` char(10) DEFAULT NULL, `str_col2` varchar(10) DEFAULT NULL, PRIMARY KEY (`id`), COLUMNAR INDEX (`id`,`str_col1`,`str_col2`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='COLUMNAR=1 PRUNER_MINMAX=1'
刪除表
t1
中的PRUNER_MINMAX
屬性,操作步驟如下:執行以下命令,刪除表
t1
上的列索引。ALTER TABLE t1 COMMENT = "COLUMNAR=0";
執行以下命令,將COMMENT中的
PRUNER_MINMAX
設置為0,并重建列索引。ALTER TABLE t1 COMMENT = "COLUMNAR=1 PRUNER_MINMAX=0";
(可選)執行以下命令,查看重建列索引后的表結構。
SHOW CREATE TABLE t1 FULL \G
查詢結果如下:
*************************** 1. row *************************** Table: t1 Create Table: CREATE TABLE `t1` ( `id` int(11) NOT NULL, `str_col1` char(10) DEFAULT NULL, `str_col2` varchar(10) DEFAULT NULL, PRIMARY KEY (`id`), COLUMNAR INDEX (`id`,`str_col1`,`str_col2`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='COLUMNAR=1'
由以上表結構可以看出,表
t1
中的PRUNER_MINMAX
屬性已被刪除。
刪除列上的
PRUNER
屬性。假設表
t1
中的str_col1
列上存在PRUNER_MINMAX
屬性。表結構如下:Table: t1 Create Table: CREATE TABLE `t1` ( `id` int(11) NOT NULL, `str_col1` char(10) DEFAULT NULL COMMENT 'PRUNER_MINMAX=1', `str_col2` varchar(10) DEFAULT NULL, PRIMARY KEY (`id`), COLUMNAR INDEX (`id`,`str_col1`,`str_col2`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='COLUMNAR=1'
刪除
str_col1
列上的PRUNER_MINMAX
屬性,操作步驟如下:執行以下命令,刪除
str_col1
列上的PRUNER_MINMAX
屬性。ALTER TABLE t1 MODIFY COLUMN str_col1 char(10) COMMENT 'PRUNER_MINMAX=0';
按順序執行以下命令,重建列索引。
ALTER TABLE t1 COMMENT = "COLUMNAR=0"; ALTER TABLE t1 COMMENT = "COLUMNAR=1";
(可選)執行以下命令,查看重建列索引后的表結構。
SHOW CREATE TABLE t1 FULL \G
查詢結果如下:
*************************** 1. row *************************** Table: t1 Create Table: CREATE TABLE `t1` ( `id` int(11) NOT NULL, `str_col1` char(10) DEFAULT NULL, `str_col2` varchar(10) DEFAULT NULL, PRIMARY KEY (`id`), COLUMNAR INDEX (`id`,`str_col1`,`str_col2`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='COLUMNAR=1'
由以上表結構可以看出,
str_col1
列上的PRUNER_MINMAX
屬性已被刪除。
查看pruner是否生效
查看表上的字符串類型的字段是否構建了pruner
您可以通過
imci_secondary_indexes
表中的STR_BLOOM_PRUNER
和STR_MINMAX_PRUNER
字段的狀態值來判斷表中的字符串列是否構建了pruner。當狀態值為1時,表示創建了對應的pruner。示例如下:SELECT * FROM information_schema.imci_secondary_indexes WHERE schema_name='test_tmp' AND table_name='t1'\G
查詢結果如下:
*************************** 1. row *************************** TABLE_ID: 1091 SCHEMA_NAME: test_tmp TABLE_NAME: t1 COLUMN_NAME: str_col1 STR_BLOOM_PRUNER: 1 --str_col1 建了bloom filter STR_MINMAX_PRUNER: 1 --str_col1 建了minmax SINDEX_SWITCH: 0 *************************** 2. row *************************** TABLE_ID: 1091 SCHEMA_NAME: test_tmp TABLE_NAME: t1 COLUMN_NAME: str_col2 STR_BLOOM_PRUNER: 1 --str_col2 建了bloom filter STR_MINMAX_PRUNER: 1 --str_col2 建了minmax SINDEX_SWITCH: 0 2 rows in set (0.00 sec)
由以上查詢結果可以看出:
STR_BLOOM_PRUNER
字段的狀態值為1,表示對str_col1
和str_col2
構建了bloom filter。STR_MINMAX_PRUNER
狀態值為1,表示對str_col1
和str_col2
構建了minmax indexes。查看構建的pruner對查詢語句是否生效
您可以在執行查詢語句前后,執行
SHOW STATUS LIKE 'imci_pruner%'
命令查看數據塊過濾情況,以此來判斷pruner對該查詢是否生效。查詢結果中的狀態值說明如下:imci_pruner_accepted
:滿足過濾條件的數據塊數量。imci_pruner_rejected
:不滿足過濾條件的數據塊數量。
通過列存索引pruner功能跳過掃描的數據塊數量=accepted數量+rejected數量
被accept的數據塊,無需在每條記錄上使用過濾條件進行過濾,如果部分列需要被物化,則仍然需要訪問該數據塊;被reject的數據塊,直接跳過掃描,不會產生任何IO。
示例
以表
t1
為例,判斷pruner對查詢語句是否生效。表t1
的表結構如下:Table: t1 Create Table: CREATE TABLE `t1` ( `id` int(11) NOT NULL, `str_col1` char(10) DEFAULT NULL, `str_col2` varchar(10) DEFAULT NULL, PRIMARY KEY (`id`), COLUMNAR INDEX (`id`,`str_col1`,`str_col2`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='COLUMNAR=1 PRUNER=1'
假設表中包含10個數據塊,符合條件
str_col1='polardb'
的記錄集中在其中一個數據塊中,執行以下步驟,查看pruner是否對SELECT COUNT(1) FROM t1 WHERE str_col1='polardb'
查詢語句生效。執行以下命令,查看當前的pruner狀態信息。
SHOW STATUS LIKE 'imci_pruner%';
查詢結果如下:
+----------------------+-------+ | Variable_name | Value | +----------------------+-------+ | imci_pruner_accepted | 0 | | imci_pruner_rejected | 0 | +----------------------+-------+ 2 rows in set (0.00 sec)
執行以下命令,查詢符合條件
str_col1='polardb'
的數量。SELECT COUNT(1) FROM t1 WHERE str_col1='polardb';
查詢結果如下:
+----------+ | count(1) | +----------+ | 1 | +----------+ 1 row in set (0.01 sec)
執行以下命令,再次查看pruner狀態信息。
SHOW STATUS LIKE 'imci_pruner%';
查詢結果如下:
+----------------------+-------+ | Variable_name | Value | +----------------------+-------+ | imci_pruner_accepted | 0 | | imci_pruner_rejected | 9 | +----------------------+-------+ 2 rows in set (0.00 sec)
由以上查詢結果可以看出,
imci_pruner_accepted
結果為0,imci_pruner_rejected
為9,查詢過程中跳過了9個數據塊,以此判斷出pruner對該查詢生效。
性能測試
以包含1.2億行數據的表為例,表中包含約1800個數據塊,且集群內存為2核4 GB,表中字符串類型的字段col
的distinct值為8000萬,分別測試在該列構建bloom filter和不構建bloom filter兩種場景下的查詢性能。 查詢語句如下:
SELECT COUNT(1) FROM t1 WHERE col='xxx'
查詢時間見下表:
構建bloom filter | 不構建bloom filter |
0.15s | 18.6s |
SQL語句中的col='xxx'
條件結合bloom filter過濾掉了絕大部分的數據塊,實際的查詢過程中僅需要掃描幾個數據塊,從而提升了查詢性能。