CREATE TABLE建表语法

更新时间:
复制 MD 格式

本文介绍CREATE TABLE建表语法,包含5种常见场景的建表模板、完整参数说明和常见问题。

表的数据分布策略

建表前,您可以通过下图中的示例,了解关于表的几个重要概念,包括分片、分区、聚集索引。

image

快速建表指南

根据您的业务场景,选择以下建表模板快速开始。每个场景提供最小可用的建表语句和关键注意事项。详细参数说明请参见下方参数章节。

场景1:按日期分区的事实表(最常见)

适用于业务数据按日期持续写入,需要按日期查询和管理数据生命周期的场景,如订单表、日志表、行为数据表。

CREATE TABLE sales (
  sale_id BIGINT NOT NULL COMMENT '订单ID',
  customer_id VARCHAR NOT NULL COMMENT '顾客ID',
  revenue DECIMAL(15, 2) COMMENT '订单金额',
  sale_time TIMESTAMP NOT NULL COMMENT '订单时间',
  PRIMARY KEY (sale_time, sale_id)
)
DISTRIBUTED BY HASH(sale_id)
PARTITION BY VALUE(DATE_FORMAT(sale_time, '%Y%m%d'))
LIFECYCLE 365;
重要
  • 主键必须包含分布键和分区键。上例中sale_id(分布键)和sale_time(分区键)都在主键中。详见PRIMARY KEY

  • 分区键建议使用TIMESTAMPDATE类型。详见PARTITION BY

  • INSERT遇到主键重复时,系统会静默忽略重复记录,不会报错。请确保主键能唯一标识每条记录。

场景2:冷热分层分区表(降低存储成本)

适用于历史数据查询频率低,希望将冷数据存储在OSS以降低成本,同时保证近期数据查询性能的场景。

CREATE TABLE order_history (
  order_id BIGINT NOT NULL,
  customer_id INT NOT NULL,
  order_date DATE NOT NULL,
  amount DECIMAL(15, 2),
  PRIMARY KEY (order_date, order_id)
)
DISTRIBUTED BY HASH(order_id)
PARTITION BY VALUE(DATE_FORMAT(order_date, '%Y%m')) LIFECYCLE 120
STORAGE_POLICY='MIXED' HOT_PARTITION_COUNT=3;
重要
  • COLDMIXED策略仅对分区表生效。非分区表即使设置了COLDMIXED,数据仍存储在SSD(等同于HOT)。详见storage_policy

  • 冷热分离只能在表级别设置,不支持库级别操作。

  • HOT_PARTITION_COUNT=3表示最近3个分区的数据存热存储(SSD),其余存冷存储(OSS)。上例按月分区,因此最近3个月的数据为热存储,3个月之前的数据为冷存储。

场景3:带聚集索引的高性能查询表

适用于表数据量大,高频进行范围查询,需要通过聚集索引优化读取性能的场景,如SaaS多租户表按tenant_id查询。

CREATE TABLE user_events (
  tenant_id VARCHAR NOT NULL COMMENT '租户ID',
  event_id BIGINT NOT NULL,
  event_time TIMESTAMP NOT NULL,
  event_type VARCHAR,
  CLUSTERED KEY idx_tenant(tenant_id, event_time DESC),
  PRIMARY KEY (event_time, tenant_id, event_id)
)
DISTRIBUTED BY HASH(tenant_id)
PARTITION BY VALUE(DATE_FORMAT(event_time, '%Y%m%d'))
LIFECYCLE 90;
重要
  • 聚集索引需BUILD后才能生效。创建表或通过ALTER TABLE添加聚集索引后,需等待BUILD任务完成(或手动执行BUILD TABLE table_name)。详见CLUSTERED KEY

  • 当查询条件不包含分布键时,查询需扫描所有分片。如果业务查询无法覆盖分布键,建议为高频查询列创建聚集索引(CLUSTERED KEY)

  • 每个表只能有一个聚集索引。聚集索引默认升序,降序查询请将聚集索引设为DESC。

场景4:非分区表(小表/维度表)

适用于数据量较小(千万级以下)、无需按时间管理生命周期的表。

CREATE TABLE product (
  product_id BIGINT NOT NULL PRIMARY KEY,
  product_name VARCHAR,
  category VARCHAR,
  price DECIMAL(10, 2)
)
DISTRIBUTED BY HASH(product_id);
重要
  • 未定义主键和分布键时,系统自动添加__adb_auto_id__列作为主键和分布键。

  • 非分区表的所有数据在同一分区,数据量超过千万行时索引扫描效率会下降。大表建议使用分区表。

场景5:复制表(小型查找表)

适用于数据量小(建议不超过2万行)的维度表,需要与大表频繁JOIN。

CREATE TABLE dim_city (
  city_id INT NOT NULL PRIMARY KEY,
  city_name VARCHAR,
  province VARCHAR
)
DISTRIBUTED BY BROADCAST;
重要
  • 复制表在每个节点存一份全量数据,JOIN时无需跨节点传输,但写入会广播到所有节点。

  • 数据量不宜超过2万行。不建议频繁增删改。

注意事项

以下属性建表后不可修改,请在建表前仔细规划:

  • 主键:不能增加、减少或变更主键列。

  • 分布键:不能增加、减少或变更分布键列,需重建表并迁移数据。

  • 分区键:不能增加分区键(即不能将非分区表变更为分区表),也不支持增加、减少或修改分区键中的列,需重建表。

  • 存储引擎:不能切换存储引擎(XUANWUXUANWU_V2之间不能互相切换),需重建表。

语法

CREATE TABLE [IF NOT EXISTS] table_name
  ({column_name column_type [column_attributes] [ column_constraints ] [COMMENT 'column_comment']
  | table_constraints}
  [, ... ])
  [table_attribute]
  [partition_options]
  [index_all]
  [storage_policy]
  [block_size]
  [engine]
  [table_properties]
  [AS query_expr]
  [COMMENT 'table_comment']

column_attributes:
  [DEFAULT {constant | CURRENT_TIMESTAMP}]
  [AUTO_INCREMENT]

column_constraints:
  [{NOT NULL|NULL} ]
  [PRIMARY KEY]

