创建列存表

更新时间:
复制为 MD 格式

本文将指导您如何创建列存表,并详细介绍建表时常见的约束、属性配置方法及实用示例。

适用场景

本文适用于需要在列存引擎中创建新表的用户,尤其适合以下场景:

  • 新建用于在线分析的列存表。

  • 新建带主键以支持精确查询的列存表。

  • 为离线分析场景创建仅依赖 Base 层的列存表,以优化写入性能。

前置准备

在开始创建列存表前,请确保满足以下条件:

  • 使用列存引擎的 SQL 连接地址。如何查看列存引擎的连接地址,请参考 使用MySQL客户端访问列存引擎

  • 已具备在目标库中创建表的权限。

  • 已规划好表的字段类型和分区策略。


快速开始

下面是一个最基础的列存表示例,它创建了一个非主键表,并按 device_id 进行了哈希分区。

CREATE TABLE IF NOT EXISTS sensor_data (
  device_id VARCHAR NOT NULL,
  region VARCHAR NOT NULL,
  ts TIMESTAMP NOT NULL,
  temperature DECIMAL(15, 2),
  humidity BIGINT
)
PARTITION BY LIST(`bucket`(100, device_id));

语法说明

建表语法

create_table_statement :: =  CREATE TABLE [IF NOT EXISTS] table_name 
                            '('
                                  column_definition,
                                  (',' column_definition )* 
                                  [',' PRIMARY KEY '(' pk_list ')']  
                             ')'
                             PARTITION BY LIST '(' part_list ')'
                             [ WITH '(' table_options ')' ]

column_definition ::= column_name data_type [NOT NULL]
pk_list           ::= column_name [, column_name ...]
part_expr         ::= `bucket`(bucket_num, column_name)
                    | `identity`(column_name)
                    | `year`(column_name)
                    | `month`(column_name)
                    | `day`(column_name)
                    | `hour`(column_name)
table_options     ::= attr_def [, attr_def ...]
attr_def          ::= `attr_name` = 'attr_value'

语法要点

  • PRIMARY KEY (...):可选。不指定时,表示创建非主键表。

  • PARTITION BY LIST (...):必选。用于指定 Base 层的分区规则。

  • WITH (...):可选。用于设置表属性,例如关闭 Delta 层或调整文件配置。

  • 格式要求:表属性名使用反引号 ` ` 包裹,属性值使用单引号 ' ' 包裹。

字段类型

支持的数据类型,请参考数据类型

分区函数

支持以下分区函数:

分区函数

说明

适用列类型

bucket(num, col)

对指定列做哈希分桶,num 为分桶数。

任何类型,建议高基数列。

identity(col)

按字段原值分区。

SMALLINTINTBIGINTVARCHAR 等。

year(col)

按年分区。

TIMESTAMP 或 DATE

month(col)

按月分区。

TIMESTAMP 或 DATE

day(col)

按天分区。

TIMESTAMP 或 DATE

hour(col)

按小时分区。

TIMESTAMP 或 DATE

分区字段选择建议

  • 优先选择高基数列:例如用户ID、设备ID等,作为分区字段,这有利于数据打散和负载均衡。

  • 主键表约束:如果创建的是主键表,分区键必须来自主键列。

  • 时间维度查询:如果查询常按时间范围过滤,可优先考虑使用 yearmonthdayhour 等时间分区函数。

  • 分区函数需要用反引号 ` ` 包裹。


表属性

您可以通过 WITH (...) 子句为列存表设置特定的属性。

写法示例:

WITH (
  `column.engine.delta_store.memstore.skip_write.enabled` = 'true',
  `column.engine.delta_store.query.enabled` = 'false'
)

Delta 层相关属性

Delta 层用于承接实时写入与查询。如果业务对数据新鲜度要求高,请保留 Delta 层;如果主要是离线分析,可关闭 Delta 层以提高写入效率。

属性名

是否可选

默认值

说明

EngineType

可选

基于引擎类型自动判断

引擎类型。通常使用列存连接地址时,默认值为 COLUMN。

column.engine.delta_store.memstore.skip_write.enabled

可选

false

是否跳过 Delta 层。false 表示写入 Delta 层,实时性更高;true 表示不写 Delta 层。

column.engine.delta_store.query.enabled

可选

true

是否查询 Delta 层。只有在数据写入 Delta 层时,查询 Delta 层才有意义。

column.engine.delta_store.dump.interval

可选

600000

Delta 层数据 Dump 到 Base 层的时间周期,单位为毫秒,默认 10 分钟。

column.engine.num_regions

可选

5 * 节点数量

Delta 层数据的分区数。数据在经过 PARTITION BY 分区后,还会再做一次哈希拆分,便于 Delta 层管理。

