专栏首页机器学习与系统ClickHouse是如何批量写入的?

ClickHouse是如何批量写入的?

简介

批量写入又称为bulk write,对于单表插入多条数据的场景,可以减少插入请求数量,提高吞吐量和效率。clickhouse官方Golang驱动clickhouse-go[1]支持该关键特性,但是文档的介绍不是很详细,只有一句:

Bulk write support : begin->prepare->(in loop exec)->commit

并没有详细介绍用法和原理,笔者在开发业务时使用的库是sqlx[2],sql也支持clickhouse-go驱动。参考了官方样例代码[3]:

...
tx, err := connect.Begin()
checkErr(err)
stmt, err := tx.Prepare("INSERT INTO example (country_code, os_id, browser_id, categories, action_day, action_time) VALUES (?, ?, ?, ?, ?, ?)")
checkErr(err)

for i := 0; i < 100; i++ {
 if _, err := stmt.Exec(
  "RU",
  10+i,
  100+i,
  []int16{1, 2, 3},
  time.Now(),
  time.Now(),
 ); err != nil {
  log.Fatal(err)
 }
}
...

我写的bulk write类似上面的代码,但是提交给同事review时,他提出了疑问:stmt.Exec是每次执行都发送写请求到数据库吗?这个问题其实我不敢肯定,官方文档也说得不明确。考虑到严谨性,让自己的PR更有说服力,自己去翻看了相关源代码。

这里需要指出,如果利用编辑器里的代码跳转功能会跳到database/sql库中的Exec函数实现,实际上我们要看的代码是clickhouse-go中的实现,至于编辑器跳转到database/sql中的原因,书写此文时笔者也没弄清楚,先挖个坑吧

核心实现

stmt.Exec的核心代码如下[4]:

func (stmt *stmt) execContext(ctx context.Context, args []driver.Value) (driver.Result, error) {
 if stmt.isInsert {
  stmt.counter++
  if err := stmt.ch.block.AppendRow(args); err != nil {
   return nil, err
  }
  if (stmt.counter % stmt.ch.blockSize) == 0 {
   stmt.ch.logf("[exec] flush block")
   if err := stmt.ch.writeBlock(stmt.ch.block); err != nil {
    return nil, err
   }
   if err := stmt.ch.encoder.Flush(); err != nil {
    return nil, err
   }
  }
  return emptyResult, nil
 }
 if err := stmt.ch.sendQuery(stmt.bind(convertOldArgs(args))); err != nil {
  return nil, err
 }
 if err := stmt.ch.process(); err != nil {
  return nil, err
 }
 return emptyResult, nil
}

上面的代码不多,非常清晰,当执行Exec时,stmt.ch.block.AppendRow(args)会先把sql参数附加到本地缓存block中,然后(stmt.counter % stmt.ch.blockSize)判断本地缓存大小是否到达阈值,到达则执行Flush(),将数据写入远端。综上,clickhouse-go中的核心实现逻辑是:

  1. 底层维护一个缓存block,同时设置block_size控制缓存大小
  2. 执行stmt.Exec时,不会直接写入远程ClickHouse中,而是将插入参数Append到block中
  3. 每次Append后,判断block的size和block_size的关系,如果正好整除,则刷新block(即写入clickhouse)

因此block_size这个参数很重要,它表示本地缓存的上限,如果很大的话,程序会占用一些内存。笔者起初设置为100000,在调试日志中看不到stmt.ch.logf("[exec] flush block")打印的log,设置小后就看到下面的输出:

...
[clickhouse][connect=1][begin] tx=false, data=false
[clickhouse][connect=1][prepare]
[clickhouse][connect=1][read meta] <- data: packet=1, columns=6, rows=0
[clickhouse][connect=1][exec] flush block
[clickhouse][connect=1][exec] flush block
....

总结

很多数据库驱动都支持bulk write特性,clickhouse-go这个驱动也不例外,但是它的文档写得不是很详细,只是在文档中指明要放在begin/commit中做。再加上clickhouse不支持事务,begin/commit这种写法会让人困惑。

本文通过分析clickhouse-go的源代码,了解bulk write的执行过程,帮助大家梳理其具体实现。

参考资料

[1]

clickhouse-go: https://github.com/ClickHouse/clickhouse-go

[2]

sqlx: https://github.com/jmoiron/sqlx

[3]

官方样例代码: https://github.com/ClickHouse/clickhouse-go/blob/master/examples/sqlx.go#L35-L51

[4]

核心代码如下: https://github.com/clickhouse/clickhouse-go/blob/master/stmt.go#L44-L68

[5]

INSERT INTO Statement: https://clickhouse.tech/docs/en/sql-reference/statements/insert-into/

[6]

go-clickhouse-batchinsert: https://github.com/MaruHyl/go-clickhouse-batchinsert/blob/master/batch.go#L349-L354