table_constraints:
  [{INDEX|KEY} [index_name] (column_name|column_name->'$.json_path'|column_name->'$[*]')][,...]
  [FULLTEXT [INDEX|KEY] [index_name] (column_name) [index_option]] [,...]
  [PRIMARY KEY [index_name] (column_name,...)]
  [CLUSTERED KEY [index_name] (column_name[ASC|DESC],...) ]
  [[CONSTRAINT [symbol]] FOREIGN KEY (fk_column_name) REFERENCES pk_table_name (pk_column_name)][,...]
  [ANN INDEX [index_name] (column_name,...) [index_option]] [,...]

table_attribute:
  DISTRIBUTED BY HASH(column_name,...) | DISTRIBUTED BY BROADCAST

partition_options:
  PARTITION BY 
        {VALUE(column_name) | VALUE(DATE_FORMAT(column_name, 'format')) | VALUE(FROM_UNIXTIME(column_name, 'format'))}
  LIFECYCLE N
  
 index_all:
 INDEX_ALL= 'Y|N'

storage_policy:
  STORAGE_POLICY= {'HOT'|'COLD'|'MIXED' {hot_partition_count=N}}

block_size:
  BLOCK_SIZE= VALUE

engine:
  ENGINE= 'XUANWU|XUANWU_V2'

参数

table_name、column_name、column_type、COMMENT

参数

说明

table_name

表名。表名以字母或下划线(_)开头,可包含字母、数字以及下划线(_),长度为1127个字符。

您可以使用db_name.table_name,指定在某个数据库下创建表。

column_name

列名。列名以字母或下划线(_)开头,可包含字母、数字以及下划线(_),长度为1127个字符。

column_type

列的数据类型。AnalyticDB for MySQL支持的数据类型,请参见基础数据类型复杂数据类型

COMMENT

为列或表添加备注信息。

column_attributes(默认值与自增列)

DEFAULT {constant | CURRENT_TIMESTAMP}

定义列的默认值。仅支持常量CURRENT_TIMESTAMP函数。不支持其他函数和变量表达式。

如果未指定默认值,则列的默认值为NULL

AUTO_INCREMENT

定义自增列。 自增列的数据类型必须是BIGINT类型。

AnalyticDB for MySQL为自增列提供唯一值,但自增列的值不是顺序递增,且不支持从1开始递增

重要
  • 在向包含自增列的表中插入数据时,建议显式指定列名,例如INSERT INTO table (column1,column2) VALUES (value1,value2)。这种做法能够有效避免因列数或列顺序不匹配而导致的报错,例如Insert query has mismatched column sizes

  • 由于分布式系统实现的原因,当通过ETL过程使用INSERT INTO SELECT语句写入数据时,自增列仅能保证单个ETL内部的唯一性,无法保证多个ETL之间的唯一性。

column_constraints(非空与主键)

NOT NULL

定义列不允许NULL值。未定义时,列默认允许NULL

PRIMARY KEY

定义单列主键,例如id BIGINT NOT NULL PRIMARY KEY。复合主键请在表约束(table_constraints)中定义。

table_constraints(索引)

定义表级索引和约束。支持普通索引、主键、聚集索引、全文索引、向量索引、外键等,可组合使用。

INDEX | KEY

定义普通索引。INDEXKEY作用相同。

  • XUANWU_V2表,默认不创建全列索引。如果表定义了主键,默认只会为主键创建普通索引。

  • XUANWU表,默认创建全列索引。但是,如果您在创建XUANWU表时手动为部分列创建索引(例如INDEX (id)),则AnalyticDB for MySQL不会再为表中其他列自动创建索引。

注意事项:不支持多列复合索引,即不支持INDEX (column1,column2)

PRIMARY KEY

定义单列或复合主键。主键用于数据去重和唯一标识每行记录。

基本使用:

  • 每个表只能有一个主键。

  • 主键可以是单个列或多个列的组合,例如PRIMARY KEY (id)PRIMARY KEY (id,name)

  • 主键中必须包含分布键分区键,并且建议将分布键分区键放在主键的前部

注意事项

  • 无主键的表,不能执行DELETEUPDATE操作。

  • 未定义主键,会有以下行为:

    • 如果未定义主键和分布键,AnalyticDB for MySQL自动添加一个列__adb_auto_id__作为表的主键和分布键

    • 如果未定义主键,但定义了分布键,AnalyticDB for MySQL不会自动添加主键

  • 建表后,不能增加、减少或变更主键列。更多建表后不可修改的属性,请参见快速建表指南

  • INSERT INTO遇到主键重复时,系统会静默忽略重复记录(等同于INSERT IGNORE INTO),不会报错也不会写入。如需保留所有数据,请确保主键组合能唯一标识每条记录。如需在主键冲突时更新已有数据,请使用INSERT ON DUPLICATE KEY UPDATE语法。

调优建议:推荐使用数值类型的列作为主键,并尽量减少主键包含的列的个数,以获得较好的性能。

说明

主键包含的列过多,可能导致:

  • 数据写入时,AnalyticDB for MySQL检查主键是否重复,将消耗更多的CPUIO资源。

  • 主键索引将占用更多的磁盘空间。您可以使用空间分析功能,查看主键索引占用的磁盘空间。

  • 主键包含的列越多,BUILD任务会越慢。

CLUSTERED KEY

定义聚集索引。聚集索引决定分区内数据的物理存储顺序(默认升序),使键值相近的数据连续存储,从而加速范围查询和等值查询。

聚集索引示意图

image

适用场景:

高频出现在查询条件中的列适合作为聚集索引。例如SaaS场景中,tenant_id作为聚集索引可使同一租户数据连续存储,加速查询。

基本使用:

  • 聚集索引创建后需等待BUILD任务完成才能生效。可手动执行BUILD TABLE table_name加速生效。通过ALTER TABLE添加聚集索引后同样需要BUILD。

  • 每个表中只能有一个聚集索引。

  • 聚集索引可以基于单个列创建(例如CLUSTERED KEY index(id)),也可以基于多个列创建(例如CLUSTERED KEY index(id,name))。当聚集索引键涉及多个列时,数据会先根据第一个列的值排序,在第一个列的值相同时,按第二个列的值进行次级排序。所以CLUSTERED KEY index(id,name)CLUSTERED KEY index(name,id)是不同的聚集索引。

  • 聚集索引默认为升序排列,适用于升序查询。如果您的查询是降序的,请在创建表时将聚集索引设置为降序,例如CLUSTERED KEY index(id) DESC。如果您已创建表,可以删除聚集索引,并重新创建一个降序的聚集索引。

  • 如果字段值较长,例如长达十几KB或几十KB的字符串,则不建议聚集索引采用该字段,避免影响排序性能。

