ByteHouse Unique 表主要用于实现 upsert 功能。该能力是 ByteHouse 团队自研的独有特性,既能保持高效的查询性能、又支持主键更新。主要解决了开源 ClickHouse 不能支持高效更新操作的痛点,帮助业务更简单地开发实时分析应用。用户通过指定唯一键 UNIQUE KEY 来实现 Upsert 更新写语义,查询自动返回每个唯一键的最新值。
Unique 表主要具有以下特点:
注意
当数据量超过亿级时,去重效率会严重下降,进而影响写入效率。
推荐使用时,单分区数据量不超过千万级别。若为全表唯一,则全表数据量建议不超过千万级别。
上述场景都可以通过唯一键 upsert 功能来支持,不管是幂等还是更新的需求。
-- 引擎默认保证 unique key 在分区内的唯一性 -- 注:UNIQUE KEY 不支持 Nullable CREATE TABLE t1 ( `event_time` DateTime, `product_id` UInt64, `city` String, `category` String, `amount` UInt32, `revenue` UInt64 ) ENGINE = CnchMergeTree PARTITION BY toDate(event_time) ORDER BY (city, category) UNIQUE KEY product_id; INSERT INTO t1 VALUES ('2020-10-29 23:40:00', 10001, 'Beijing', '男装', 5, 500), ('2020-10-29 23:40:00', 10002, 'Beijing', '男装', 2, 200), ('2020-10-29 23:40:00', 10003, 'Beijing', '男装', 1, 100); -- 写入相同 key 的数据可以实现更新(upsert语义) INSERT INTO t1 VALUES ('2020-10-29 23:50:00', 10002, 'Beijing', '男装', 4, 400), ('2020-10-29 23:50:00', 10003, 'Beijing', '男装', 2, 200), ('2020-10-29 23:50:00', 10004, 'Beijing', '男装', 1, 100), ('2020-10-30 00:00:05', 10001, 'Beijing', '男装', 1, 100), ('2020-10-30 00:00:05', 10002, 'Beijing', '男装', 2, 200); -- 查询自动返回每个key最新的数据 select * from t1 order by toDate(event_time), product_id; ┌──────────event_time─┬─product_id─┬─city────┬─category─┬─amount─┬─revenue─┐ │ 2020-10-29 23:40:00 │ 10001 │ Beijing │ 男装 │ 5 │ 500 │ │ 2020-10-29 23:50:00 │ 10002 │ Beijing │ 男装 │ 4 │ 400 │ │ 2020-10-29 23:50:00 │ 10003 │ Beijing │ 男装 │ 2 │ 200 │ │ 2020-10-29 23:50:00 │ 10004 │ Beijing │ 男装 │ 1 │ 100 │ │ 2020-10-30 00:00:05 │ 10001 │ Beijing │ 男装 │ 1 │ 100 │ │ 2020-10-30 00:00:05 │ 10002 │ Beijing │ 男装 │ 2 │ 200 │ └─────────────────────┴────────────┴─────────┴──────────┴────────┴─────────┘
-- UNIQUE KEY 可以包含多个字段和表达式 -- sipHash64 是一种快速且低冲突率的哈希函数,使用 sipHash64 作为 unique key 需要考虑到可能的 hash 冲突 -- sipHash64:https://clickhouse.com/docs/en/sql-reference/functions/hash-functions#siphash64 CREATE TABLE t1m ( `event_time` DateTime, `product_id` UInt64, `city` String, `category` String, `amount` UInt32, `revenue` UInt64 ) ENGINE = CnchMergeTree PARTITION BY toDate(event_time) ORDER BY (city, category) UNIQUE KEY (product_id, sipHash64(city)); INSERT INTO t1m VALUES ('2020-10-29 23:40:00', 10001, 'Beijing', '男装', 5, 500), ('2020-10-29 23:40:00', 10002, 'Beijing', '男装', 2, 200), ('2020-10-29 23:40:00', 10003, 'Beijing', '男装', 1, 100), ('2020-10-29 23:50:00', 10002, 'Shanghai', '男装', 4, 400), ('2020-10-29 23:50:00', 10003, 'Beijing', '男装', 2, 200), ('2020-10-29 23:50:00', 10004, 'Beijing', '男装', 1, 100); select * from t1m; ┌──────────event_time─┬─product_id─┬─city─────┬─category─┬─amount─┬─revenue─┐ │ 2020-10-29 23:40:00 │ 10001 │ Beijing │ 男装 │ 5 │ 500 │ │ 2020-10-29 23:40:00 │ 10002 │ Beijing │ 男装 │ 2 │ 200 │ │ 2020-10-29 23:50:00 │ 10003 │ Beijing │ 男装 │ 2 │ 200 │ │ 2020-10-29 23:50:00 │ 10004 │ Beijing │ 男装 │ 1 │ 100 │ │ 2020-10-29 23:50:00 │ 10002 │ Shanghai │ 男装 │ 4 │ 400 │ └─────────────────────┴────────────┴──────────┴──────────┴────────┴─────────┘
-- 设置 partition_level_unique_keys = 0 后,CNCH 保证 unique key 在表级别唯一 CREATE TABLE t2 ( `event_time` DateTime, `product_id` UInt64, `city` String, `category` String, `amount` UInt32, `revenue` UInt64 ) ENGINE = CnchMergeTree PARTITION BY toDate(event_time) ORDER BY (city, category) UNIQUE KEY product_id SETTINGS partition_level_unique_keys = 0; -- 插入测试数据 INSERT INTO t2 VALUES ('2020-10-29 23:40:00', 10001, 'Beijing', '男装', 5, 500), ('2020-10-29 23:40:00', 10002, 'Beijing', '男装', 2, 200), ('2020-10-29 23:40:00', 10003, 'Beijing', '男装', 1, 100); INSERT INTO t2 VALUES ('2020-10-29 23:50:00', 10002, 'Beijing', '男装', 4, 400), ('2020-10-29 23:50:00', 10003, 'Beijing', '男装', 2, 200), ('2020-10-29 23:50:00', 10004, 'Beijing', '男装', 1, 100), ('2020-10-30 00:00:05', 10001, 'Beijing', '男装', 1, 100), ('2020-10-30 00:00:05', 10002, 'Beijing', '男装', 2, 200); -- 可以看到,10001 和 10002 这两个产品从 2020-10-29 分区更新到了 2020-10-30 分区 select * from t2 order by toDate(event_time), product_id; ┌──────────event_time─┬─product_id─┬─city────┬─category─┬─amount─┬─revenue─┐ │ 2020-10-29 23:50:00 │ 10003 │ Beijing │ 男装 │ 2 │ 200 │ │ 2020-10-29 23:50:00 │ 10004 │ Beijing │ 男装 │ 1 │ 100 │ │ 2020-10-30 00:00:05 │ 10001 │ Beijing │ 男装 │ 1 │ 100 │ │ 2020-10-30 00:00:05 │ 10002 │ Beijing │ 男装 │ 2 │ 200 │ └─────────────────────┴────────────┴─────────┴──────────┴────────┴─────────┘
说明
使用版本字段时有以下限制:
默认情况下,相同 unique key 后写入的数据会覆盖已有的数据。这可能会带来以下问题
为了解决上面的问题,Cnch Unique 表支持将表中的某个字段指定为版本字段。引擎保证写入相同 key 的数据时,只有数据版本 >= 已有版本时,才会进行覆盖。版本字段支持所有UInt类型和Data/DateTime,且不能为 Nullable。
-- CnchMergeTree 括号内参数为可选的版本字段 CREATE TABLE t3 ( `event_time` DateTime, `product_id` UInt64, `city` String, `category` String, `amount` UInt32, `revenue` UInt64 ) ENGINE = CnchMergeTree(event_time) PARTITION BY toDate(event_time) ORDER BY (city, category) UNIQUE KEY product_id; INSERT INTO t3 VALUES ('2020-10-29 23:40:00', 10001, 'Beijing', '男装', 5, 500), ('2020-10-29 23:40:00', 10002, 'Beijing', '男装', 2, 200), ('2020-10-29 23:50:00', 10001, 'Beijing', '男装', 8, 800), ('2020-10-29 23:50:00', 10002, 'Beijing', '男装', 5, 500); -- 回溯前两条数据,由于版本 < 已有版本,写入时自动跳过 INSERT INTO t3 VALUES ('2020-10-29 23:40:00', 10001, 'Beijing', '男装', 5, 500), ('2020-10-29 23:40:00', 10002, 'Beijing', '男装', 2, 200); -- 10001 和 10002 的版本没有回退 select * from t3 order by toDate(event_time), product_id; ┌──────────event_time─┬─product_id─┬─city────┬─category─┬─amount─┬─revenue─┐ │ 2020-10-29 23:50:00 │ 10001 │ Beijing │ 男装 │ 8 │ 800 │ │ 2020-10-29 23:50:00 │ 10002 │ Beijing │ 男装 │ 5 │ 500 │ └─────────────────────┴────────────┴─────────┴──────────┴────────┴─────────┘ -- 继续回溯后两条数据,并写入两条新版本数据 INSERT INTO t3 VALUES ('2020-10-29 23:50:00', 10001, 'Beijing', '男装', 8, 800), ('2020-10-29 23:50:00', 10002, 'Beijing', '男装', 5, 500), ('2020-10-29 23:55:00', 10001, 'Beijing', '男装', 10, 1000), ('2020-10-29 23:55:00', 10002, 'Beijing', '男装', 7, 700); -- 查询自动返回最新版本的数据 select * from t3 order by toDate(event_time), product_id; ┌──────────event_time─┬─product_id─┬─city────┬─category─┬─amount─┬─revenue─┐ │ 2020-10-29 23:55:00 │ 10001 │ Beijing │ 男装 │ 10 │ 1000 │ │ 2020-10-29 23:55:00 │ 10002 │ Beijing │ 男装 │ 7 │ 700 │ └─────────────────────┴────────────┴─────────┴──────────┴────────┴─────────┘
考虑以下Lambda架构的需求场景
我们可以将分区字段(日期)作为版本字段来实现该场景,然而这需要额外存储一个日期字段。由于分区下所有数据的日期都是一样的,这样做显然存在资源浪费。
为了优化该场景,Cnch Unique 表支持直接使用分区表达式作为版本。当引擎发现版本字段为分区字段时,会自动从元数据中读取版本,避免额外的数据读写。
-- 创建一张按天分区、表粒度唯一的 unique 表,使用分区字段作为版本 CREATE TABLE t4 ( `event_time` DateTime, `product_id` UInt64, `city` String, `category` String, `amount` UInt32, `revenue` UInt64 ) ENGINE = CnchMergeTree(toDate(event_time)) PARTITION BY toDate(event_time) ORDER BY (city, category) UNIQUE KEY product_id SETTINGS partition_level_unique_keys = 0; -- 10-29 实时任务数据写入 INSERT INTO t4 VALUES ('2020-10-29 10:00:00', 10001, 'Beijing', '男装', 5, 500), ('2020-10-29 10:00:00', 10002, 'Beijing', '男装', 2, 200), ('2020-10-29 10:10:00', 10001, 'Beijing', '男装', 8, 800), ('2020-10-29 10:10:00', 10002, 'Beijing', '男装', 5, 500); -- 10-30 实时任务数据写入,将 10002 更新到 10-30 分区 INSERT INTO t4 VALUES ('2020-10-30 08:00:00', 10002, 'Beijing', '男装', 10, 1000), ('2020-10-30 08:00:00', 10003, 'Beijing', '男装', 3, 300); -- 10-30 离线任务重写 10-29 数据 INSERT INTO t4 VALUES ('2020-10-29 10:10:00', 10001, 'Beijing', '男装', 7, 700), ('2020-10-29 10:10:00', 10002, 'Beijing', '男装', 5, 500); -- 离线任务只会覆盖 10001 数据,10002 保留 10-30 中的最新数据 select * from t4 order by toDate(event_time), product_id; ┌──────────event_time─┬─product_id─┬─city────┬─category─┬─amount─┬─revenue─┐ │ 2020-10-29 10:10:00 │ 10001 │ Beijing │ 男装 │ 7 │ 700 │ │ 2020-10-30 08:00:00 │ 10002 │ Beijing │ 男装 │ 10 │ 1000 │ │ 2020-10-30 08:00:00 │ 10003 │ Beijing │ 男装 │ 3 │ 300 │ └─────────────────────┴────────────┴─────────┴──────────┴────────┴─────────┘
在某些应用场景下,用户希望在INSERT时加上一个字段来标识是否删除来扩展INSERT语义。
在 Cnch Unique 表中,为每张表都添加了一个保留字段_delete_flag_
,类型为UInt8
, 0表示数据写入,非0表示数据删除。该字段不可在CREATE TABLE时指定,也不可查询该字段,仅可以在INSERT时指定,包括INSERT和INSERT SELECT。此外,Cnch Unique 表基于保留字段_delete_flag_
,支持了 DELETE FROM
子句。
用法示例如下:
-- 引擎默认保证 unique key 在分区内的唯一性 -- 注:UNIQUE KEY 不支持 Nullable CREATE TABLE t5 ( `event_time` DateTime, `product_id` UInt64, `city` String, `category` String, `amount` UInt32, `revenue` UInt64 ) ENGINE = CnchMergeTree PARTITION BY toDate(event_time) ORDER BY (city, category) UNIQUE KEY product_id; INSERT INTO t5 VALUES ('2020-10-29 23:40:00', 10001, 'Beijing', '男装', 5, 500), ('2020-10-29 23:40:00', 10002, 'Beijing', '男装', 2, 200), ('2020-10-29 23:40:00', 10003, 'Beijing', '男装', 1, 100); -- 指定删除字段进行数据删除,删除字段设置非0时表示删除,设置为0时表示正常的upsert操作 INSERT INTO t5 (event_time, product_id, city, category, amount, revenue, _delete_flag_) VALUES ('2020-10-29 23:50:00', 10001, 'Beijing', '男装', 4, 400, 5), ('2020-10-29 23:50:00', 10002, 'Beijing', '男装', 2, 200, 1), ('2020-10-29 23:50:00', 10004, 'Beijing', '男装', 1, 100, 0); -- 查询结果中包含了新加入的一行数据,并删除了两行旧数据 select * from t5 order by toDate(event_time), product_id; ┌──────────event_time─┬─product_id─┬─city────┬─category─┬─amount─┬─revenue─┐ │ 2020-10-29 23:40:00 │ 10003 │ Beijing │ 男装 │ 1 │ 100 │ │ 2020-10-29 23:50:00 │ 10004 │ Beijing │ 男装 │ 1 │ 100 │ └─────────────────────┴────────────┴─────────┴──────────┴────────┴─────────┘
-- 引擎默认保证 unique key 在分区内的唯一性 -- 注:UNIQUE KEY 不支持 Nullable -- 指定版本号 CREATE TABLE t5m ( `event_time` DateTime, `product_id` UInt64, `city` String, `category` String, `amount` UInt32, `revenue` UInt64, `version` UInt64 ) ENGINE = CnchMergeTree(version) PARTITION BY toDate(event_time) ORDER BY (city, category) UNIQUE KEY product_id; INSERT INTO t5m VALUES ('2020-10-29 23:40:00', 10001, 'Beijing', '男装', 5, 500, 10), ('2020-10-29 23:40:00', 10002, 'Beijing', '男装', 2, 200, 10), ('2020-10-29 23:40:00', 10003, 'Beijing', '男装', 1, 100, 10); -- 指定删除字段并指定版本号,版本号小于查询结果中相应行的版本号,删除操作不会起作用 INSERT INTO t5m (event_time, product_id, city, category, amount, revenue, version, _delete_flag_) VALUES ('2020-10-29 23:40:00', 10001, 'Beijing', '男装', 4, 400, 5, 1), ('2020-10-29 23:40:00', 10002, 'Beijing', '男装', 2, 200, 5, 1), ('2020-10-29 23:40:00', 10003, 'Beijing', '男装', 1, 100, 5, 1); -- 查询结果不变,没有任何数据被删除 select * from t5m order by toDate(event_time), product_id; ┌──────────event_time─┬─product_id─┬─city────┬─category─┬─amount─┬─revenue─┬─version─┐ │ 2020-10-29 23:40:00 │ 10001 │ Beijing │ 男装 │ 5 │ 500 │ 10 │ │ 2020-10-29 23:40:00 │ 10002 │ Beijing │ 男装 │ 2 │ 200 │ 10 │ │ 2020-10-29 23:40:00 │ 10003 │ Beijing │ 男装 │ 1 │ 100 │ 10 │ └─────────────────────┴────────────┴─────────┴──────────┴────────┴─────────┴─────────┘ -- 指定删除字段进行数据删除,不指定版本号或者版本号设置为0,删除操作会跳过版本检查,直接执行 INSERT INTO t5m (event_time, product_id, city, category, amount, revenue, _delete_flag_) VALUES ('2020-10-29 23:40:00', 10001, 'Beijing', '男装', 4, 400, 1); INSERT INTO t5m (event_time, product_id, city, category, amount, revenue, version, _delete_flag_) VALUES ('2020-10-29 23:40:00', 10003, 'Beijing', '男装', 1, 100, 0, 1); -- 查询结果删除了两行旧数据 select * from t5m order by toDate(event_time), product_id; ┌──────────event_time─┬─product_id─┬─city────┬─category─┬─amount─┬─revenue─┬─version─┐ │ 2020-10-29 23:40:00 │ 10002 │ Beijing │ 男装 │ 2 │ 200 │ 10 │ └─────────────────────┴────────────┴─────────┴──────────┴────────┴─────────┴─────────┘
-- 引擎默认保证 unique key 在分区内的唯一性 -- 注:UNIQUE KEY 不支持 Nullable CREATE TABLE t5x ( `event_time` DateTime, `product_id` UInt64, `city` String, `category` String, `amount` UInt32, `revenue` UInt64 ) ENGINE = CnchMergeTree PARTITION BY toDate(event_time) ORDER BY (city, category) UNIQUE KEY product_id; INSERT INTO t5x VALUES ('2020-10-29 23:40:00', 10001, 'Beijing', '男装', 5, 500), ('2020-10-29 23:40:00', 10002, 'Beijing', '男装', 2, 200), ('2020-10-29 23:40:00', 10003, 'Beijing', '男装', 1, 100); -- 通过INSERT SELECT 来删除revenue >= 200的数据 INSERT INTO t5x (event_time, product_id, city, category, amount, revenue, _delete_flag_) SELECT *, 1 as _delete_flag_ from t5x where revenue >= 200; -- 查询结果中已删除revenue >= 200的数据 select * from t5x order by toDate(event_time), product_id; ┌──────────event_time─┬─product_id─┬─city────┬─category─┬─amount─┬─revenue─┐ │ 2020-10-29 23:40:00 │ 10003 │ Beijing │ 男装 │ 1 │ 100 │ └─────────────────────┴────────────┴─────────┴──────────┴────────┴─────────┘
-- 引擎默认保证 unique key 在分区内的唯一性 -- 注:UNIQUE KEY 不支持 Nullable CREATE TABLE t5y ( `event_time` DateTime, `product_id` UInt64, `city` String, `category` String, `amount` UInt32, `revenue` UInt64 ) ENGINE = CnchMergeTree PARTITION BY toDate(event_time) ORDER BY (city, category) UNIQUE KEY product_id; INSERT INTO t5y VALUES ('2020-10-29 23:40:00', 10001, 'Beijing', '男装', 5, 500), ('2020-10-29 23:40:00', 10002, 'Beijing', '男装', 2, 200), ('2020-10-29 23:40:00', 10003, 'Beijing', '男装', 1, 100); -- 通过 DELETE FROM 来删除revenue == 500的数据 DELETE FROM t5y WHERE revenue = 500; -- 通过 DELETE FROM 来删除revenue >= 200的数据 DELETE FROM t5y WHERE revenue >= 200; -- 查询结果中已删除revenue >= 200的数据 select * from t5y order by toDate(event_time), product_id; ┌──────────event_time─┬─product_id─┬─city────┬─category─┬─amount─┬─revenue─┐ │ 2020-10-29 23:40:00 │ 10003 │ Beijing │ 男装 │ 1 │ 100 │ └─────────────────────┴────────────┴─────────┴──────────┴────────┴─────────┘
-- 引擎默认保证 unique key 在分区内的唯一性 -- 注:UNIQUE KEY 不支持 Nullable CREATE TABLE t6 ( `event_time` DateTime, `product_id` UInt64, `city` String, `category` String, `amount` UInt32, `revenue` UInt64 ) ENGINE = CnchMergeTree PARTITION BY toDate(event_time) ORDER BY (city, category) UNIQUE KEY product_id; INSERT INTO t6 VALUES ('2020-10-29 23:40:00', 10001, 'Beijing', '男装', 5, 500), ('2020-10-29 23:40:00', 10002, 'Beijing', '男装', 2, 200), ('2020-10-29 23:40:00', 10003, 'Beijing', '男装', 1, 100); -- 通过 UPDATE 语句来进行更新 UPDATE t6 SET amount = 10, revenue = 1000 WHERE event_time = '2020-10-29 23:40:00' and product_id=10001; -- 查询结果中 10001 数据行进行了 UPDATE 变更 select * from t6 order by toDate(event_time), product_id; ┌──────────event_time─┬─product_id─┬─city────┬─category─┬─amount─┬─revenue─┐ │ 2020-10-29 23:40:00 │ 10001 │ Beijing │ 男装 │ 10 │ 1000 │ │ 2020-10-29 23:40:00 │ 10002 │ Beijing │ 男装 │ 2 │ 200 │ │ 2020-10-29 23:40:00 │ 10003 │ Beijing │ 男装 │ 1 │ 100 │ └─────────────────────┴────────────┴─────────┴──────────┴────────┴─────────┘ -- 通过 UPDATE 语句来进行更新,使用 limit 限定范围 UPDATE t6 SET category = concat('新',category) WHERE event_time = '2020-10-29 23:40:00' order by product_id limit 1; -- 查询结果中仅 10001 数据行进行了 UPDATE 变更 select * from t6 order by toDate(event_time), product_id; ┌──────────event_time─┬─product_id─┬─city────┬─category─┬─amount─┬─revenue─┐ │ 2020-10-29 23:40:00 │ 10001 │ Beijing │ 新男装 │ 10 │ 1000 │ │ 2020-10-29 23:40:00 │ 10002 │ Beijing │ 男装 │ 2 │ 200 │ │ 2020-10-29 23:40:00 │ 10003 │ Beijing │ 男装 │ 1 │ 100 │ └─────────────────────┴────────────┴─────────┴──────────┴────────┴─────────┘ -- 通过 UPDATE 语句交换列 UPDATE t6 SET amount=revenue, revenue=amount WHERE event_time = '2020-10-29 23:40:00'; -- 查询结果 amount 、revenue 列进行了交换 select * from t6 order by toDate(event_time), product_id; ┌──────────event_time─┬─product_id─┬─city────┬─category─┬─amount─┬─revenue─┐ │ 2020-10-29 23:40:00 │ 10001 │ Beijing │ 新男装 │ 1000 │ 10 │ │ 2020-10-29 23:40:00 │ 10002 │ Beijing │ 男装 │ 200 │ 2 │ │ 2020-10-29 23:40:00 │ 10003 │ Beijing │ 男装 │ 100 │ 1 │ └─────────────────────┴────────────┴─────────┴──────────┴────────┴─────────┘
Bucket table 是 Cnch 在建表的时候的一种性能优化选项,在 Cnch 中使用 Bucket table 时,系统会依据用户建表语句中提供的一个或者多个列、表达式整理表数据,将相同值的数据聚簇在同一个 bucket number 下,从而在查询计算中获得更好的性能。
当不使用 enable_bucket_level_unique_keys
指定 bucket 级别唯一时。Cnch Unique 表使用表引擎指定的去重范围,默认为分区级别唯一,此时同分区下不同 bucket 不会存在重复数据。
-- 引擎默认保证 unique key 在分区内的唯一性 -- 注:UNIQUE KEY 不支持 Nullable CREATE TABLE t7 ( `d` Date, `id` Int32, `s` String ) ENGINE = CnchMergeTree PARTITION BY d CLUSTER BY s INTO 10 BUCKETS ORDER BY s UNIQUE KEY id; INSERT INTO t7 VALUES ('2023-06-26', 1, '1a'), ('2023-06-26', 2, '2a'), ('2023-06-26', 3, '3a'), ('2023-06-26', 3, '3b'), ('2023-06-26', 3, '3c'); select * from t7 order by id; ┌──────────d─┬─id─┬─s──┐ │ 2023-06-26 │ 1 │ 1a │ │ 2023-06-26 │ 2 │ 2a │ │ 2023-06-26 │ 3 │ 3c │ └────────────┴────┴────┘
在 bucket 级别唯一的情况下,相同分区的不同 bucket 下可能存在相同 unique key 的数据;此时 partition_level_unique_keys
用于指定 bucket 的去重范围(分区级/表级)。
如果 Cnch Unique 表为分区级别唯一,且需要在 **enable_bucket_level_unique_keys = 1
**同时达到分区级唯一的效果,则需要用户侧保证 cluster by 字段与 unique key 字段的唯一对应关系。注意:仅需要保证在cluster by里所需的列在unique key字段里都包含即可。
-- 引擎默认保证 unique key 在分区内的唯一性 -- 注:UNIQUE KEY 不支持 Nullable CREATE TABLE t7m ( `d` Date, `id` Int32, `s` String ) ENGINE = CnchMergeTree PARTITION BY d CLUSTER BY s INTO 10 BUCKETS ORDER BY s UNIQUE KEY id SETTINGS enable_bucket_level_unique_keys = 1; INSERT INTO t7m VALUES ('2023-06-26', 1, '1a'), ('2023-06-26', 2, '2a'), ('2023-06-26', 3, '3a'), ('2023-06-26', 3, '3b'), ('2023-06-26', 3, '3c'); select * from t7m order by id, s; ┌──────────d─┬─id─┬─s──┐ │ 2023-06-26 │ 1 │ 1a │ │ 2023-06-26 │ 2 │ 2a │ │ 2023-06-26 │ 3 │ 3c │ └────────────┴────┴────┘ INSERT INTO t7m VALUES ('2023-06-26', 3, '3d'); -- 相同分区,不同 bucket 存在相同的 unique key select * from t7m order by id, s; ┌──────────d─┬─id─┬─s──┐ │ 2023-06-26 │ 1 │ 1a │ │ 2023-06-26 │ 2 │ 2a │ │ 2023-06-26 │ 3 │ 3c │ │ 2023-06-26 │ 3 │ 3d │ └────────────┴────┴────┘
ByteHouse云数仓版支持部分列更新模式。
在行更新模式时,缺省列采用默认值填充。而在列更新模式下,缺省列如果有原值会保留,否则填充默认值。详情请参见部分列更新。
CNCH Unique 表对标并支持了 MySQL 的不同写入模式。
MySQL DML 语义 | MySQL | CNCH Unique 表 |
---|---|---|
DDL 示例 |
|
|
insert into |
|
|
insert ignore |
┌─a─┬─b─┐ |
┌─a─┬─b─┐ |
replace into |
┌─a─┬─b─┐
Query OK, 2 rows affected (0.11 sec)
┌─a─┬─b─┐ |
┌─a─┬─b─┐
┌─a─┬─b─┐ |
说明
在并发 INSERT 时的性能情况:
注意
此情况下包含以下限制:
[U]Int8/16/32/64, Boolean, Date, DateTime, String
这些数据类型可以用作 UNIQUE KEY
UNIQUE KEY
不可以与 CLUSTER BY
一起使用(未来会提供支持);String
类型的UNIQUE KEY
大小必须 <= 1 MB (此值取决于 max_string_size_for_unique_key
),否则insert
会失败.DELETE FROM [db.]table WHERE expr;
UPDATE table_name SET assignment_list [WHERE where_condition] [ORDER BY ...] [LIMIT ...]