本文分享自微信公众号 - 机器学习与系统(aimlsystem),作者:陆道峰

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-12-30

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Linux系统:Centos7下搭建ClickHouse列式存储数据库

    知了一笑
  • Clickhouse 系列 - 番外 - LSM 算法

    在本系列的第三章中介绍了 clickhouse 通过 block 和 lsm 来减少磁盘读取的数据量。严谨的逻辑应该时 clickhouse 通过 lsm 算法...

    不会飞的小鸟
  • 真是秀,ClickHouse批量导入还可以这样玩?

    用过 HBase 的同学应该都知道,当批量导入数据的时候,可以利用 Spark 这样的计算引擎,直接将数据生成 HFile 一次性导入到 HBase,既有效地分...

    Nauu
  • 技术分享 | ClickHouse GDB 调试笔记

    记录下第一次使用 GDB 调试 ClickHouse 源码的过程,这里仅仅是通过简单的调试过程了解 ClickHouse 内部的机制,有助于解决疑惑,代码小白,...

    爱可生开源社区
  • 交互式分析领域,为何ClickHouse能够杀出重围?

    导语 | 在百花齐放的交互式分析领域,ClickHouse 绝对是后起之秀,它虽然年轻,却有非常大的发展空间。本文将分享 PB 级分析型数据库 ClickHou...

    Spark学习技巧
  • 谈谈ClickHouse性能情况以及相关优化

    注意:ClickHouse并非无所不能,查询语句需要不断的调优,可能与查询条件有关,不同的查询条件表是左join还是右join也是很有讲究的

    Spark学习技巧
  • 用户行为分析-埋点实时数仓实践

    此文重点讲述埋点的数据模型、数据格式、数据实时采集、加工、存储及用户关联。关于用户行为分析的概念、意义以及埋点相关的东西此文不作赘述

    大数据真好玩
  • 基于Flink+ClickHouse构建实时数仓

    Flink和ClickHouse分别是实时计算和(近实时)OLAP领域的翘楚,也是近些年非常火爆的开源框架,很多大厂都在将两者结合使用来构建各种用途的实时平台,...

    大数据老哥
  • 打造轻量级实时数仓实践

    Flink 和 ClickHouse 分别是实时计算和(近实时)OLAP 领域的翘楚,也是近些年非常火爆的开源框架,很多大厂都在将两者结合使用来构...

    大数据老哥
  • 基于Flink+ClickHouse打造轻量级点击流实时数仓

    Flink和ClickHouse分别是实时计算和(近实时)OLAP领域的翘楚,也是近些年非常火爆的开源框架,很多大厂都在将两者结合使用来构建各种用途的实时平台,...

    大数据真好玩
  • 巧用 Flink 构建高性能 ClickHouse 实时数仓

    Apache Flink 是流式计算处理领域的领跑者。它凭借易用、高吞吐、低延迟、丰富的算子和原生状态支持等优势,多方位领先同领域的开源竞品。

    KyleMeow
  • Snuba:Sentry 新的搜索基础设施(基于 ClickHouse 之上)

    Sentry 已经在名为 Search,Tagstore(用于事件标签)和 TSDB(时间序列数据库,为大多数图形提供动力)的抽象服务接口上运行。这些服务中的每...

    为少
  • 超快!大数据分析引擎ClickHouse

    ClickHouse是一个用于联机分析(OLAP)的列式数据库管理系统(DBMS)。

    凹谷
  • 干货 | 每天十亿级数据更新,秒出查询结果,ClickHouse在携程酒店的应用

    蔡岳毅,携程酒店大数据高级研发经理,负责酒店数据智能平台研发,大数据技术创新工作。喜欢探索研究大数据的开源技术框架。

    携程技术
  • 干货 | 每天十亿级数据更新,秒出查询结果,ClickHouse在携程酒店的应用

    蔡岳毅,携程酒店大数据高级研发经理,负责酒店数据智能平台研发,大数据技术创新工作。喜欢探索研究大数据的开源技术框架。

    Fayson
  • 一文了解ClickHouse

    ClickHouse是Yandex(俄罗斯最大的搜索引擎)开源的一个用于实时数据分析的基于列存储的数据库,其处理数据的速度比传统方法快100-1000倍。Cli...

    用户1278550
  • Clickhouse简介和性能对比

    ClickHouse是一个用于联机分析(OLAP)的列式数据库管理系统(DBMS)。

    职场亮哥
  • ClickHouse介绍

    上周组内技术分享我选择了ClickHouse这个主题,对我来说,是个纯新的技术,从零开始,无论是原理理解上,还是环境搭建,碰到了很多问题,顶多是踉踉跄跄踏入了C...

    bisal
  • Sentry 监控 - Snuba 数据中台架构简介(Kafka+Clickhouse)

    Snuba 是一种在 Clickhouse 之上提供丰富数据模型以及快速摄取消费者(直接从 Kafka 获取数据)和查询优化器的服务。

    为少

扫码关注云+社区

领取腾讯云代金券