FULLTEXT INDEX | FULLTEXT KEY

定义全文索引

语法与参数说明

语法[FULLTEXT [INDEX|KEY] [index_name] (column_name) [index_option]] [,...]

参数说明:

  • index_name:全文索引名称。

  • column_name:全文索引的列。列的类型必须是VARCHAR类型,或JSON类型的路径表达式(如column_name->'$.key')。

  • index_option:指定全文索引的分词器和自定义词典。非必填。

FOREIGN KEY

定义外键索引。外键索引用于消除不必要的JOIN

语法与参数说明

版本说明:

AnalyticDB for MySQL集群内核版本需为3.1.10或以上

说明

云原生数据仓库AnalyticDB MySQL控制台集群信息页面,配置信息区域,查看和升级内核版本

语法[[CONSTRAINT [symbol]] FOREIGN KEY (fk_column_name) REFERENCES pk_table_name (pk_column_name)][,...]

参数说明:

  • symbol:可选项,外键约束名,在表内唯一。不指定时,解析器将会在外键列名后面自动补充后缀_fk用作外键约束名。

  • fk_column_name:指定外键列。外键列需要在建表语句中定义。

  • pk_table_name:指定主表名。主表必须已存在。

  • pk_column_name:指定外键约束列,该列必须存在且为主表的主键列。

基本使用:

  • 每个表可以有多个外键索引。

  • 不支持复合的外键索引,即不支持多个列组成的外键索引,例如:FOREIGN KEY (sr_item_sk, sr_ticket_number) REFERENCES store_sales(ss_item_sk,d_date_sk)

  • AnalyticDB for MySQL不会进行数据的约束检查。您需要自行确保主表的主键和从表的外键之间的数据约束关系。

  • 外表不支持创建外键约束。

ANN INDEX

定义向量索引

注意事项:XUANWUXUANWU_V2引擎均支持创建向量索引。

语法与参数说明

语法[ANN INDEX [index_name] (column_name,...) [index_option]] [,...]

参数说明:

  • index_name:向量索引的名称。

  • column_name:向量列的列名。向量列的类型需为array<float>、array<smallint>、array<byte>,且需指定向量列的维数,向量列的定义示例:feature array<float>(4)

  • index_option:向量索引的属性。

    • algorithm:向量距离计算公式使用的算法。取值仅支持HNSW_PQ,适用于单表数据量在百万级别到千万级别之间,对向量维度敏感的中等规模数据量场景。

    • dis_function:向量距离计算公式。取值仅支持SquaredL2。计算公式:(x1-y1)^2+(x2-y2)^2+…

JSON INDEX

定义JSON索引JSON Array索引。

语法与参数说明

JSON索引

版本说明:

  • 3.1.5.10及以上内核版本的集群,创建表后不会自动创建JSON索引,您需手动创建JSON索引。

  • 3.1.5.10以下内核版本的集群,创建表后会自动为JSON列创建JSON索引。

说明

云原生数据仓库AnalyticDB MySQL控制台集群信息页面,配置信息区域,查看和升级内核版本

语法[INDEX [index_name] (column_name|column_name->'$.json_path')]

参数说明:

  • index_name:索引的名称。

  • column_name|column_name->'$.json_path':

    • column_name:JSON索引的列。

    • column_name->'$.json_path':JSON索引的列及其指定的属性键。每一个JSON索引只能有一个JSON列的一个属性键。

      重要

JSON Array索引

版本说明:

3.1.10.6及以上内核版本的集群支持创建JSON Array索引。

说明

云原生数据仓库AnalyticDB MySQL控制台集群信息页面,配置信息区域,查看和升级内核版本

语法[INDEX [index_name] (column_name->'$[*]')]

参数说明:

  • index_name:索引的名称。

  • column_name->'$[*]':column_nameJSON Array索引的列。例如:vj->'$[*]'表示为vj列创建JSON Array索引。

table_attribute(分布键)

table_attribute决定了表的类型是普通表还是复制表。

  • DISTRIBUTED BY HASH,定义表为普通表。普通表能够充分利用分布式系统的查询优势,提高查询效率。普通表可存储的数据量较大,通常可以存储千万条甚至千亿条数据。

  • DISTRIBUTED BY BROADCAST,定义表为复制表。详见下方DISTRIBUTED BY BROADCAST说明。

DISTRIBUTED BY HASH (column_name,...)

定义分布键。系统对分布键值进行HASH计算,将数据分散到不同分片(Shard),提高查询性能和可扩展性。

数据分片示意图

image

基本用法:

  • 每个表只能有一个分布键

  • 一个分布键可以包含一个列或者多个列。

  • 分布键中的列,必须包含在主键中。详见PRIMARY KEY

注意事项

  • 创建表时未定义分布键,系统会自动处理。详见PRIMARY KEY注意事项

  • 建表后,不能增加、减少或变更分布键列。如果需要修改分布键,需重新建表并迁移数据。更多建表后不可修改的属性,请参见快速建表指南

调优建议:

  • 建议分布键包含尽可能少的列,使得分布键在各种复杂查询中更加通用。

  • 尽可能选择高频率出现在查询条件中,且值分布均匀的列作为分布键,例如交易ID、设备ID、用户ID或者自增列作为分布键。但如果查询条件非常局限,例如列a虽然值分布均匀且高频出现在查询条件中,但总是以a=3的形式出现在查询条件中,那么列a作为分布键会造成数据热点,列a就不适合作为分布键。

  • 当查询条件不包含分布键时,查询需扫描所有分片(Shard),性能会显著下降。如果业务查询条件无法覆盖分布键,建议为高频查询列创建聚集索引(CLUSTERED KEY)来优化查询性能。

  • 尽可能将需要Join的列作为分布键。参与Join的两个表,按相同的分布键(Join列)进行数据分布,使得两个表相同键值的数据被分布到同一分片,可直接在同一分片进行Join操作,无需在分片之间进行数据传输,能够有效减少查询过程中的数据重分布,提升查询性能。例如,需要按照顾客维度查看历史订单信息,可以选择customer_id作为分布键。

  • 尽量不要选择日期、时间和时间戳类型的列作为分布键,写入时容易发生倾斜,影响写入性能,且多数查询通常是限定了日期或时间段,如:查询最近一天或一个月的数据,可能会导致要查询的数据只存在于一个节点上,无法充分利用分布式数据库中所有节点的处理能力。建议将日期、时间类型的列作为分区键

  • 您可以利用存储空间诊断功能查看表的分布键是否合理、数据是否倾斜。

