ClickHouse进阶|如何自研一款企业级高性能网关组件?

技术

picture.image

使用原生ClickHouse集群进行节点数据查询和写入时, 通常会配合使用chproxy来对查询进行负载均衡 。 但由于chproxy缺少TCP协议支持,导致性能、查询能力等受限。 这也成为困扰众多ClickHouse开发者的一大难题。

那么,究竟应该如何突破? 本文将揭秘火山引擎ByteHouse企业版自研网关组件如何解决以上问题。

picture.image

文 | 一柯、 禕暘 来自火山引擎ByteHouse团队

picture.image

ClickHouse是一款广受欢迎且应用广泛的分析型数据库。它通过列式存储和向量化处理等成熟的优化手段,配合高质量的工程化,实现了极高的性能表现。在许多业务场景下,ClickHouse展现出了非常强悍的性能表现,因此吸引了大量实际生产使用用户。

在使用原生ClickHouse集群时,用户往往通过直连节点进行数据查询或写入。然而,由于缺少中间层进行负载均衡,在某些情况下会导致分片节点上的数据写入不均衡。同时,由于客户端配置ClickHouse数据源时指定了连接的具体节点信息,查询请求也会集中于部分节点。这样一来,如果某个节点宕机,就会引发单点故障。

为了解决这些问题,ClickHouse官方文档推荐了一些第三方开源网关组件,如chproxy和KittenHouse等。其中,chproxy是应用最广泛的组件之一,具备丰富的功能。它支持灵活的用户和集群映射配置,代理HTTP类型的请求。 然而,目前开源社区还没有提供在TCP协议基础上支持的网关组件。 由于TCP协议是ClickHouse集群间默认的通信协议,也是ClickHouse客户端和许多高性能第三方驱动程序所默认选择的查询协议,缺少对TCP协议的支持使得使用上存在很大限制。

ByteHouse企业版是基于开源ClickHouse的企业级分析型数据库,支持用户交互式分析PB级别的数据,通过多种自研表引擎,灵活支持各类数据分析和应用。 为解决原生ClickHouse集群存在的一些问题,ByteHouse企业版试图提供一个高性能的网关组件。 同时,这也将进一步释放ByteHouse强大的查询引擎能力,为用户提供极致的使用体验。

本文将主要介绍火山引擎ByteHouse企业版网关组件的功能和特性、ClickHouse不同的查询协议对比、ByteHouse网关与开源chproxy的功能对比。

picture.image

/ 查询路由与负载均衡 /

ByteHouse企业版查询网关 同时支持HTTP协议和TCP协议的查询请求 ,最大程度上 兼容了各种社区语言的Driver, 例如ClickHouse GO、ClickHouse JDBC等,同时也支持诸如DataGrip、DBeaver等数据库管理工具的使用。

例:企业版查询网关架构

picture.image

监听层, 同时支持HTTP和TCP两种Protocol,接收请求。

流量控制层, 记录并限制请求的频率和并发数。

分发层, 根据配置中的集群信息和状态,负载均衡算法以及用户等信息,将请求发送至对应clickhouse节点。

健康检查器, 通过发送探针请求的方式,时刻关注每个节点的健康状态以及响应灵敏度,避免将请求转发至不健康节点。

/ 打通ByteHouse控制面元数据 /

企业版网关通过与控制面元数据的连接,使得网关用户可以直接在控制面进行创建和授权。同时,网关读取控制面集群元数据,获取ByteHouse集群节点的信息。

ByteHouse控制面支持多集群下的管控,因此对于企业版网关来说也需要支持多集群模式。与chproxy不同的是, 企业版网关可以直接读取控制面用户集群授权元数据。 对于可自动推断对应集群的用户,网关可以实现自动代理请求到对应的集群,更加灵活和便捷。

/ 监控告警 /

火山引擎ByteHouse企业版查询网关与控制面的深度集成也体现在监控告警方面。ByteHouse企业版控制面监控组件可以通过收集网关的查询指标 metrics,支持在控制面配置来自网关指标的告警规则。

例:企业版网关监控告警配置界面

picture.image

/ 其他功能 /

通过连接网关组件,ByteHouse为用户提供了更多的灵活性, 基于代理层能够实现许多原来不便于实现的能力和管控。 因此,基于服务端代理模式的ByteHouse企业版查询网关还拓展实现了其他更多功能,诸如下发指定节点和全部节点。

其中当用户使用社区ClickHouse Client连接ByteHouse企业版查询网关可支持直接通过SQL语句来切换连接的ClickHouse节点

