表设计

更新时间:
复制为 MD 格式

本文介绍如何创建和管理列存表,包括设置排序键、转换表格式等核心操作,以及通过分析执行计划来优化查询性能的方法。

创建列存表

本章节介绍如何创建一张新的列存表,包括基础创建、设置排序键以优化性能,以及了解存储策略。

创建单表

创建一张最基础的列存表。您只需在CREATE TABLE语句中指定ENGINE=XEngineTABLE_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语法虽已支持,但在当前版本中,所有数据(无论HOTCOLD)实际上都暂时存储在 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=XEngineTABLE_FORMAT=COLUMN即可。

说明

由于创建列存分区表后,当前表就会转变为混合分区表。目前,混合分区表处于灰度阶段。如需使用该功能,请前往配额中心,根据配额IDpolardb_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 |                                                                                                    |
+----+-------------------------------------------+----------+----------------------------------------------------------------------------------------------------+

如何解读:

  1. 确认列式执行:Extra Info列中出现IMCI Execution Plan字样,表明该查询已由列式引擎处理。

  2. 查看并行度:max_dop = 32表示该查询在列式引擎内部的最大并行线程数为32。

  3. 理解核心算子:

    • 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等标志,表示需要创建临时表并在磁盘上进行排序,这是分析查询中的主要性能瓶颈。列式计划通过内存中的并行计算避免了这些低效操作。