DISTRIBUTED BY BROADCAST

定义复制表。每个节点存储全量数据,JOIN时无需跨节点传输。写入时变更会广播到所有节点,因此复制表适合数据量小、写入不频繁的维度表。

partition_options(分区键与生命周期)

如果设置了分布键后,单个分片的数据量较大,您可以定义分区键将分片上的数据划分为不同的分区,加快数据过滤速度,提高查询性能。

为什么要定义分区

  • 分区可以加快数据过滤速度,提高查询性能。

    • 分区裁剪。只查询相关数据的分区,跳过无关分区,减少数据扫描,提高查询速度。

    • 索引的扫描性能较好。当索引的行数过大,例如超过5000万行,索引的扫描效率就会下降。索引是分区级别的,即每个分区有一个独立的索引。如果表没有定义数据分区,那么表的所有数据都在一个分区中,数据量超过千万时,索引扫描效率下降。当表定义了数据分区,数据分散在不同分片的不同分区时,每个分区索引的行数可以控制在千万行内,可以保证扫描的性能。

    • 提高Build的效率。Build用于将实时数据转换成历史数据,过程中会构建分区、构建索引、清理冗余数据等。Build任务完成,新的索引才能生效。当表没有定义分区时,每次Build任务都是全表Build,数据越多,Build越慢,新的索引就会越晚生效,从而影响查询性能。当表定义了分区,每次只Build有数据变更的分区,Build的时间就会缩短。

  • 分区结合生命周期(LIFECYCLE),可以实现数据的生命周期管理。

  • 分区结合存储策略(storage_policy),可以实现冷热数据分层。

数据分区与生命周期的示意图

image

PARTITION BY

指定分区键。

语法PARTITION BY VALUE {(column_name)|(DATE_FORMAT(column_name, 'format'))|(FROM_UNIXTIME(column_name, 'format'))} LIFECYCLE n

参数说明

  • column_name:分区键。PARTITION BY VALUE(column_name)表示使用column_name的值来分区。分区键的数据类型可以是数值类型、日期时间类型或表示数字的字符串类型。

  • DATE_FORMAT(column_name, 'format'))|FROM_UNIXTIME(column_name, 'format'):使用DATE_FORMATFROM_UNIXTIME函数将日期时间类型的列转换成指定的日期格式,再对数据分区。format仅支持年、月、日,即%Y、%y、%Y%m、%y%m、%Y%m%d、%y%m%d。建表后,format可以通过ALTER TABLE语句修改。

    • columnBIGINT、TIMESTAMP、DATETIMEVARCHAR数据类型时,须使用DATE_FORMAT函数。其中,BIGINT类型的列,值为毫秒级UNIX时间戳,类似1734278400000。TIMESTAMP、DATETIMEVARCHAR类型的列,值类似"2024-11-26 00:01:02"。

    • columnINT数据类型时,须使用FROM_UNIXTIME函数,值为秒级UNIX时间戳,类似1696266000。

注意事项

  • 3.2.1.0以下内核版本的集群,使用PARTITION BY定义分区时,必须同时定义生命周期LIFECYCLE n),否则会报错。

  • 3.2.1.0 及以上内核版本的集群,使用PARTITION BY定义分区时,生命周期LIFECYCLE n为可选参数。若未配置,则分区数据不会被清理。

  • 建表后,不能增加分区键,也不支持增加、减少或修改分区键中的列。如果需要增加或修改分区键,请重新建表并迁移数据,具体操作请参见ALTER TABLE。更多建表后不可修改的属性,请参见快速建表指南

  • DATETIME类型列使用DATE_FORMAT作为分区函数时,在部分场景下可能导致分区不生效(数据全部归入默认分区)。建议分区键使用DATETIMESTAMP类型列。如已使用DATETIME列,建表后请写入测试数据并执行BUILD TABLE,验证分区是否正确创建。

调优建议:

  • 建议使用日期时间类型的字段作为分区键。

  • 分区太大或太小都会影响查询性能和写入性能,甚至影响集群的稳定性。建议的分区内数据行数以及查看分区是否合理,请参见分区表诊断

  • 不建议频繁更新历史分区的数据,例如,如果每天频繁更新多个历史分区,应考虑使用的分区键是否合理。

LIFECYCLE n

配合PARTITION BY使用,管理分区生命周期。系统按分区键值从大到小排序,保留最新的n个分区,超出的分区将被自动删除。

  • 3.2.1.1以下内核版本,LIFECYCLE n定义每个分片最多保留n个分区。以分片级管理分区的生命周期时,在数据分布不均或数据量极少的情况下,可能会出现保留的总分区数多于n的情况。

  • 3.2.1.1及以上内核版本,对于XUANWU引擎表:升级后新建的表按照表级管理分区的生命周期,LIFECYCLE n定义每个表最多保留n个分区。升级前已创建的表仍按照分片级管理分区的生命周期,LIFECYCLE n定义每个分片最多保留n个分区。

  • XUANWU_V2引擎表仍然按分片级管理分区的生命周期,LIFECYCLE n定义每个分片最多保留n个分区,暂不支持按表级管理。

示例:

例如,PARTITION BY VALUE (DATE_FORMAT(date, '%Y%m%d')) LIFECYCLE 30表示,将date列转换为yyyyMMdd的格式并对数据分区,最多保留30个分区。假设第1天的数据写入分区20231201,第2天的数据写入分区20231202,依次类推,第30天的数据写入分区20231230。当第31天的数据写入分区20231231时,因为最多只能保留30个分区,所以最小的分区(即分区20231201)将会被自动删除。

index_all(全列索引)

是否为所有列创建INDEX索引。

