ByteHouse 唯一键表主要用于实现 upsert 功能。该能力是 ByteHouse 团队自研的独有特性,既能保持高效的查询性能、又支持唯一键更新。ByteHouse 唯一键表主要解决了开源 ClickHouse 不能支持高效更新操作的痛点,帮助业务更简单地开发实时分析应用。您可通过指定唯一键 UNIQUE KEY 来实现 Upsert 更新写语义,查询自动返回每个唯一键的最新值。
唯一键表即指定唯一键索引(UNIQUE KEY)的 CnchMergeTree 表,具有以下特点:
更多关于唯一键表的能力介绍、功能逻辑、使用限制等内容,请参见唯一键表。
建议 UNIQUE KEY 字段设置不超过 5 个;如果多个字段组合构成唯一键,可以使用它们的 toString(sipHash128()) 哈希作为唯一键。
如需去重,建议您优先选择使用分区级去重,再根据数据量级和写入性能需求 RPS(record per second)判断是否需要开启 bucket 去重优化。
出现如下任意一种情况时,建议开启 bucket 去重优化:
可通过在建表时设置 CLUSTER BY 子句所需列为 UNIQUE KEY 所需列子集,进而开启 bucket 去重优化。
当去重粒度数据量(拆分 bucket 前)为 m,需求峰值 RPS 为 n 时。以 max{⌈m / 20M⌉, ⌈n / 20K⌉} 取 bucket 数量(number)。如果您需要对数据量/RPS 预留一定余量,以 2 * max{⌈m /20M⌉, ⌈n / 20K⌉} 取 bucket 数量。
对于查询来说,相同 bucket 数量的 parts 在查询时会发送到同一个 worker 节点上计算。因此选择一个合适的 bucket 数量对于查询有较大影响,一般遵循如下原则:
满足写入侧需求后,使用 2 * max{⌈m / 20M⌉, ⌈n / 20K⌉} 为 write_bucket_num,bucket 数量可以根据 write_bucket_num 和 worker 数量设置:
write_bucket_num ≤ worker 数量时,可直接使用 worker 数量。若考虑长期横向扩容,可使用 worker 数量 * 2作为最终 bucket 数量。write_bucket_num > worker 数量,向上取 worker 整数倍作为最终 bucket 数量。CLUSTER BY EXPRESSION cityHash64V2(cluster_column) % N INTO N BUCKETS 子句,具体使用方法可参考创建动态分区表。CLUSTER BY 的列建议使用高基数列,否则容易出现 bucket 数据倾斜,从而影响写入和查询性能。建议使用以下 SETTINGS 参数:
max_dedup_worker_number = [cnch_vw_write 的 pod 数], pick_dedup_worker_by_mod = 1, dedup_impl_version = 'dedup_in_txn_commit'
完整建表命令示例如下:
CREATE TABLE orders ( `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) CLUSTER BY EXPRESSION cityHash64V2(product_id) % 6 INTO 6 BUCKETS UNIQUE KEY product_id SETTINGS [..., ]max_dedup_worker_number = [cnch_vw_write 的 pod 数], pick_dedup_worker_by_mod = 1, dedup_impl_version = 'dedup_in_txn_commit';
参数名 | 参数类型 | 可选值 | 无配置默认值 | 参数说明 |
|---|---|---|---|---|
max_dedup_worker_number | UInt64 | 整数 | 1 | Dedup worker 的数量,建议和 cnch_vw_write 设置的计算组 pod 数对齐。 |
pick_dedup_worker_by_mod | Bool | true/false | false | Dedup worker 的分布模式,配置为 true 时按计算组的节点总数取模可以保证分布均匀;默认为 false 时按照 hash 值分布,不一定均匀。 |
dedup_impl_version | DedupImplVersion | dedup_in_write_suffix/ | dedup_in_write_suffix | 去重实现,dedup_in_write_suffix 在写入阶段结束时执行去重逻辑;dedup_in_txn_commit 在事务预提交阶段执行去重逻辑,为优化版本。 |
ByteHouse 整体设计对于大批量数据友好,因此尽量优先选择在上游进行攒批。
注意
一般非去重模式在历史数据迁移阶段使用,正式生产环境慎用,否则会引入重复数据问题。
当您需要导入的数据满足如下两个条件时,可在 query 级别开启非去重导入:
使用以下参数为 query 级别开启非去重导入:
dedup_key_mode='append', enable_unique_partial_update = 0
完整使用示例如下:
INSERT INTO target_table SELECT * FROM source_table SETTINGS dedup_key_mode='append', enable_unique_partial_update = 0;
参数说明
参数名 | 参数类型 | 可选值 | 无配置默认值 | 参数说明 |
|---|---|---|---|---|
dedup_key_mode | DedupKeyMode | replace/throw/append/ignore | replace | 设置去重模式,不同参数对应不同的去重模式:
|
enable_unique_partial_update | Bool | true/false | true | 是否使用部分列更新模式。需要配合表级同名参数来使用,当且仅当该参数和表级都开启时部分列更新模式生效。 |
数据导入时如果存在并发写入,您可优先考虑上层对去重粒度进行写入对齐。在难以做到的情况下,可以在有去重瓶颈时开启并发写优化。ByteHouse 可通过设置表级参数,实现基于后台线程攒批去重,大批量去重性能更佳。
表级参数设置如下:
cloud_enable_staging_area=1, cloud_staging_area_wait_mode='wait_txn_visible'
完整建表命令示例如下:
CREATE TABLE orders ( `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) CLUSTER BY EXPRESSION cityHash64V2(product_id) % 6 INTO 6 BUCKETS UNIQUE KEY product_id SETTINGS [..., ]cloud_enable_staging_area=1, cloud_staging_area_wait_mode='wait_txn_visible';
参数说明
参数名 | 参数类型 | 可选值 | 无配置默认值 | 参数说明 |
|---|---|---|---|---|
cloud_enable_staging_area | Bool | true/false | false | 是否开启暂存区,可支持异步写入或者并发写入优化场景。 |
cloud_staging_area_wait_mode | StagingAreaWaitMode | no_wait/wait_txn_visible | no_wait | 开启暂存区后,数据写入模式。no_wait 表示异步写入模式,wait_txn_visible 表示同步写入模式。 |
当一次 INSERT SELECT/DELETE/UPDATE 操作影响的数据超过 1 亿行时,可以认为本次任务是大 ELT 任务。ByteHouse 支持通过设置表级参数和 query 级别参数,充分利用多 worker 资源,中心化调度去重任务。参数设置示例如下:
# 表级参数设置 dedup_impl_version = 'dedup_in_txn_commit' # query 级别参数设置 optimize_unique_table_write = 1, max_insert_threads = 8
完整使用示例如下:
表级参数设置
CREATE TABLE orders ( `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) CLUSTER BY EXPRESSION cityHash64V2(product_id) % 6 INTO 6 BUCKETS UNIQUE KEY product_id SETTINGS [..., ]dedup_impl_version = 'dedup_in_txn_commit';
Query 级别参数设置
INSERT INTO target_table SELECT * FROM source_table SETTINGS optimize_unique_table_write = 1, max_insert_threads = 8;
注意
min_insert_block_size_bytes 和 min_insert_block_size_rows,可设置为当前值×2。min_insert_block_size_bytes 和 min_insert_block_size_rows,可设置为当前值/2。参数说明
参数名 | 参数类型 | 可选值 | 无配置默认值 | 参数说明 |
|---|---|---|---|---|
dedup_impl_version | DedupImplVersion | dedup_in_write_suffix/ | dedup_in_write_suffix | 去重实现,dedup_in_write_suffix 在写入阶段结束时执行去重逻辑;dedup_in_txn_commit 在事务预提交阶段执行去重逻辑,为优化版本。 |
optimize_unique_table_write | Bool | true/false | false | 唯一键表 insert select 导入优化,仅当未开启暂存区且 dedup_impl_version='dedup_in_txn_commit' 时建议开启。 |
max_insert_threads | UInt64 | 正整数 | 0 | worker 执行 insert select 请求的写入线程池大小。 |
min_insert_block_size_bytes | UInt64 | 正整数 | 1073710080 |
|
min_insert_block_size_rows | UInt64 | 正整数 | 1048545 |
|
注意
该功能当前正在灰度发布中,若使用中遇到问题,可提交工单或联系 ByteHouse 团队。
在线导入任务与离线导入任务共存时,数据量大的离线任务会严重影响实时导入的写入延迟,导致后者有非常大的性能波动,该场景可开启优化解决。可通过通过表级参数设置,依赖暂存区,高优为实时任务分配去重带宽,减少离线大 ELT 任务对于实时写入延迟的影响。
表级参数设置示例如下:
cloud_enable_staging_area=1, cloud_staging_area_wait_mode='wait_txn_visible',real_time_task_optimize_algo='strict_optimize'
完整建表命令示例如下:
CREATE TABLE orders ( `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) CLUSTER BY EXPRESSION cityHash64V2(product_id) % 6 INTO 6 BUCKETS UNIQUE KEY product_id SETTINGS [..., ]cloud_enable_staging_area=1, cloud_staging_area_wait_mode='wait_txn_visible',real_time_task_optimize_algo='strict_optimize';
参数说明
参数名 | 参数类型 | 可选值 | 无配置默认值 | 参数说明 |
|---|---|---|---|---|
cloud_enable_staging_area | Bool | true/false | false | 是否开启暂存区,可支持异步写入或者并发写入优化场景。 |
cloud_staging_area_wait_mode | StagingAreaWaitMode | no_wait/wait_txn_visible | no_wait | 开启暂存区后,数据写入模式。no_wait 表示异步写入模式,wait_txn_visible 表示同步写入模式。 |
real_time_task_optimize_algo | RealTimeTaskOptimizeAlgo | disabled/strict_optimize/ loose_optimize | disabled | 设置离线/在线导入优化算法,不同参数值对应的效果如下:
|
-- 引擎默认保证 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 │ └─────────────────────┴────────────┴──────────┴──────────┴────────┴─────────┘
说明
使用版本字段时有以下限制:
默认情况下,相同 unique key 后写入的数据会覆盖已有的数据。这可能会带来以下问题
为了解决上面的问题,ByteHouse 唯一键表支持将表中的某个字段指定为版本字段。引擎保证写入相同 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 │ └─────────────────────┴────────────┴─────────┴──────────┴────────┴─────────┘
在某些应用场景下,用户希望在INSERT时加上一个字段来标识是否删除来扩展INSERT语义。
在 ByteHouse 唯一键表中,为每张表都添加了一个保留字段_delete_flag_,类型为UInt8, 0表示数据写入,非0表示数据删除。该字段不可在CREATE TABLE时指定,也不可查询该字段,仅可以在INSERT时指定,包括INSERT和INSERT SELECT。此外,ByteHouse 唯一键表基于保留字段_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 是 ByteHouse 在建表的时候的一种性能优化选项,在ByteHouse 中使用 Bucket table 时,系统会依据用户建表语句中提供的一个或者多个列、表达式整理表数据,将相同值的数据聚簇在同一个 bucket number 下,从而在查询计算中获得更好的性能。
非 bucket 级别唯一
当不使用 enable_bucket_level_unique_keys 指定 bucket 级别唯一时。ByteHouse 唯一键表使用表引擎指定的去重范围,默认为分区级别唯一,此时同分区下不同 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 级别唯一
当 cluster by 所需的列在 unique key 字段里都包含时,相同 unique key 的数据一定会落到固定的 bucket 中,可以优化为 bucket 去重;此时 partition_level_unique_keys 用于指定 bucket 的去重范围(分区级/表级)。
Bucket 唯一可以降低去重的数据量,从而提升写入 rps 和 P99 延时;enable_bucket_level_unique_keys = 1可以跳过 part 的 bucket 校验,进一步提升写入效率;
-- 引擎默认保证 unique key 在分区内的唯一性 -- 注:UNIQUE KEY 不支持 Nullable CREATE TABLE t7m ( `d` Date, `id` Int32, `s` String ) ENGINE = CnchMergeTree PARTITION BY d CLUSTER BY id 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; ┌──────────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'); select * from t7m order by id; ┌──────────d─┬─id─┬─s──┐ │ 2023-06-26 │ 1 │ 1a │ │ 2023-06-26 │ 2 │ 2a │ │ 2023-06-26 │ 3 │ 3d │ └────────────┴────┴────┘
ByteHouse云数仓版支持部分列更新模式。
在行更新模式时,缺省列采用默认值填充。而在列更新模式下,缺省列如果有原值会保留,否则填充默认值。详情请参见部分列更新。
ByteHouse 唯一键表对标并支持了 MySQL 的不同写入模式。
MySQL DML 语义 | MySQL | ByteHouse 唯一键表 |
|---|---|---|
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─┐ |