配置建议:

  • 实时场景:要求“写入后尽快可查”,建议保留默认配置。

  • 离线场景:对数据新鲜度无要求,建议建表时显式关闭 Delta 层,以减少开销、提升写入效率:

    WITH (
      `column.engine.delta_store.memstore.skip_write.enabled` = 'true',
      `column.engine.delta_store.query.enabled` = 'false'
    )

数据文件与快照相关属性

用于控制底层数据文件的格式、压缩方式和快照保留策略。

属性名

是否可选

默认值

说明

column.engine.lce.write.format.default

可选

parquet

数据文件格式,支持 parquet 和 avro

column.engine.lce.write.parquet.compression-codec

可选

zstd

压缩算法,支持 zstdsnappygzip 等。

column.engine.lce.write.parquet.row-group-size-bytes

可选

134217728

Parquet Row Group 大小(字节),默认 128 MB。

column.engine.lce.write.target-file-size-bytes

可选

536870912

目标数据文件大小(字节),默认 512 MB。

column.engine.lce.history.expire.min-snapshots-to-keep

可选

1

最少保留的快照数量。

column.engine.lce.history.expire.max-snapshot-age-ms

可选

3600000

快照最大保留时间(毫秒),默认 1 小时。

配置建议:

对于复杂的 ETL 任务,或对底层 Snapshot 文件有较强依赖的场景(例如数据管道中某个步骤依赖上一个快照),建议将 column.engine.lce.history.expire.max-snapshot-age-ms 配置得更长一些,例如设置为 6 小时:

WITH (
  `column.engine.lce.history.expire.max-snapshot-age-ms` = '21600000'
)

查看表结构

使用 DESCRIBE 或 DESC 命令查看表的字段定义:

DESCRIBE TABLE tableName;
-- 或简写为
DESC tableName;

查看表属性

使用 SHOW TABLE VARIABLES 命令查看表的属性:

-- 查看所有属性
SHOW TABLE VARIABLES FROM tableName;

-- 按条件过滤,例如查看分区规则
SHOW TABLE VARIABLES FROM tableName LIKE 'PARTITION_KEY';

-- 查看以 'column.engine' 开头的所有已设置属性
SHOW TABLE VARIABLES FROM tableName LIKE 'column.engine%';

示例

示例一:创建销售订单主键表

电商或零售场景中,销售订单表通常需要按订单 ID 精确查询,适合建立主键表。

CREATE TABLE IF NOT EXISTS order_detail (
  order_id    VARCHAR NOT NULL,
  user_id     VARCHAR NOT NULL,
  created_at  TIMESTAMP NOT NULL,
  product_id  VARCHAR,
  category    VARCHAR,
  quantity    BIGINT,
  amount      DECIMAL(15, 2),
  status      VARCHAR,
  PRIMARY KEY(order_id, user_id, created_at)
)
PARTITION BY LIST(`bucket`(100, order_id));
说明

该表以 order_iduser_id 和 created_at 作为联合主键。分区键 order_id 属于主键列,满足约束。

示例二:创建设备测量数据非主键表

物联网场景中,设备持续上报传感器测量数据,写入量大、主要用于聚合分析,适合建立非主键表。

CREATE TABLE IF NOT EXISTS measurement (
  device_id   VARCHAR NOT NULL,
  region      VARCHAR NOT NULL,
  ts          TIMESTAMP NOT NULL,
  metric      VARCHAR NOT NULL,
  value       DOUBLE,
  unit        VARCHAR
)
PARTITION BY LIST(`bucket`(100, device_id));
说明

该表未定义主键,适合以聚合分析为主的场景。按 device_id 分区有助于数据打散和均衡负载。

示例三:创建离线分析场景的销售汇总表

数仓场景中,销售汇总数据通常通过批量 ETL 写入,对实时性没有要求,可以关闭 Delta 层以提升写入效率。

CREATE TABLE IF NOT EXISTS sale_summary (
  shop_id      VARCHAR NOT NULL,
  region       VARCHAR NOT NULL,
  sale_date    DATE NOT NULL,
  category     VARCHAR NOT NULL,
  total_sales  DECIMAL(15, 2),
  order_count  BIGINT,
  return_rate  DOUBLE
)
PARTITION BY LIST(`bucket`(50, shop_id), `day`(sale_date))
WITH (
  `column.engine.delta_store.memstore.skip_write.enabled` = 'true',
  `column.engine.delta_store.query.enabled` = 'false'
);
说明

该表使用组合分区(店铺ID哈希 + 日期),并通过 WITH 子句关闭了 Delta 层的读写,优化了批量写入性能。