取值说明

  • Y:为所有列创建INDEX索引。XUANWU表默认值为Y

  • N:只为主键创建INDEX索引,其他列不创建INDEX索引。XUANWU_V2表默认值为N

storage_policy(存储策略)

企业版、基础版及湖仓版数仓版弹性模式集群版(新版)支持指定数据的存储策略。不同的存储策略,数据的读写性能不同,数据的存储成本也不同。

重要

STORAGE_POLICY='COLD'STORAGE_POLICY='MIXED'仅对分区表生效。非分区表即使设置了COLDMIXED策略,数据仍存储在SSD,等同于HOT。如需使用冷存储,请确保表定义了PARTITION BY

取值说明

  • hot(默认值):热存储。即全表所有分区的数据都存储在SSD。性能最好,但存储成本最高。

  • cold:冷存储。即全表所有分区的数据存储在OSS。相比热存储性能较低,但存储成本最低。

  • mixed:冷热混合存储,又叫冷热分层存储,即查询频率高的分区数据(又称热数据)存储在SSD,查询频率低的分区数据(又称冷数据)存储在OSS,既降低存储成本,又保证查询性能。选择mixed时,必须同时使用PARTITION BY定义分区并使用hot_partition_count指定热分区的数量。如未定义分区,mixed不生效,数据实际会存储在SSD。

    冷热混合存储示意图

    image

hot_partition_count(热分区)

STORAGE_POLICY='mixed'时,需要通过hot_partition_count=n(n为正整数)定义热分区的数量。AnalyticDB for MySQL将根据分区键的值,从大到小对分区排列,最大的n个分区为热分区,其他分区为冷分区。

说明

如果存储策略(STORAGE_POLICY)的取值不是mixed,则不支持指定hot_partition_count=n,否则会报错。

block_size(数据块)

指定列式存储中每个数据块(Block)的行数,影响每次IO读取的数据量。点查场景可适当调小block_size以提高效率。

默认值说明:

  • 复制表的block_size默认值为4096。

  • 弹性模式集群版(新版)单机版(计算资源为32核以下),block_size默认值为8192。

  • 其它情况下,block_size默认值为32760。当block_size32760时,在SHOW CREATE TABLE 时,不显示block_size。

重要

若不熟悉列式存储原理,建议不要更改block_size。

engine(存储引擎)

指定内表的存储引擎。

  • 3.2.2.0以下内核版本,取值为XUANWU。若在建表时未显式指定ENGINE,则默认选择该值。

    重要

    3.1.9.5以下内核版本,如果在创建内表时显式指定了ENGINE='XUANWU',则需同时显式指定table_properties='{"format":"columnstore"}',否则建表会失败。

  • 3.2.2.0及以上内核版本,取值如下:

    • RC_DDL_ENGINE_REWRITE_XUANWUV2=true时,取值仅支持XUANWU_V2。

    • RC_DDL_ENGINE_REWRITE_XUANWUV2=false时,取值支持XUANWU_V2XUANWU。

    您可以使用SHOW ADB_CONFIG KEY=RC_DDL_ENGINE_REWRITE_XUANWUV2;命令查看参数的值。您也可以在集群级别或表级别修改RC_DDL_ENGINE_REWRITE_XUANWUV2的值

AS query_expr(CTAS)

CREATE TABLE AS query_expr表示创建表并将SELECT查询结果写入新创建的表。具体用法,请参见CREATE TABLE AS SELECT(CTAS)

重要

使用CTAS创建冷存储表(STORAGE_POLICY='COLD')时,请注意:CTASPARTITION BY仅支持直接引用列名,不支持NOW()等函数表达式作为分区键。详细限制请参见CTAS

示例

完整的建表场景和模板请参见快速建表指南。以下示例展示特定功能的用法。

新建非分区表

未定义分布键和分区键,系统自动将主键作为分布键

表定义了主键但未定义分布键,AnalyticDB for MySQL默认将主键作为分布键。

CREATE TABLE orders (
  order_id BIGINT NOT NULL COMMENT '订单ID',
  customer_id INT NOT NULL COMMENT '顾客ID',
  order_status VARCHAR(1) NOT NULL COMMENT '订单状态',
  total_price DECIMAL(15, 2) NOT NULL COMMENT '订单金额',
  order_date DATE NOT NULL COMMENT '订单日期',
  PRIMARY KEY(order_id,order_date)
);

查询建表语句,可以看到主键order_idorder_date被采纳为分布键。

SHOW CREATE TABLE orders;
+---------+-----------------------------------------------------------------------------------------------------------------------------------------------+
| Table   | Create Table                                                                                                                                  | 
+---------+-----------------------------------------------------------------------------------------------------------------------------------------------+
| orders  | CREATE TABLE `orders` (                                                                                                                       |
|         | `order_id` bigint NOT NULL COMMENT '订单ID',                                                                                                   |
|         | `customer_id` int NOT NULL COMMENT '顾客ID',                                                                                                   |
|         | `order_status` varchar(1) NOT NULL COMMENT '订单状态',                                                                                         | 
|         | `total_price` decimal(15, 2) NOT NULL COMMENT '订单金额',                                                                                      |
|         | `order_date` date NOT NULL COMMENT '订单日期',                                                                                                 |
|         | PRIMARY KEY (`order_id`,`order_date`)                                                                                                         |
|         | ) DISTRIBUTED BY HASH(`order_id`,`order_date`) INDEX_ALL='Y' STORAGE_POLICY='HOT' ENGINE='XUANWU' TABLE_PROPERTIES='{"format":"columnstore"}'  |
+---------+-----------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.04 sec)

未定义主键和分布键,系统自动增加主键和分布键

表未定义主键,也未定义分布键,AnalyticDB for MySQL将添加一个列__adb_auto_id__作为主键和分布键。

CREATE TABLE orders_new (
  order_id BIGINT NOT NULL COMMENT '订单ID',
  customer_id INT NOT NULL COMMENT '顾客ID',
  order_status VARCHAR(1) NOT NULL COMMENT '订单状态',
  total_price DECIMAL(15, 2) NOT NULL COMMENT '订单金额',
  order_date DATE NOT NULL COMMENT '订单日期'
);

查询建表语句,可以看到表中自动增加一个自增列__adb_auto_id__,该自增列作为表的主键和分布键。

