ClickHouse数据一致性

简介: ClickHouse数据一致性

1 准备测试表和数据

查询 CK 手册发现,即便对数据一致性支持最好的 Mergetree,也只是保证最终一致性



我们在使用 ReplacingMergeTree、SummingMergeTree 这类表引擎的时候,会出现短暂数据不一致的情况。

  在某些对一致性非常敏感的场景,通常有以下几种解决方案。

  1. 创建表
   CREATE TABLE test_a(
    user_id UInt64,
    score String,
    deleted UInt8 DEFAULT 0,
    create_time DateTime DEFAULT toDateTime(0)
   )ENGINE= ReplacingMergeTree(create_time)
   ORDER BY user_id;

其中:

  • user_id 是数据去重更新的标识;
  • create_time 是版本号字段,每组数据中 create_time 最大的一行表示最新的数据;
  • deleted 是自定的一个标记位,比如 0 代表未删除,1 代表删除数据。
  1. 写入 1000 万 测试数据
   INSERT INTO TABLE test_a(user_id,score)
   WITH(
    SELECT ['A','B','C','D','E','F','G']
   )AS dict
   SELECT number AS user_id, dict[number%7+1] FROM numbers(10000000);
  1. 修改前 50 万 行数据,修改内容包括 name 字段和 create_time 版本号字段
   INSERT INTO TABLE test_a(user_id,score,create_time)
   WITH(
   SELECT ['AA','BB','CC','DD','EE','FF','GG']
   )AS dict
   SELECT number AS user_id, dict[number%7+1], now() AS create_time FROM 
   numbers(500000);
  1. 统计总数
   SELECT COUNT() FROM test_a;
   10500000
   12

还未触发分区合并,所以还未去重。

2 手动OPTIMIZE(不推荐)

在写入数据后,立刻执行 OPTIMIZE 强制触发新写入分区的合并动作。

OPTIMIZE TABLE test_a FINAL;
语法:OPTIMIZE TABLE [db.]name [ON CLUSTER cluster] [PARTITION partition | 
PARTITION ID 'partition_id'] [FINAL] [DEDUPLICATE [BY expression]]
1234

3 通过 Group by 去重

  1. 执行去重的查询
   SELECT
   user_id ,
   argMax(score, create_time) AS score, 
   argMax(deleted, create_time) AS deleted,
   max(create_time) AS ctime 
   FROM test_a 
   GROUP BY user_id
   HAVING deleted = 0;

函数说明:

argMax(field1,field2):取 field2 最大值所在行的 field1 字段值

当我们更新数据时,会写入一行新的数据,例如上面语句中,通过查询最大的create_time 得到修改后的 score 字段值。

  1. 创建视图,方便测试
   CREATE VIEW view_test_a AS
   SELECT
   user_id ,
   argMax(score, create_time) AS score, 
   argMax(deleted, create_time) AS deleted,
   max(create_time) AS ctime 
   FROM test_a 
   GROUP BY user_id
   HAVING deleted = 0;
  1. 插入重复数据,再次查询
   #再次插入一条数据
   INSERT INTO TABLE test_a(user_id,score,create_time)
   VALUES(0,'AAAA',now())
   #再次查询
   SELECT *
   FROM view_test_a
   WHERE user_id = 0;
  1. 删除数据测试
   #再次插入一条标记为删除的数据
   INSERT INTO TABLE test_a(user_id,score,deleted,create_time) 
   VALUES(0,'AAAA',1,now());
   #再次查询,刚才那条数据看不到了
   SELECT *
   FROM view_test_a
   WHERE user_id = 0;

这行数据并没有被真正的删除,而是被过滤掉了。在一些合适的场景下,可以结合表级别的 TTL 最终将物理数据删除。

4 通过 FINAL 查询

在查询语句后增加 FINAL 修饰符,这样在查询的过程中将会执行 Merge 的特殊逻辑(例如数据去重,预聚合等)。

 但是这种方法在早期版本基本没有人使用,因为在增加 FINAL 之后,我们的查询将会变成一个单线程的执行过程,查询速度非常慢。

 在 v20.5.2.7-stable 版本中,FINAL 查询支持多线程执行,并且可以通过 max_final_threads 参数控制单个查询的线程数。但是目前读取 part 部分的动作依然是串行的。

 FINAL 查询最终的性能和很多因素相关,列字段的大小、分区的数量等等都会影响到最终的查询时间,所以还要结合实际场景取舍。


参考链接:https://github.com/ClickHouse/ClickHouse/pull/10463

分别安装了 20.4.5.36 和 21.7.3.14 两个版本的 ClickHouse 进行对比。

4.1 老版本测试

  1. 普通查询语句
select * from visits_v1 WHERE StartDate = '2014-03-17' limit 100;
  1. FINAL 查询
select * from visits_v1 FINAL WHERE StartDate = '2014-03-17' limit 100;

先前的并行查询变成了单线程。

4.2 新版本测试

  1. 普通语句查询
   select * from visits_v1 WHERE StartDate = '2014-03-17' limit 100 settings max_threads = 2;

查看执行计划:

   explain pipeline select * from visits_v1 WHERE StartDate = '2014-03-17' limit 100 settings max_threads = 2;
   (Expression) 
    ExpressionTransform × 2
    (SettingQuotaAndLimits) 
      (Limit) 
      Limit 2 → 2
        (ReadFromMergeTree) 
        MergeTreeThread × 2 0 → 1

