本文介绍如何创建和管理列存表,包括设置排序键、转换表格式等核心操作,以及通过分析执行计划来优化查询性能的方法。
创建列存表
本章节介绍如何创建一张新的列存表,包括基础创建、设置排序键以优化性能,以及了解存储策略。
创建单表
创建一张最基础的列存表。您只需在CREATE TABLE语句中指定ENGINE=XEngine和TABLE_FORMAT=COLUMN即可。
-- 创建一张用于存储销售记录的列存表
CREATE TABLE sales_records (
sale_id INT PRIMARY KEY AUTO_INCREMENT,
product_id INT NOT NULL,
region VARCHAR(50),
sale_date DATE,
amount DECIMAL(10, 2)
) ENGINE=XEngine, TABLE_FORMAT=COLUMN;使用排序键(order key)优化查询性能
通过预排序数据,大幅提升特定查询的性能。排序键(order key)是列存表的核心优化手段。它决定了数据的物理存储顺序。一个设计良好的排序键可以使过滤、聚合和排序操作的性能提升数倍。
决策建议
高频过滤列:将最常用于
WHERE条件进行范围查询或等值查询的列作为排序键,特别是时间列(如sale_date)。高频分组列:将经常用于
GROUP BY的维度列放入排序键。高基数维度列:选择基数(唯一值数量)较高的列作为排序键的前缀,可以提高数据筛选的效率。
操作步骤:在CREATE TABLE语句中通过ORDER KEY子句指定排序键。
order_key中所有列的总长度不能超过3072字节。
-- 创建一张带排序键的列存表
-- 经常按日期范围和地域进行查询,因此将它们设为排序键
CREATE TABLE sales_records_sorted (
sale_id INT PRIMARY KEY AUTO_INCREMENT,
product_id INT NOT NULL,
region VARCHAR(50),
sale_date DATE,
amount DECIMAL(10, 2),
-- 定义一个名为 idx_date_region 的排序键
ORDER KEY idx_date_region(sale_date, region)
) ENGINE=XEngine, TABLE_FORMAT=COLUMN;指定存储策略(storage_policy)实现冷热分层
storage_policy用于指定数据的存储介质,设计目标是实现冷热数据分离,将访问频率低的数据存放到成本更低的对象存储(OSS)中。
HOT:数据存储在高性能的PolarStore中(默认)。COLD:数据存储在低成本的OSS中。
storage_policy=cold语法虽已支持,但在当前版本中,所有数据(无论HOT或COLD)实际上都暂时存储在 PolarStore上。
-- 语法示例:创建一个存储策略为 COLD 的列存表
CREATE TABLE archive_logs (
log_id BIGINT PRIMARY KEY,
log_time TIMESTAMP,
message TEXT
) ENGINE=XEngine, TABLE_FORMAT=COLUMN, STORAGE_POLICY=COLD;创建分区表
创建一张列存分区表的语法与单表一致,均在CREATE TABLE语句中指定ENGINE=XEngine和TABLE_FORMAT=COLUMN即可。
由于创建列存分区表后,当前表就会转变为混合分区表。目前,混合分区表处于灰度阶段。如需使用该功能,请前往配额中心,根据配额ID:polardb_mysql_hybrid_partition找到相应的配额,并在对应的操作列点击申请以开通该功能。
-- 创建列存分区表
CREATE TABLE t1(a1 INT, a2 INT, a3 INT, a4 INT) ENGINE=XEngine TABLE_FORMAT=COLUMN
PARTITION BY RANGE(a1)
(
PARTITION p1 VALUES LESS THAN (20),
PARTITION p2 VALUES LESS THAN (40),
PARTITION p3 VALUES LESS THAN (60),
PARTITION p4 VALUES LESS THAN MAXVALUE
);
-- 创建列存分区表,将数据存储在OSS上
CREATE TABLE t2(a1 INT, a2 INT, a3 INT, a4 INT) ENGINE=XEngine TABLE_FORMAT=COLUMN STORAGE_POLICY=COLD
PARTITION BY RANGE(a1)
(
PARTITION p1 VALUES LESS THAN (20),
PARTITION p2 VALUES LESS THAN (40),
PARTITION p3 VALUES LESS THAN (60),
PARTITION p4 VALUES LESS THAN MAXVALUE
);管理列存表
本章节介绍如何对已有的表进行管理,包括在行存和列存之间转换、修改表结构以及变更分区格式。
转换表存储格式(行存与列存)
将现有的行存表转换为列存表以加速分析,或将列存表转换回行存表。
// 将行存表转换为xengine列存表
ALTER TABLE sales_records ENGINE=XEngine, TABLE_FORMAT=COLUMN;
// 将列存表转换为xengine行存表
ALTER TABLE sales_records ENGINE=XEngine TABLE_FORMAT=ROW;
//将列存表转换为innodb行存表
ALTER TABLE sales_records ENGINE=innodb;修改表结构(列与索引)
列存表支持Online DDL,可以在不阻塞DML的情况下修改添加或删除列或索引,语法与行存表一致。
-- 在线添加一个新列
ALTER TABLE sales_records ADD COLUMN salesperson_id INT;
-- 在线删除一个列
ALTER TABLE sales_records DROP COLUMN salesperson_id;
-- 在线添加一个普通索引
ALTER TABLE sales_records ADD INDEX idx_product(product_id);
-- 在线删除索引
ALTER TABLE sales_records DROP INDEX idx_product;当前版本中,ADD COLUMN操作不支持Instant DDL,但支持Online DDL,这意味着做加列操作要拷贝全表数据,但DDL执行过程中不阻塞DML。
修改排序键(order key)
根据查询模式的变化,调整或删除列存表的排序键。
-- 为已存在的列存表添加排序键
ALTER TABLE sales_records ADD ORDER KEY idx_region(region), ALGORITHM=INPLACE;
-- 修改排序键的定义(先删除后添加)
ALTER TABLE sales_records_sorted DROP ORDER KEY idx_date_region, ADD ORDER KEY idx_product(product_id), ALGORITHM=INPLACE;
-- 删除排序键
ALTER TABLE sales_records_sorted DROP ORDER KEY idx_date_region, ALGORITHM=INPLACE;变更分区存储格式
在分区表中,将部分历史分区转换为列存格式,实现行存、列存混合存储。保留近期活跃数据的分区为行存以保证OLTP性能,将历史归档数据的分区转换为列存以加速分析。
-- 步骤1:创建一个 InnoDB 分区表
CREATE TABLE historical_orders (
order_id INT NOT NULL,
order_date DATE NOT NULL,
amount DECIMAL(10, 2)
) ENGINE=InnoDB
PARTITION BY RANGE(TO_DAYS(order_date)) (
PARTITION p2023 VALUES LESS THAN (TO_DAYS('2024-01-01')),
PARTITION p2024 VALUES LESS THAN (TO_DAYS('2025-01-01'))
);
-- 步骤2:将 2023 年的历史数据分区转换为列存格式
ALTER TABLE historical_orders CHANGE PARTITION p2023 ENGINE=XEngine TABLE_FORMAT=COLUMN;分析与优化查询性能
通过EXPLAIN命令判断查询是否成功利用了列式引擎,并理解其执行过程。列式执行计划以横向树状结构展示,与传统的行存计划格式有显著区别。
示例
以下以TPC-H Q5基准测试查询为例,对比列存与行存的执行计划。
列式执行计划示例
+----+-------------------------------------------+----------+----------------------------------------------------------------------------------------------------+
| ID | Operator | Name | Extra Info |
+----+-------------------------------------------+----------+----------------------------------------------------------------------------------------------------+
| 1 | Select Statement | | IMCI Execution Plan (max_dop = 32, max_query_mem = unlimited) |
| 2 | └─Sort | | Sort Key: revenue DESC |
| 3 | └─Hash Groupby | | Group Key: nation.n_name |
| 4 | └─Hash Join | | Join Cond: (lineitem.l_suppkey, customer.c_nationkey) = (supplier.s_suppkey, supplier.s_nationkey) |
| 5 | ├─Hash Join | | Join Cond: orders.o_orderkey = lineitem.l_orderkey |
| 6 | │ ├─Hash Join | | Join Cond: (customer.c_nationkey, region.r_regionkey) = (nation.n_nationkey, nation.n_regionkey) |
| 7 | │ │ ├─Hash Join | | Join Cond: orders.o_custkey = customer.c_custkey |
| 8 | │ │ │ ├─Cartesian Product | | |
| 9 | │ │ │ │ ├─Table Scan | region | Cond: (r_name = "ASIA") |
| 10 | │ │ │ │ └─Table Scan | orders | Cond: ((o_orderdate >= 01/01/1994) AND (o_orderdate < 01/01/1995)) |
| 11 | │ │ │ └─Table Scan | customer | |
| 12 | │ │ └─Table Scan | nation | |
| 13 | │ └─Table Scan | lineitem | |
| 14 | └─Table Scan | supplier | |
+----+-------------------------------------------+----------+----------------------------------------------------------------------------------------------------+如何解读:
确认列式执行:
Extra Info列中出现IMCI Execution Plan字样,表明该查询已由列式引擎处理。查看并行度:
max_dop = 32表示该查询在列式引擎内部的最大并行线程数为32。理解核心算子:
Table Scan:表示对列存表的扫描。Cond部分显示已将WHERE条件下推到存储层执行,这是性能优化的关键。Hash Join/Hash Groupby:列式引擎中使用的高效关联和聚合算法,适合处理大规模数据。Sort:排序操作。如果排序键与ORDER BY的列匹配,此处性能会很高。
行式执行计划对比
+----+-------------+----------+------------+--------+---------------+---------+---------+---------------------------+---------+----------+----------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+----------+------------+--------+---------------+---------+---------+---------------------------+---------+----------+----------------------------------------------+
| 1 | SIMPLE | region | NULL | ALL | PRIMARY | NULL | NULL | NULL | 5 | 20.00 | Using where; Using temporary; Using filesort |
| 1 | SIMPLE | orders | NULL | ALL | PRIMARY | NULL | NULL | NULL | 1500000 | 11.11 | Using where; Using join buffer (hash join) |
| 1 | SIMPLE | customer | NULL | eq_ref | PRIMARY | PRIMARY | 8 | tpch.orders.o_custkey | 1 | 100.00 | NULL |
| 1 | SIMPLE | nation | NULL | eq_ref | PRIMARY | PRIMARY | 8 | tpch.customer.c_nationkey | 1 | 10.00 | Using where |
| 1 | SIMPLE | lineitem | NULL | ref | PRIMARY | PRIMARY | 8 | tpch.orders.o_orderkey | 2 | 100.00 | NULL |
| 1 | SIMPLE | supplier | NULL | eq_ref | PRIMARY | PRIMARY | 8 | tpch.lineitem.l_suppkey | 1 | 10.00 | Using where |
+----+-------------+----------+------------+--------+---------------+---------+---------+---------------------------+---------+----------+----------------------------------------------+行存计划通常包含Using temporary; Using filesort等标志,表示需要创建临时表并在磁盘上进行排序,这是分析查询中的主要性能瓶颈。列式计划通过内存中的并行计算避免了这些低效操作。