SHOW CREATE TABLE orders_new;
+-------------+-----------------------------------------------------------------------------------------------------------------------------------------------+
| Table       | Create Table                                                                                                                                  | 
+-------------+-----------------------------------------------------------------------------------------------------------------------------------------------+
| orders_new  | CREATE TABLE `orders_new` (                                                                                                                   |
|             | `__adb_auto_id__` bigint AUTO_INCREMENT,                                                                                                      |
|             | `order_id` bigint NOT NULL COMMENT '订单ID',                                                                                                   |
|             | `customer_id` int NOT NULL COMMENT '顾客ID',                                                                                                   |
|             | `order_status` varchar(1) NOT NULL COMMENT '订单状态',                                                                                         | 
|             | `total_price` decimal(15, 2) NOT NULL COMMENT '订单金额',                                                                                      |
|             | `order_date` date NOT NULL COMMENT '订单日期',                                                                                                 |
|             | PRIMARY KEY (`__adb_auto_id__`)                                                                                                               |
|             | ) DISTRIBUTED BY HASH(`__adb_auto_id__`) INDEX_ALL='Y' STORAGE_POLICY='HOT' ENGINE='XUANWU' TABLE_PROPERTIES='{"format":"columnstore"}'        |
+-------------+-----------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.04 sec)

定义主键和分布键,但未定义分区键

新建表supplier,supplier_id为自增列,分布键为supplier_id,按照supplier_id值进行HASH分片。

CREATE TABLE supplier (
  supplier_id BIGINT AUTO_INCREMENT PRIMARY KEY,
  supplier_name VARCHAR,
  address INT,
  phone VARCHAR
) 
DISTRIBUTED BY HASH(supplier_id);

对部分列创建普通索引

仅对id列和date列创建普通索引,其他列不创建索引。

CREATE TABLE index_tb (
  id INT,
  sales DECIMAL(15, 2),
  date DATE,
  INDEX (id),
  INDEX (date),
  PRIMARY KEY (id)
) 
DISTRIBUTED BY HASH(id);

定义全文索引

content列创建全文索引,索引名称为fidx_c。

CREATE TABLE fulltext_tb (
  id INT,
  content VARCHAR,
  keyword VARCHAR,
  FULLTEXT INDEX fidx_c(content),
  PRIMARY KEY (id)
) 
DISTRIBUTED BY HASH(id);

关于创建和变更全文索引的更多内容,请参见创建全文索引

关于全文检索,请参见全文检索

定义向量索引

定义short_feature、float_feature为向量列,类型是array<float>,向量维数为4。

根据short_feature创建向量索引short_feature_index,根据float_feature创建向量索引float_feature_index。

CREATE TABLE fact_tb (  
  xid BIGINT NOT NULL,  
  cid BIGINT NOT NULL,  
  uid VARCHAR NOT NULL,  
  vid VARCHAR NOT NULL,  
  wid VARCHAR NOT NULL,  
  short_feature array<smallint>(4),  
  float_feature array<float>(4),  
  ann index short_feature_index(short_feature), 
  ann index float_feature_index(float_feature),  
  PRIMARY KEY (xid, cid, vid)
) 
DISTRIBUTED BY HASH(xid) PARTITION BY VALUE(cid) LIFECYCLE 4;

更多关于向量索引和向量检索的内容,请参见向量检索

定义外键索引

新增一个名为store_returns的表,通过使用外键语法FOREIGN KEYsr_item_sk列和customer表的主键列customer_id关联起来。

CREATE TABLE store_returns (
  sr_sale_id BIGINT NOT NULL PRIMARY KEY,
  sr_store_sk BIGINT,
  sr_item_sk BIGINT NOT NULL,
  FOREIGN KEY (sr_item_sk) REFERENCES customer (customer_id)
);

定义JSON Array索引

vj列创建JSON Array索引,索引名称为idx_vj。

CREATE TABLE json(
  id INT,
  vj JSON,
  INDEX idx_vj(vj->'$[*]')
)
DISTRIBUTED BY HASH(id);

关于创建和变更JSON Array索引的更多内容,请参见创建JSON Array索引JSON Array索引

常见问题

压缩与存储

建表时可以指定压缩算法或压缩级别吗?

AnalyticDB for MySQL的存储压缩由系统自动管理,建表时无法通过语法指定压缩算法或压缩级别。如果您想节省存储空间,可以指定STORAGE_POLICYCOLD数据存储在OSS,以节省成本,HOT数据存储在SSD,更注重查询性能。

列属性和列约束

自增列是从1开始递增吗?值是唯一的吗?

自增列的值不是顺序递增,也不支持从1开始递增。但自增列的值都是唯一值。

分布键、分区键与生命周期

分布键和分区键有什么区别?

根据分布键值的HASH结果不同,数据被分散到不同的分片上。在分片上,根据分区键值的不同,数据被划分为不同的分区。示意图如下。

image

建表时,是否必须指定分布键?

  • 创建分区表,不是必须手动指定分布键。如果未手动指定分布键,AnalyticDB for MySQL会采纳主键作为分布键。无主键时,自动生成一列__adb_auto_id__作为分布键和主键。

  • 创建复制表,不需要指定分布键,但需要指定DISTRIBUTED BY BROADCAST,表示每个存储节点都存储一份全量数据。

在集群变配时,是否改变分片数?

变配不会改变集群的分片数(Shard数量)。

如何查询表的分区信息?

执行SQL查询表的分区信息:

SELECT partition_id, --分区名
 row_count, -- 分区总行数
 local_data_size, --分区本地存储所占用空间大小
 index_size, -- 分区的索引大小
 pk_size, --分区的主键索引大小
 remote_data_size --分区的远端存储所占用空间大小
FROM information_schema.kepler_partitions
WHERE schema_name = '$DB'
 AND table_name ='$TABLE' 
 AND partition_id > 0;

创建分区表后,为什么查不到分区信息?

创建分区表后,查不到分区信息主要有两个原因:

  • 建表时,只是通过定义分区键设置了数据分区的规则,并没有开始创建分区。分区由分区键的键值决定。如果表还没写入数据,分区键值为空,不会创建分区。

  • 分区不是实时构建的。当写入的数据BUILD完成后,才能查看到分区信息。

解决方法:

请先写入数据,并等待BUILD任务完成。BUILD任务完成后,可以查看分区信息。

如何查询指定分区的数据?

您可以通过查询的过滤条件WHERE <分区键名称> = '<分区键值>',查询指定分区的数据。不支持类似用法:SELECT * FROM table PARTITION(202304)

完整使用示例如下。

已创建分区表orders_demo,并按日期order_date分区,建表语句示例如下:

CREATE TABLE orders_demo (
  order_id BIGINT NOT NULL COMMENT '订单ID',
  customer_id INT NOT NULL COMMENT '顾客ID',
  order_status VARCHAR(1) NOT NULL COMMENT '订单状态',
  total_price DECIMAL(15, 2) NOT NULL COMMENT '订单金额',
  order_date DATE NOT NULL COMMENT '订单日期',
  PRIMARY KEY(order_id,order_date)
)
DISTRIBUTED BY HASH(order_id) 
PARTITION BY VALUE(date_format(order_date, '%Y%m')) LIFECYCLE 30 ;

向表中写入10行示例数据,示例如下:

INSERT INTO orders_demo (order_id, customer_id, order_status, total_price, order_date)
VALUES
  (1001, 1, 'C', 150.75, '2023-10-01'),
  (1002, 2, 'P', 200.50, '2023-10-01'),
  (1003, 3, 'S', 99.99, '2023-10-01'),
  (1004, 4, 'C', 300.00, '2023-10-01'),
  (1005, 5, 'P', 450.25, '2023-10-02'),
  (1006, 6, 'S', 120.00, '2023-10-02'),
  (1007, 7, 'C', 80.50, '2023-10-03'),
  (1008, 8, 'P', 600.00, '2023-10-03'),
  (1009, 9, 'S', 250.75, '2023-10-03'),
  (1010, 10, 'C', 199.99, '2023-10-14');

手动Build分区表,完成分区的构建,示例如下:

BUILD TABLE orders_demo;
表满足一定条件后,可以自动完成BUILD。本示例为了更方便地体验后续步骤,选择手动Build表。

查询Build状态。当status字段返回FINISH时,说明orders_demo表已完成Build。

SELECT table_name, schema_name, status FROM INFORMATION_SCHEMA.KEPLER_META_BUILD_TASK WHERE table_name='ORDERS_DEMO';

本示例中,分区键order_dateDATE日期类型,查询2023-10-01分区数据,可以使用如下SQL:

SELECT * FROM orders_demo WHERE order_date='2023-10-01';

假如分区键是DATETIME类型,查询2023-10-01分区数据,查询条件需为WHERE order_date >= "2023-10-01 00:00:00" and order_date < "2023-10-02 00:00:00"。示例如下:

SELECT * FROM orders_demo WHERE order_date >= "2023-10-01 00:00:00" and order_date < "2023-10-02 00:00:00";

查询分区表时,是否必须指定分区键作为过滤条件?

如果表设置了分区键,查询时不要求过滤条件中必须包含分区键。但将分区键作为过滤条件可以显著加速查询,因为只需扫描相关分区,而无需遍历整个表,从而大幅提升查询性能。

分区键的数据类型有什么要求?

分区键的数据类型可以是数值类型、日期时间类型或表示数字的字符串类型。除此以外的数据类型,可能导致数据写入报错。

如果写入数据时,遇到报错partition format function error,说明写入分区键的值不符合数据类型的要求。

指定分区键时,能否使用除DATE_FORMATFROM_UNIXTIME以外的其他函数?

不能。定义分区键仅支持三种方法:PARTITION BY VALUE(column)、PARTITION BY VALUE(DATE_FORMAT(column,'format'))或PARTITION BY VALUE(FROM_UNIXTIME(column,'format'))。使用其他函数会报错。

说明

分区键定义方法请参见partition_options(分区键与生命周期)

如何查看分区表的生命周期?

通过SHOW CREATE TABLE <table_name>,查看建表语句。建表语句中显示分区表的生命周期。

已设置数据只保留30天(即设置LIFECYCLE30),为什么还是可以查到30天以前的数据?

可能有两个原因:

  • 分区刚刚过期,尚未被删除。过期的分区数据不会被立即删除。当该表的BUILD任务完成后,过期的分区数据才会被删除。

  • 3.2.1.1以下内核版本中创建的表,LIFECYCLE的定义为一个分片上保留多少个分区。当一个分片上,实际分区数小于LIFECYCLE设定值时,可能出现该现象。在3.2.1.1及以上内核版本中新建的表,不存在该问题。

    例如:

    • 数据分布不均。假设按日期分区,分片1有分区20231201、20231202、依次类推到20231230;分片2有分区20231202、20231203、依次类推到20231231。分片1和分片2上的分区数均为30,没有超过LIFECYCLE的设定值(30),因此分片1和分片2都不会删除分区。查询数据时,可以查到日期为20231201~20231231的数据。

    • 长时间无数据写入。假设按日期分区,分片1已有分区20231201、20231202、2023120320231204,并且在20231204后该表没有新的分区数据写入。此时分片1仅有4个分区,没有超过LIFECYCLE的设定值(30),因此不会删除分区。在20231231后,仍然可以查到日期为20231201的数据。

超过生命周期的分区数据,会被立即清理吗?

不会。分区不是实时构建和清理的。表的某个分区过期后,该表需要完成BUILD,分区才会被清理。

索引

如何查询表的聚集索引?

通过SHOW CREATE TABLE查询建表语句中定义的聚集索引。

是否支持唯一索引(UNIQUE INDEX)?

AnalyticDB for MySQL不支持唯一索引(UNIQUE INDEX)。但AnalyticDB for MySQL的主键索引就是唯一索引,可以确保主键值在表中是唯一的。

是否支持多列复合索引,例如INDEX(column1,column2)?

不支持多列复合索引。一个普通索引,只能包含一个列,例如INDEX(column1)

为什么空间总览显示主键索引、普通索引的大小为0?

新写入的数据会先进入实时引擎,主键与索引需在BUILD任务完成后才会物化到分区。若空间总览中主键索引、普通索引的大小显示为0,通常是尚未执行或未完成BUILD。可执行BUILD TABLE 表名;,待任务完成后重新查看。

列存

建表语句中的TABLE_PROPERTIES='{"format":"columnstore"}'是什么意思?