设置网关连接指定节点 示例:


          
clickhouse client --host <HOST> --user <USER> --password <PASSWORD>                     
          

          
ByteHouse Gateway :) set custom_gw_force_ck_node='<node_ip>'
      

设置网关在全部ClickHouse节点执行SQL 示例:


          
clickhouse client --host <HOST>.bytehouse-ce.volces.com --user <USER> --password <PASSWORD>
          

          
ByteHouse Gateway :) set custom_gw_force_all_nodes=true
          
ByteHouse Gateway :) CREATE TABLE default.test(`id` Int64,`info` String COMMENT '1') ENGINE = MergeTree ORDER BY id SETTINGS index_granularity = 8192
          

          
CREATE TABLE default.test
          
(
          
    `id` Int64,
          
    `info` String COMMENT '1'
          
)
          
ENGINE = MergeTree
          
ORDER BY id
          
SETTINGS index_granularity = 8192
      

ByteHouse企业版查询网关为了避免执行查询时客户端和服务端连接中断导致无法获取查询结果,实现了异步查询来增强ByteHouse的查询能力。

对于HTTP协议基础的查询,可以通过在Header中添加X-Async-Query即可使用。

示例:


          
curl --location --request POST 'http://<gateway-address>:8123/?user=<user_name>&password=<password>&query_id=<queryID>' \
          
--header 'X-Async-Query: 1' \
          
--data-raw 'show tables FORMAT JSON;'
          

          
Query In Progress
          
    HTTP Header: X-Async-Query: running
          

          
Query Finished
          
    HTTP Header: X-Spend-Time: 100 (milliseconds)
      

picture.image

为了更好地了解ByteHouse企业版查询网关,首先需要深入探究ClickHouse所提供的查询协议和接口。了解ClickHouse服务端如何处理客户端请求,有助于我们理解如何构建高性能的查询网关。

在与ClickHouse服务端通信时,客户端使用的查询协议主要有两种:

一种基于HTTP协议的查询协议:

HTTP协议通用性较强,在任何平台或编程语言中使用HTTP Client都可以调用ClickHouse的HTTP API进行查询和数据写入。

另一种基于TCP(Native)协议的查询协议:

TCP协议则具有更少的额外开销,通过在Socket连接上自定义查询协议和优化的数据类型序列化过程,避免了HTTP七层协议带来的不必要的网络IO开销,并且原生支持session。

下面简要介绍这两种协议的不同特点。

/ ClickHouse HTTP协议的特点 /

ClickHouse服务端默认使用8123端口提供HTTP API供客户端进行查询和数据写入操作。在设计和实现上,ClickHouse为HTTP协议查询提供了很大的灵活性。

例如,Query Settings可以放置在HTTP Query Parameters中,查询SQL可以放在GET请求的query参数中或者POST请求body里,甚至分割开放置在两部分中也是允许的。

以下是一些例子:


          
$ echo 'SELECT 1' | curl 'http://localhost:8123/' --data-binary @-
          
1
          

          
$ echo 'SELECT 1' | curl 'http://localhost:8123/?query=' --data-binary @-
          
1
          

          
$ echo '1' | curl 'http://localhost:8123/?query=SELECT' --data-binary @-
          
1
      

ClickHouse本身也提供了一个可交互的前端页面,通过浏览器访问,用户可以直接在Web页面上进行ClickHouse数据库的查询操作。

picture.image

HTTP协议是无状态的,因此在使用session时需要在参数中添加session_id。通过设置session id,ClickHouse服务端能够确定请求属于哪个session。对于带有session id参数的请求,同时只能有一条SQL语句正在执行,并且不能跨节点设置session。

因此,对于查询网关来说,需要将带有session id参数的HTTP Query请求转发到同一台ClickHouse节点上,以确保session生效。

/ ClickHouse TCP协议的特点 /

ClickHouse TCP协议是ClickHouse Client和ClickHouse服务端之间默认的连接协议,也是用于ClickHouse节点间通信的协议格式。相较于HTTP协议,它具有更少的额外网络IO开销,因此效率更高。

但是,由于它是基于TCP连接底层的二进制数据流编解码,因此实现上相对复杂,需要考虑各种数据类型如何编解码以更高效地进行传输。

例如,当Client需要发送查询请求时,它会将查询语句和查询参数转换为ClickHouse TCP协议格式的字节流,并将其通过Socket连接发送到ClickHouse服务端。服务端会解析字节流并执行查询操作,最终将结果以相同的协议格式返回给Client。在这个过程中,需要考虑如何对各种数据类型进行编解码,以确保传输效率和数据准确性。