明显将由 2 个线程并行读取 part 查询。


FINAL 查询

   select * from visits_v1 final WHERE StartDate = '2014-03-17' limit 100 
   settings max_final_threads = 2;

查询速度没有普通的查询快,但是相比之前已经有了一些提升,查看 FINAL 查询的执行计划:

   explain pipeline select * from visits_v1 final WHERE StartDate = '2014-03-17' limit 100 settings max_final_threads = 2;
   (Expression) 
   ExpressionTransform × 2 
   (SettingQuotaAndLimits) 
    (Limit) 
    Limit 2 → 2 
      (ReadFromMergeTree) 
      ExpressionTransform × 2 
        CollapsingSortedTransform × 2
          Copy 1 → 2 
            AddingSelector 
              ExpressionTransform
                MergeTree 0 → 1 

从 CollapsingSortedTransform 这一步开始已经是多线程执行,但是读取 part 部分的动作还是串行。

目录
相关文章
|
6月前
|
存储 大数据 对象存储
ClickHouse 如何实现数据一致性
本文探讨了在 ClickHouse 中实现数据一致性的方法,主要关注 `ReplacingMergeTree` 引擎。该引擎允许更新已有数据,通过定期合并操作删除重复并保持最终一致性。然而,由于合并时间不可预测,单纯依赖此引擎无法确保实时一致性。为解决此问题,文章提出了四种策略:1)手动触发合并,但不建议频繁使用;2)使用 `FINAL` 查询,但在查询时合并数据,效率较低;3)通过标记和 `GroupBy` 查询实现一致性;4)在允许一定偏差的情况下,直接使用 `ReplacingMergeTree` 保持最终一致性。在实践中,推荐结合标记列和 `GroupBy` 以保证数据一致性。
232 0
|
1月前
|
存储 关系型数据库 MySQL
一个项目用5款数据库?MySQL、PostgreSQL、ClickHouse、MongoDB区别,适用场景
一个项目用5款数据库?MySQL、PostgreSQL、ClickHouse、MongoDB——特点、性能、扩展性、安全性、适用场景比较
|
1月前
|
存储 分布式计算 数据库
阿里云国际版设置数据库云分析工作负载的 ClickHouse 版
阿里云国际版设置数据库云分析工作负载的 ClickHouse 版
|
2月前
|
存储 SQL 缓存
数据库测试|Elasticsearch和ClickHouse的对决
由于目前市场上主流的数据库有许多,这次我们选择其中一个比较典型的Elasticsearch来和ClickHouse做一次实战测试,让大家更直观地看到真实的比对数据,从而对这两个数据库有更深入的了解,也就能理解为什么我们会选择ClickHouse。
数据库测试|Elasticsearch和ClickHouse的对决
|
1月前
|
存储 关系型数据库 MySQL
四种数据库对比MySQL、PostgreSQL、ClickHouse、MongoDB——特点、性能、扩展性、安全性、适用场景
四种数据库对比 MySQL、PostgreSQL、ClickHouse、MongoDB——特点、性能、扩展性、安全性、适用场景
|
5月前
|
DataWorks API 调度
DataWorks产品使用合集之在调度配置配置了节点的上游节点输出,没办法自动生成这个flow的依赖,该怎么操作
DataWorks作为一站式的数据开发与治理平台,提供了从数据采集、清洗、开发、调度、服务化、质量监控到安全管理的全套解决方案,帮助企业构建高效、规范、安全的大数据处理体系。以下是对DataWorks产品使用合集的概述,涵盖数据处理的各个环节。
|
5月前
|
DataWorks 安全 关系型数据库
DataWorks产品使用合集之建了 polar 与clickhouse的数据源。为什么数据库这里总是mysql呢
DataWorks作为一站式的数据开发与治理平台,提供了从数据采集、清洗、开发、调度、服务化、质量监控到安全管理的全套解决方案,帮助企业构建高效、规范、安全的大数据处理体系。以下是对DataWorks产品使用合集的概述,涵盖数据处理的各个环节。
|
4月前
|
存储 大数据 关系型数据库
从 ClickHouse 到阿里云数据库 SelectDB 内核 Apache Doris:快成物流的数智化货运应用实践
目前已经部署在 2 套生产集群,存储数据总量达百亿规模,覆盖实时数仓、BI 多维分析、用户画像、货运轨迹信息系统等业务场景。
|
5月前
|
存储 SQL 运维
OLAP数据库选型指南:Doris与ClickHouse的深入对比与分析
OLAP数据库选型指南:Doris与ClickHouse的深入对比与分析
|
6月前
|
关系型数据库 MySQL 数据库
ClickHouse(07)ClickHouse数据库引擎解析
ClickHouse支持多种数据库引擎,包括Atomic(默认)、MySQL、MaterializeMySQL、Lazy、PostgreSQL、MaterializedPostgreSQL。Atomic提供非阻塞的表操作和原子的表交换,有UUID标识和延迟删除功能。MySQL引擎允许与远程MySQL服务器交互,支持INSERT和SELECT,不支持RENAME操作。PostgreSQL引擎类似,可与远程PostgreSQL服务进行读写操作。SQLite引擎用于连接SQLite数据库。实验性引擎如MaterializeMySQL和MaterializedPostgreSQL用于实现实时数据同步。
559 5