TABLE_PROPERTIES='{"format":"columnstore"}'是固定取值,表示ENGINE中的数据是列存格式。建表时您无需手动指定该属性。

是否支持一个表的部分分区行存,其他分区列存?

不支持。

其他

建表后,哪些参数可以通过ALTER TABLE变更?

ALTER TABLE支持变更以下参数:

  • table_name、column_name、column_type、COMMENT

  • 增加和删除列(主键列除外)

  • 列的默认值

  • NOT NULL变更为NULL

  • 增加和删除INDEX索引

  • 分区函数的日期格式

  • 生命周期

  • 存储策略

具体用法请参见ALTER TABLE

其他参数在建表后无法变更。

建表后,哪些属性不可修改?

以下属性建表后不可修改,如需修改请重建表并迁移数据:

  • 主键:不能增加、减少或变更主键列。

  • 分布键:不能增加、减少或变更分布键列。

  • 分区键:不能增加分区键(即不能将非分区表变更为分区表),也不支持增加、减少或修改分区键中的列。

  • 存储引擎:不能切换存储引擎(XUANWUXUANWU_V2之间不能互相切换)。

更多信息,请参见快速建表指南

一个集群最多能够创建多少个表?

一个AnalyticDB for MySQL集群的表数量上限如下:

  • 企业版集群:80000/(分片数/预留资源节点数/3)分片数/预留资源节点数/3向上取整。增加预留资源节点数,可提高内表数量的上限。

  • 基础版集群:80000/(分片数/预留资源节点数)(分片数/预留资源节点数)向上取整。增加预留资源节点数,可提高内表数量的上限。

  • 企业版、基础版及湖仓版数仓版弹性模式集群的外表数量上限:50万。

  • 湖仓版集群的内表数量上限:[80000/(分片数/存储预留资源的组数)]*2。一组存储预留资源为24 ACU。假设集群的存储预留资源为48 ACU,则存储预留资源的组数为2。扩容存储预留资源,可提高内表数量的上限。

  • 数仓版弹性模式集群的内表数量上限:[80000/(分片数/EIU数量)]*2分片数/EIU数量向上取整。EIU又叫弹性IO资源。增加EIU数量,可提高内表数量的上限。

  • 数仓版预留模式集群(具备1~20个节点组):80000/(分片数/节点组数量)分片数/节点组数量向上取整。增加节点组数量,可提高内表数量的上限。

说明

查询分片数(Shard数量):SELECT COUNT(1) FROM information_schema.kepler_meta_shards;。不支持增加或减少分片数。

AnalyticDB for MySQL的默认字符集是什么?

AnalyticDB for MySQL默认的字符集为utf-8,相当于MySQLutf8mb4字符集,暂不支持其他字符集。

如何判断一个表是内表还是外表?

您可以通过SHOW CREATE TABLE db_name.table_name;语句查看该表的DDL语句。若您在DDL语句中未找到ENGINE参数,或者ENGINE参数的取值为XUANWUXUANWU_V2,则表示该表为内表,反之则为外表

建表时有哪些数量限制?

完整的使用限制请参见使用限制。以下是建表时常见的数量限制:

限制项

上限

每张表的最大列数

4096

每个集群的最大分区总数

102400

分片(Shard)内单个分区的最大数据量

21亿条

COMMENT的最大长度

1024个字符

说明

一级Hash分区数(即DISTRIBUTED BY HASH的分片数)在建表后不可修改。系统默认分片数为128,建议根据数据量合理规划。集群的表数量上限与分片数直接相关,详见上方一个集群最多能够创建多少个表?

常见报错

partition number must larger than 0

原因:建表语句中定义了分区,但未设置分区的生命周期。

报错的建表语句示例如下:

CREATE TABLE test (
  id INT COMMENT '',
  name VARCHAR(10) COMMENT '',
  PRIMARY KEY (id, name)
) 
DISTRIBUTED BY HASH(id) PARTITION BY VALUE(name);

解决方法:在建表语句中定义分区的生命周期,正确的示例如下。

CREATE TABLE test (
  id INT COMMENT '',
  name VARCHAR(10) COMMENT '',
  PRIMARY KEY (id, name)
) 
DISTRIBUTED BY HASH(id) PARTITION BY VALUE(name) LIFECYCLE 30;
说明

仅 3.2.1.0 以下内核版本的集群会出现该报错。

Only 204800 partition allowed, the number of existing partition=>196462

原因:AnalyticDB for MySQL集群分区数量的上限默认为102400。分区数量超过上限,会出现该报错。

查询集群的分区数量,方法如下。

SELECT count(partition_id)
FROM information_schema.kepler_partitions
WHERE partition_id > 0;

解决方法:您可以通过ALTER TABLE语句调整分区的粒度。例如,按天分区改为按月分区。

partition column 'XXX' is not found in primary index=> [YYY]

原因:主键需包含分布键和分区键。如果主键未包含分区键,会出现该报错。

SQL错误示例1:

CREATE TABLE test (
  id INT COMMENT '',
  name VARCHAR(10) COMMENT '',
  PRIMARY KEY (id)
) 
DISTRIBUTED BY HASH(id) PARTITION BY VALUE(name) LIFECYCLE 30;

如果未指定主键和分布键,也会出现该报错。因为建表未指定主键和分布键时,AnalyticDB for MySQL会自动生成一列__adb_auto_id__,作为主键和分布键。此时主键只有__adb_auto_id__,因为不包含分区键,所以报错。

SQL错误示例2:

CREATE TABLE test (
  id INT COMMENT '',
  name VARCHAR(10) COMMENT ''
) 
PARTITION BY VALUE(name) LIFECYCLE 30;

解决方法:请将分区键添加到主键中。

SemanticException:only 5000 table allowed

原因:AnalyticDB for MySQL集群中表的总数量(集群中正在使用的表数量+表回收站中的表数量)有上限,超过上限,会出现该报错。不同产品系列不同产品规格的表数量上限不同,具体请参见表数量上限

解决方法:

unsigned expr not supported

原因:AnalyticDB for MySQL不支持UNSIGNED属性,即不支持无符号数。

解决方法:建表语句的列属性中不定义UNSIGNED属性。您需要自己在业务代码中实现非负数的约束。

相关文档