总之,ClickHouse TCP协议虽然实现上相对复杂,但由于具有更高的效率和更少的网络IO开销,因此在高负载环境下使用它可以提高系统的性能和吞吐量。

例:Client与Server基于Socket传输字节流通信

picture.image

当Client端与ClickHouse Server端建立tcp连接后,Client会发送一个Client Hello数据块给Server。这个数据块包含了Client的版本信息以及用于身份验证的信息等。Server在收到来自Client的Hello数据块后,会返回一个Server Hello数据块给Client,其中包含Server的版本信息以及一些其他的配置信息。这些信息可以用于确定Client和Server之间使用哪个协议版本以及哪些功能。

一旦Client和Server之间成功建立了连接并且进行了协议交换,Client就可以开始发送查询请求到Server了。这些查询请求可以包括SELECT、INSERT、ALTER等语句,同时也可以包含一些参数和设置来控制查询的行为。Server会解析这些请求并且返回结果给Client。在整个过程中,Client和Server之间通过tcp连接来传输数据。

例:TCP协议 Client Hello数据块格式

| 字段 | 类型 | | 描述 | | client_name | String | "Go Client" | 客户端名字 | | version_major | UVarInt | 1 | 客户端major版本 | | version_minor | UVarInt | 10 | 客户端minor版本 | | protocol_version | UVarInt | 54451 | TCP协议版本 | | database | String | "default" | database | | username | String | "default" | 账户名 | | password | String | "secret" | 密码 |

许多语言ClickHouse Driver通过支持TCP Native协议提高读写性能,例如:

● 社区 ClickHouse Native JDBC( https://github.com/housepower/ClickHouse-● Native-JDBC)

● clickhouse-go (ht

tps://github.com/ClickHouse/clickhouse-go)

● 官方JDBC Driver (https://github.com/ClickHouse/clickhouse-java)

由于ClickHouse TCP协议天然具有session状态,不同于HTTP只能在查询结束才能返回查询结果不同,TCP协议允许ClickHouse服务端将查询进度及时返回给Client。

picture.image

例:TCP协议 ClickHouse服务端返回给Client的几种不同类型的数据块

| value | name | 描述 | | 0 | Hello | Server响应Client Hello | | 1 | Data | 数据块 | | 2 | Exception | 异常 | | 3 | Progress | 查询进度 |

/ 探索ClickHouse批量写入 /

由于ClickHouse引擎底层依赖LSM数据结构进行数据存储,因此它具有独特的引擎特点。为了充分利用这些特点,ClickHouse推荐以批量的形式进行数据写入,以提高写入的效率并避免产生大量细碎的part文件。 因此,在实现上,HTTP和TCP协议都支持批量插入,但在底层实现上存在一些区别。

HTTP协议支持将批量写入的行数据以各种不同的格式放在HTTP Body中,并可以通过压缩来提高数据写入的效率。

例:


        
            

          $ echo -ne '10\n11\n12\n' | curl 'http://localhost:8123/?query=INSERT%20INTO%20t%20FORMAT%20TabSeparated' --data-binary @-
        
      

而TCP协议支持发送多个数据块来避免重复提供写入效率,从图中可以看出Client发送给Server的data类型数据块可以很大(受max_block_size参数约束,默认值为65505)

例:TCP协议发起batch insert过程

picture.image

例:TCP协议 Data数据块格式

| 字段 | 类型 | 描述 | | info | BlockInfo | Encoded block info | | columns | UVarInt | Columns count | | rows | UVarInt | Rows count | | columns | []Column | Columns with data |

例:TCP协议 Column结构体格式

| 字段 | 类型 | 描述 | | name | string | 字段名 | | type | string | 字段类型 | | data | byte数组 | 整列数据 |

就TCP协议而言,在进行batch insert时,插入的数据以整列的形式进行传输。这种方式不仅有利于数据在传输过程中得到更高效的压缩,而且由于自定义了数据类型的序列化机制,所以在读写过程中不需要插入分隔符,直接读取或写入定长的字节数组即可,从而大大提高了IO效率。

相比之下,HTTP协议下的batch insert需要通过FORMAT来分割行数据进行写入。同时,数据以整行传输不利于压缩,这也是HTTP协议相较于TCP协议下的batch insert效率较低的一个原因。

picture.image

/ 功能比较 /

火山引擎ByteHouse企业版查询网关与 chproxy 差异对比:

| 能力 | ByteHouse 网关 | chproxy | | 用户请求代理到集群 | 支持 | 支持 | | 支持协议 | HTTP,TCP | HTTP | | 负载均衡策略 | round robin | least loadedround robin | | 健康检查 | 支持 | 支持 | | 下发模式 | 任意节点(根据负载均衡策略) 指定节点 全部节点 | 任意节点(根据负载均衡策略) | | 告警 | 支持(控制面集成) | 不支持 | | 监控 | 支持 (控制面健康度集成) | 支持 (Prometheus Metrics) | | 多集群 | 支持 (自动推断) | 支持 (配置文件设置) |

/ 性能比较 /

在查询性能上,由于用户可以通过使用ClickHouse TCP协议连接ByteHouse网关,因此拥有比chproxy更快的性能表现。

特别是在应对批量数据写入batch insert的场景下,使用ByteHouse网关以TCP协议连接有着更高的效率。

👉参考ClickHouse Native JDBC驱动做了对HTTP、TCP协议性能测试基础测试

https://github.com/housepower/ClickHouse-Native-JDBC/blob/master/docs/dev/benchmark.md

/ 使用体验比较 /

开箱即用 免二次配置

对于用户使用体验来说,ByteHouse网关由于和ByteHouse企业版深度集成,做到了开箱即用。避免了chproxy需要手工在机器上维护yaml配置文件的繁琐。同时由于有了与控制面集群元数据的打通,因此集群运维操作例如节点替换、水平扩容操作,不需要更新网关配置。

用户模型对齐ClickHouse

由于chproxy定义了自己的网关用户与实际ClickHouse用户不一致,因此无法复用ClickHouse的RBAC功能。而ByteHouse网关用户元数据与控制面一致,在控制面创建和授权的用户可以直接连接ByteHouse网关进行连接和查询。

切换成本低

对比chproxy,对于原来使用ClickHouse Go、ClickHouse Native JDBC等组件连接ClickHouse的用户来说,可以直接连接ByteHouse网关来无缝切换到ByteHouse,避免了因为需要切换协议(TCP -> HTTP)而改变客户端Driver的问题。

监控告警集成

ByteHouse网关与控制面集成了监控指标、告警规则,因此使用ByteHouse网关更易于监控,并配置相应告警。

picture.image

本文介绍了ByteHouse企业版查询网关组件的功能和特性,并探究了ClickHouse查询协议,通过解析ClickHouse查询协议和数据类型,我们构建了能够同时代理ClickHouse两种不同查询协议的网关proxy组件,以进一步提升整个ByteHouse的使用体验和可用性。并对比了ByteHouse企业版查询网关与社区chproxy网关的差别。

ByteHouse企业版查询网关将持续迭代,不断完善功能,提升查询体验。

在安全性方面, 网关将支持数据加密来提高客户端在公网连接时的数据安全性,避免在引擎节点上进行TLS卸载,同时提升查询性能。查询网关将配合ByteHouse企业版的水平和垂直扩缩容能力,在网关层进一步优化,实现对Client无感知的运维动作。当控制面进行运维动作时,网关可以分流请求避免请求落到指定节点上。网关像sidecar一样工作,使控制面的运维操作更加智能。

在监控方面, 网关将通过接入ByteHouse Parser在网关层解析SQL语句,实现对集群、库、表,甚至字段的SQL执行统计,并深度整合到控制面库表信息和健康度功能上。

为了助力企业抓稳数字化发展机遇,加速企业数智能力升级, 自2023年2月1日开始,火山引擎ByteHouse特别推出为期一年的企业级特惠活动。

企业通过本次活动购买ByteHouse服务,包年1年可享8.3折,包年2年可享7折,包年3年可享5折。除基础优惠之外,企业包年购买后,还可获得大量额外资源免费赠送,买二送一, 买三送二,买五送三(赠送资源与包年使用期限一致), 且以上两项优惠可叠加享受!

picture.image

产品介绍

火山引擎ByteHouse

统一的大数据分析平台。目前提供企业版和云数仓两种版本,企业版是基于开源的企业级分析型数据库,支持用户交互式分析PB级别数据,通过多种自研表引擎,灵活支持各类数据分析和应用;云数仓版作为云原生的数据分析平台,实现统一的离线和实时数据分析,并通过弹性扩展的计算层和分布式存储层,有效降低 企业大数据分析。后台回复数字“6”了解产品

picture.image

picture.image

picture.image

picture.image

picture.image

42
0
0
0
相关产品
评论
未登录
看完啦,登录分享一下感受吧~
暂无评论