ClickHouse使用介绍和部署笔记
一、简介
ClickHouse 是一种用于在线分析处理 (OLAP) 的高性能、面向列的 SQL 数据库管理系统 (DBMS)。它以其快速的查询性能和高效的数据压缩而闻名,并且能够处理 PB 级别的数据。
1.什么是 OLAP
OLAP 场景需要在大型数据集之上实时响应复杂的分析查询,具有以下特征(该特征绝大多数符合 调用日志落地 and 查询的要求):
- 绝大多数请求都是为了查询,而不是插入;
- 数据以相当大的批次( > 1000 行)更新,而不是一次更新一行,或者根本不更新;
- 数据已添加到数据库但极少需要去改动他;
- 读取时,从数据库中“取出”相当多的行,但只取出一小部分列;
- 表是“宽表”的,也就是说,它们可以包含大量的列;
- 收到的请求相对较少(通常每台服务器每秒不超过一百个);
- 执行简单查询时,50ms 左右的延迟是可以接受的;
- 列中的值非常小 - 数字和小字符串(例如,每个 URL 60 字节);
- 处理单个请求时需要高吞吐量(每台服务器每秒最多数十亿行);
- 数据一致性要求低;
- 查询中有一张大表,查询中的所有其他表都很小;
- 查询结果明显小于原始数据——即数据被过滤或聚合;执行结果存放在一台服务器的RAM中。
2.为什么 ClickHouse 会这么快
架构
ClickHouse 最初是作为一个原型构建的,它只完成一项任务:尽可能快地过滤和聚合数据。这就是构建典型分析报告所需要做的事情,也是典型 GROUP BY 查询所做的事情。
- 面向列的存储: 源数据通常包含数百甚至数千列,而报表只能使用其中的几个列。系统需要避免读取不必要的列,以避免昂贵的磁盘读取操作。
- 索引: 内存驻留的 ClickHouse 数据结构允许仅读取必要的列,以及这些列的必要的行范围。
- 数据压缩: 将同一列的不同值存储在一起通常会带来更好的压缩比(与面向行的系统相比),因为在实际数据中,列的相邻行通常具有相同或没有那么多不同的值。除了通用压缩之外,ClickHouse 还支持专用编解码器,可以使数据更加紧凑。
- 向量化查询执行:ClickHouse 不仅按列存储数据,还按列处理数据。这会带来更好的 CPU 缓存利用率并允许使用 SIMD CPU 指令。
- 可扩展性:ClickHouse 可以利用所有可用的 CPU 核心和磁盘来执行单个查询。不仅在单个服务器上,而且在集群的所有 CPU 核心和磁盘上。
许多其他数据库管理系统也使用类似的技术。真正让 ClickHouse 脱颖而出的是对底层细节的关注。大多数编程语言都提供了最常见算法和数据结构的实现,但它们往往过于通用而无效。每个任务都可以被视为具有各种特征的景观,而不仅仅是随机实施。哈希表是实现的关键数据结构,ClickHouse 会为每个特定查询自动选择 30 多个变体 GROUP BY 之一( github源码连接 )。
3.Clickhouse 的特点
- 没有额外的数据与值一起存储。ClickHouse 是一个数据库管理系统,而不是一个单一的数据库。它允许在运行时创建表和数据库、加载数据和运行查询,而无需重新配置和重新启动服务器。
- 通过主键对数据进行物理排序,可以在不到几十毫秒的时间内以低延迟提取基于特定值或值范围的数据。
- 大型查询自然会并行化,利用当前服务器上可用的所有必要资源。
- 在 ClickHouse 中,数据可以驻留在不同的分片上。每个分片可以是一组用于容错的副本。所有分片都用于并行运行查询,对用户透明。
- ClickHouse 支持基于 SQL 的声明式查询语言,该语言在许多情况下与 ANSI SQL 标准相同。
- 支持的查询包括 GROUP BY、ORDER BY、FROM 中的子查询、JOIN 子句、IN 运算符、窗口函数和标量子查询。
- 通过主键对数据进行物理排序,可以在不到几十毫秒的时间内以低延迟基于特定值或值范围提取数据。
- ClickHouse 支持带有主键的表。为了快速对主键范围进行查询,使用合并树对数据进行增量排序。因此,数据可以不断地添加到表中。摄取新数据时不会采取任何锁定。
-
Clickhouse 优点:
- 高性能查询:ClickHouse 的列式存储和并行查询使其非常适合大规模数据分析和聚合查询。
- 可扩展性:ClickHouse 可以轻松扩展到数十个节点,以处理大规模数据。
- 数据压缩:ClickHouse 使用多种压缩算法,可以显著减少存储需求。
- 低维护成本:ClickHouse 的自动分区和数据复制功能降低了管理成本。
- 灵活的部署选项: 支持原生服务器部署和 Docker 容器部署。
-
Clickhouse 缺点:
- 不适合 OLTP:ClickHouse 专注于 OLAP 工作负载,因此不适合事务处理。
- 复杂的配置: 在处理大规模数据时,需要谨慎配置 ClickHouse 以获得最佳性能。
二、版本选择
ClickHouse 的版本命名规则通常遵循主版本号、次版本号和修订号的规范。主版本号表示主要的功能更新或重大变化,次版本号表示较小的更新或修复,而修订号表示累积的修复或补丁版本。版本号的一般格式是 X.Y.Z,其中 X 是主版本号,Y 是次版本号,Z 是修订号。
ClickHouse 的版本遵循 Year.Major.Minor.patchset 的风格,每年发布两次 LTS 版本,每个 LTS 版本会提供一年的支持,而普通的 Stable 版本一般只会提供 4 个月左右的支持,而且支持的频率也要低一些。由于 ClickHouse 发布版本的节奏非常之快,几乎每月一个大版本,大版本都有 Stable 版本。如果是企业用户,还是希望能提供尽可能稳定的服务,所以版本的稳定性至关重要。为了解决这个问题,ClickHouse 推出了 LTS 版本。
LTS,即 Long Term Support。大家应该对这个并不陌生,很多开源软件都支持 LTS 版本,比如 NodeJS。但是每家软件对于 LTS 发布的规则是不一样的。
ClickHouse LTS 版本的发布规则是:
- 每半年发布一次 LTS 大版本;
- 在上一个 LTS 半年后,选择当时至少被一个大客户使用过的 stable 版本作为新的 LTS 版本。
LTS 版本的小版本升级只会包含 Bug fix 和 Backports,所以稳定性会大大提升。
目前我们本地使用的是23.8.4.69,有计划升级到24.3.2.23,以上两个版本安装方式都是一样的,建议安装 Release v24.3.2.23-lts
三、表引擎介绍
-
特征
ClickHouse 表引擎决定了数据的一些基本特征:
- 数据如何存储、在何处存储、将其写入何处以及从何处读取。
- 支持哪些查询以及如何支持。
- 是否支持并发数据访问。
- 是否使用索引。
- 是否可以执行多线程请求。
- 数据是如何同步的。
-
MergeTree 家族和 Distributed
在大多数场景中, 我们所使用的引擎主要是 MergeTree 家族。这个家族的引擎 MergeTree 和其他引擎(*MergeTree)是最常用、最健壮的 ClickHouse 表引擎。该系列中的引擎 MergeTree 设计用于将大量数据插入表中。数据被快速地逐部分写入表中,然后应用规则在后台合并各部分。这种方法比插入时不断重写存储中的数据要高效得多。
主要特点:
- 存储按主键排序的数据。
- 如果指定了分区键,则可以使用分区。
- 支持数据复制(ReplicatedMergeTree 引擎)。
另一个我们主要使用的引擎则是 Distributed,分布式引擎。它本身不存储数据,可认为就是一张 View,如果写入,会把请求丢到集群里的节点(有算法控制),如果查询,会帮你做查询转发再聚合返回
-
使用建议
我们在项目中使用的是基于 Distributed+ReplicatedMergeTree+keeper(zookeeper 或者 clickhouse 自己建议的 clickhouse-keeper)的表复制,具体如何使用请看 七、使用案例
四、单机部署
-
确认系统要求和准备工作
- 确保部署 ClickHouse 的服务器满足 ClickHouse 的系统要求,包括操作系统、CPU、内存和磁盘空间等。
- 从 github 上或者 研发云 上下载对应版本的安装文件到服务器上,找个目录存放下来(一般我存放于/data/clickhouse-install-package 下)
- 配置网络和防火墙,确保服务器的防火墙允许来自其他主机的 ClickHouse 连接请求。如果有防火墙或网络安全组,确保允许 ClickHouse 的 TCP 端口(默认为 9000)和 JDBC 端口(默认为 8123)的入站连接。
- 安装,我们使用的是 rpm 包,可以使用 rpm 安装命令:
rpm -ivh clickhouse-**-x86.64.rpm
需要先安装 common-static,其次 server,最后 client。
-
修改主配置文件
默认情况下,ClickHouse 的 server 配置文件位于/etc/clickhouse-server/config.xml。可以根据需要修改此文件以配置 ClickHouse 的行为。一般而言,单体部署我们需要关注以下常用配置项:
-
- listen_host:指定 ClickHouse 服务器监听的主机地址。默认为 localhost,如果要从其他主机访问,需要修改为服务器的 IP 地址或 0.0.0.0。
- tcp_port,http_port 等:主要指几个协议所需要使用的 port 端口号,如下所示(都是默认的端口号,可以根据自己的需求修改):
<http_port>8123</http_port> <tcp_port>9000</tcp_port> <mysql_port>9004</mysql_port> <postgresql_port>9005</postgresql_port>
-
- path:指定 ClickHouse 默认的数据目录的路径。默认为/var/lib/clickhouse,需要修改为自己制定的目录,若有多个磁盘挂载在多个目录下,则需要制定任意一个目录为默认存储目录,其余的在下面的 storage_configuration 去配置。
- storage_configuration:
<storage_configuration>
<disks>
<default>
<keep_free_space_bytes>107374182400</keep_free_space_bytes>
</default>
<data02>
<path>/data02/clickhousedata/</path>
<keep_free_space_bytes>107374182400</keep_free_space_bytes>
</data02>
...
</disks>
...
</storage_configuration>
-
- data0N: 磁盘名称。所有磁盘的名称必须不同。
- path:服务器存储数据(data 和 shadow 文件夹)的路径,应以“/”结尾,需要确保每个目录都拥有权限。
- keep_free_space_bytes:要保留的可用磁盘空间量。
磁盘定义的顺序并不重要。
-
- new_storage_only:策略名称,必须是唯一的
- XX_volume:卷名,必须是唯一的
- disk:卷内的磁盘,可以按照要求服务器实际情况组成
- max_data_part_size_bytes: 可存储在任何卷磁盘上的部分的最大大小。如果合并部分的大小估计大于则 max_data_part_size_bytes 该部分将被写入下一卷。基本上,此功能允许将新/小部件保留在热(SSD)卷上,并在它们达到大尺寸时将它们移动到冷(HDD)卷。如果您的策略只有一个卷,请勿使用此设置。
- move_factor: 当可用空间量低于此系数时,数据会自动开始移动到下一个卷(如果有)(默认情况下为 0.1)。ClickHouse 按照大小从大到小(降序)对现有的 Part 进行排序,并选择总大小足以满足条件的 Part move_factor。如果所有部件的总大小不足,则所有部件将被移动。
- perform_ttl_move_on_insert:禁用数据部分 INSERT 上的 TTL 移动。默认情况下(如果启用),如果我们插入 TTL 移动规则已过期的数据部分,它将立即转到移动规则中声明的卷/磁盘。如果目标卷/磁盘很慢(例如 S3),这会显着减慢插入速度。如果禁用,则已过期的数据部分将写入默认卷,然后立即移动到 TTL 卷。
- load_balancing:磁盘平衡策略,round_robin 或 least_used。
-
修改其他配置文件
ClickHouse 还提供了其他配置文件,如 users.xml、access.xml 等,用于配置用户和权限等。根据需要修改这些文件。下面主要说 users.xml
-
- 默认情况下,ClickHouse 安装时会创建一个名为 default 的用户,并使用空密码。建议为该用户设置安全密码。
- 根据实际需求,您可能需要创建新的用户账户,并为其分配不同的权限。例如,您可以为不同的应用程序或团队创建独立的用户账户。
- 通过
<access_management>
标签可以指定用户是否有权限进行管理操作,如创建表、删除表等。 - 通过 <networks> 标签可以指定允许访问的网络地址或 IP 范围。
-
启动 Clickhouse 服务器
<storage_configuration>
...
<policies>
<new_storage_only> <!-- 策略名(唯一) -->
<volumes>
<hot_volume> <!-- 卷名(唯一) -->
<disk>default</disk>
<!-- 可以存储在该磁盘上的最大大小,如果该大小太大,则写入下一个卷 -->
<max_data_part_size_bytes>10737418240</max_data_part_size_bytes>
<!-- 可以存储在该磁盘上的最大大小比例,如果该大小太大,则写入下一个卷 -->
<!-- <max_data_part_size_ratio></max_data_part_size_ratio> -->
<!-- 是否禁止 insert 超过 ttl 时间的数据进入该卷,如果禁止,这个数据会直接插入备份卷,
而不是插入到这个卷后由 clickhouse 定时任务触发移动操作 -->
<perform_ttl_move_on_insert>false</perform_ttl_move_on_insert>
<!-- 是否禁止这个卷自动合并数据块 -->
<prefer_not_to_merge>false</prefer_not_to_merge>
<!-- 磁盘的平衡策¥,轮询平衡,如果只有一个磁盘则没有意义 = = -->
<load_balancing>round_robin</load_balancing>
</hot_volume>
<cold_volume>
<disk>data02</disk>
......
<load_balancing>round_robin</load_balancing>
</cold_volume>
</volumes>
<!-- 该策略下的移动因子,一般而言 有两种因素会导致数据搬迁到另一个卷中
1.ttl 过期策略移动,该移动由 clickhouse 后台线程检查执行移动
2.磁盘空间不足后移动,该移动在本地磁盘可用大小 小于 move_factor * disk_size 则触发移动 -->
<move_factor>0.3</move_factor>
</new_storage_only>
</policies>
</storage_configuration>
service clickhouse-server start
-
测试连接
clickhouse-client --host <HOSTNAME> \
--port 9000 \
--user <USERNAME> \
--password <PASSWORD>
:) SELECT 1
SELECT 1
┌─1─┐
│ 1 │
└───┘
1 rows in set. Elapsed: 0.003 sec.
:)
恭喜,系统正常运行!目前已经完成了 Clickhouse 的单体部署
五、集群部署
1.ClickHouse 集群的概念
ClickHouse 不同于 Elasticsearch、HDFS 这类主从架构的分布式系统,它采用多主(无中心)架构,集群中的每个节点角色对等,每个节点存储数据的一部分。多主架构天然规避了单点故障的问题,非常适合用于多数据中心、异地多活的场景。可以通过 Distributed 表进行分布式查询,这种分布式存储模式有助于提高数据的可用性和容错性,同时也能够提供更高的数据处理能力。Distributed(分布式表)可以将一个大型查询分解为多个子查询,并在集群中的多个节点上并行执行这些子查询,从而加速查询的执行速度。ClickHouse 的数据副本一般通过 ReplicatedMergeTree 复制表系列引擎实现,副本之间借助 ZooKeeper 实现数据的一致性。此外也可通过分布式表负责同时进行分片和副本的数据写入工作。
2.ClickHouse 集群架构图
下图是建议使用的 ClickHouse 集群架构,由于 ClickHouse 集群的副本间需要通过 keeper 来实现数据一致性,所以需要额外部署一个 zookeeper 集群或者 Clickhouse-keeper 集群。所以综合来说,最少需要六个节点,做两个 shard,每个 shard,提供三个节点做数据副本,保证高可用
3.集群部署步骤
集群中节点的部署与单体节点部署步骤基本一致,只是在配置文件编写中需要额外修改一些配置,配置 ClickHouse 集群涉及修改多个配置文件以确保各个节点的正确通信和协调。
-
-
补充集群节点通信配置信息
-
在 config.xml 中查询以下标签,根据实际项目需求配置具体的地址信息
-
-
- interserver_http_host:指定集群节点之间进行 HTTP 通信的主机地址。默认为 localhost。
- interserver_http_port:指定集群节点之间进行 HTTP 通信的端口号。默认为 9009
- distributed_ddl:如果启用了分布式 DDL,需要配置相应的参数。
- distributed_product_mode:指定集群的部署模式,如分布式、复制等。
-
-
-
补充<zookeeper>配置信息
-
在 config.xml 中查询配置<zookeeper>信息。
<zookeeper>
<node>
<host>chnode1.domain.com</host>
<port>9181</port>
</node>
<node>
<host>chnode2.domain.com</host>
<port>9181</port>
</node>
<node>
<host>chnode3.domain.com</host>
<port>9181</port>
</node>
</zookeeper>
范围 |
描述 |
例子 |
node |
Keeper 连接的节点列表 |
每个服务器的设置条目 |
host |
每个 ClickHouse keeper 节点的主机名、IP 或 FQDN |
chnode1.domain.com |
port |
ClickHouse Keeper 客户端端口 |
9181 |
-
-
配置集群间通信用户信息和宏
-
在 users.xml,创建用于集群使用的用户和权限,确保访问安全。
在 config.xml 中配置宏,以降低 DDL 的复杂性
<macros>
<shard>01</shard>
<replica>01</replica>
<cluster>cluster_1S_2R</cluster>
</macros>
-
-
补充<clickhouse_remote_servers>配置信息
-
在 config.xml 中查询 clickhouse_remote_servers 配置,根据实际地址配置<clickhouse_remote_servers>标签配置,也可以将该配置独立出来。可以使用 ClickHouse 的 include 功能。include 允许我们将一个配置文件中的内容包含到另一个配置文件中,从而实现配置的模块化和分离。比如在 config.d 文件夹下创建一个新的配置文件,例如 metrika.xml,在里面填写<clickhouse_remote_servers>相关配置,在 config.xml 中引入
<include from="/etc/clickhouse-server/config.d/metrika.xml" />
这样,当 ClickHouse 服务器启动时,它将自动包含 metrika.xml 中定义的远程服务器配置。
以下是 clickhouse_remote_servers 的配置和说明
<clickhouse_remote_servers>
<cluster_2shards_1replicas>
<shard>
<internal_replication>true</internal_replication>
<replica>
<host>10.130.236.XX</host>
<port>9000</port>
<user>default</user>
<password>XXX</password>
</replica>
<replica>
<host>10.130.236.XX</host>
<port>9000</port>
<user>default</user>
<password>XXX</password>
</replica>...
</shard>
<shard>
<internal_replication>true</internal_replication>
<replica>
<host>10.130.236.XX</host>
<port>9000</port>
<user>default</user>
<password>XXX</password>
</replica>
<replica>
<host>10.130.236.XX</host>
<port>9000</port>
<user>default</user>
<password>XXX</password>
</replica> ...
</shard>
</cluster_2shards_1replicas>
</clickhouse_remote_servers>
说明:
范围 |
描述 |
例子 |
shard |
集群定义上的副本列表 |
每个分片的副本列表 |
replica |
每个副本的设置列表 |
每个副本的设置条目 |
host |
将托管副本分片的服务器的主机名、IP 或 FQDN |
10.130.236.XX |
port |
用于使用本机 tcp 协议进行通信的端口 |
9000 |
user |
将用于对集群实例进行身份验证的用户名 |
default |
password |
用户定义的密码以允许连接到集群实例 |
XXX |
e.clickhouse-keeper 配置说明
zookeeper 的配置在这里不做赘述,如果使用 Clickhouse-keeper,可以参照以下的配置和说明:
<keeper_server>
<tcp_port>9181</tcp_port>
<server_id>1</server_id>
<log_storage_path>/var/lib/clickhouse/coordination/log</log_storage_path>
<snapshot_storage_path>/var/lib/clickhouse/coordination/snapshots</snapshot_storage_path>
<coordination_settings>
<operation_timeout_ms>10000</operation_timeout_ms>
<session_timeout_ms>30000</session_timeout_ms>
<raft_logs_level>warning</raft_logs_level>
</coordination_settings>
<raft_configuration>
<server>
<id>1</id>
<hostname>chnode1.domain.com</hostname>
<port>9234</port>
</server>
<server>
<id>2</id>
<hostname>chnode2.domain.com</hostname>
<port>9234</port>
</server>
<server>
<id>3</id>
<hostname>chnode3.domain.com</hostname>
<port>9234</port>
</server>
</raft_configuration>
</keeper_server>
范围 |
描述 |
例子 |
tcp_port |
keeper 客户端使用的端口 |
9181默认相当于zookeeper中的2181 |
server_id |
raft 配置中使用的每个 CLickHouse Keeper 服务器的标识符 |
1 |
coordination_settings |
超时等参数部分 |
超时:10000,日志级别:trace |
server |
服务器参与的定义 |
每个服务器定义的列表 |
raft_configuration |
keeper集群中每台服务器的设置 |
服务器和每个设置 |
id |
用于 keeper 服务的服务器的数字 ID |
1 |
hostname |
keeper 集群中每台服务器的主机名、IP 或 FQDN |
chnode1.domain.com |
port |
用于侦听服务器间 Keeper 连接的端口 |
9234 |
4.启动和验证
以上配置按照需求配置完成后,就可以启动 ClickHouse 集群了,启动和测试服务器是否正常与单机一致。
service clickhouse-server start
clickhouse-client --host <HOSTNAME> \
--port 9000 \
--user <USERNAME> \
--password <PASSWORD>
:) SELECT 1
SELECT 1
┌─1─┐
│ 1 │
└───┘
1 rows in set. Elapsed: 0.003 sec.
验证集群是否正常需要额外验证(结果仅限演示,实际 cluster 根据自己配置的来看),
:)SELECT * from system.clusters;
SELECT *
FROM system.clusters
Query id: 4f1c8711-1fb5-448a-b2cc-9a52525149e3
┌─cluster─────────────────────────────────────────┬─shard_num─┬─shard_weight─┬─replica_num─┬─host_name─┬─host_address─┬─port─┬─is_local─┬─user────┬─default_database─┬─errors_count─┬─slowdowns_count─┬─estimated_recovery_time─┐
│ test_cluster_one_shard_three_replicas_localhost │ 1 │ 1 │ 1 │ 127.0.0.1 │ 127.0.0.1 │ 9000 │ 1 │ default │ │ 0 │ 0 │ 0 │
│ test_cluster_one_shard_three_replicas_localhost │ 1 │ 1 │ 2 │ 127.0.0.2 │ 127.0.0.2 │ 9000 │ 0 │ default │ │ 0 │ 0 │ 0 │
│ test_cluster_one_shard_three_replicas_localhost │ 1 │ 1 │ 3 │ 127.0.0.3 │ 127.0.0.3 │ 9000 │ 0 │ default │ │ 0 │ 0 │ 0 │
│ test_cluster_two_shards │ 1 │ 1 │ 1 │ 127.0.0.1 │ 127.0.0.1 │ 9000 │ 1 │ default │ │ 0 │ 0 │ 0 │
│ test_cluster_two_shards │ 2 │ 1 │ 1 │ 127.0.0.2 │ 127.0.0.2 │ 9000 │ 0 │ default │ │ 0 │ 0 │ 0 │
│ test_cluster_two_shards_internal_replication │ 1 │ 1 │ 1 │ 127.0.0.1 │ 127.0.0.1 │ 9000 │ 1 │ default │ │ 0 │ 0 │ 0 │
│ test_cluster_two_shards_internal_replication │ 2 │ 1 │ 1 │ 127.0.0.2 │ 127.0.0.2 │ 9000 │ 0 │ default │ │ 0 │ 0 │ 0 │
│ test_cluster_two_shards_localhost │ 1 │ 1 │ 1 │ localhost │ ::1 │ 9000 │ 1 │ default │ │ 0 │ 0 │ 0 │
│ test_cluster_two_shards_localhost │ 2 │ 1 │ 1 │ localhost │ ::1 │ 9000 │ 1 │ default │ │ 0 │ 0 │ 0 │
│ test_shard_localhost │ 1 │ 1 │ 1 │ localhost │ ::1 │ 9000 │ 1 │ default │ │ 0 │ 0 │ 0 │
│ test_shard_localhost_secure │ 1 │ 1 │ 1 │ localhost │ ::1 │ 9440 │ 0 │ default │ │ 0 │ 0 │ 0 │
│ test_unavailable_shard │ 1 │ 1 │ 1 │ localhost │ ::1 │ 9000 │ 1 │ default │ │ 0 │ 0 │ 0 │
│ test_unavailable_shard │ 2 │ 1 │ 1 │ localhost │ ::1 │ 1 │ 0 │ default │ │ 0 │ 0 │ 0 │
└─────────────────────────────────────────────────┴───────────┴──────────────┴─────────────┴──────────┴──────────────┴──────┴──────────┴─────────┴──────────────────┴──────────────┴─────────────────┴─────────────────────────┘
13 rows in set. Elapsed: 0.002 sec.
六、使用案例
1.引擎介绍
首先需要介绍下 Clickhouse 的表引擎,CK 里面有非常多的引擎,我们这里主要介绍推荐使用的是 3 个,其余的有兴趣可以去 CK官网 看其余引擎的介绍。
- MergeTree:这个家族的引擎MergeTree和其他引擎(*MergeTree)是最常用、最健壮的ClickHouse表引擎。该系列中的引擎MergeTree设计用于将大量数据插入表中。数据被快速地逐部分写入表中,然后应用规则在后台合并各部分。这种方法比插入时不断重写存储中的数据要高效得多。
主要特点:
-
- 存储按照主键排序的数据,推荐主键使用时间(DateTime)
- 可以指定分区键,支持带有分区的操作
- 可以使用ReplicatedMergeTree做数据的复制,提供容灾备份
- 支持数据采样(目前项目中暂未使用)
- ReplicatedMergeTree:基于MergeTree,同时引入ZK,做了复制,具体使用方法下面会重点说
- Distributed:分布式引擎,本身不存储数据,可认为就是一张View,如果写入,会把请求丢到集群里的节点(有算法控制),如果查询,会帮你做查询转发再聚合返回
2.实战架构
在我们项目中,如果是小规模数据(大约千万行以下),可以直接使用MergeTree,如果是大规模数据(约数十亿),建议使用ReplicatedMergeTree+Distributed的分布式架构,目前集团内部的调用日志就是使用的该架构,下图是目前集团使用的Clickhouse项目实战架构图
1).架构解析
如开头所说,clickhouse是个多主(无中心)架构,所以集群中副本表与表之间无主次之分,该架构主要利用的是Distributed表的特性+MergeTree表的特性,分布式表不存储数据,数据来自本地表,将分布式表的数据分为3个shard,每台节点存储三分之一的数据,用户查询的时候是从分布式表所在的节点聚合三个shard的查询结果,然后返回用户,写入数据可以写入分布式表,当然这样的写入方式问题很多,一般是禁止写入分布式表的,那么选择写入本地表的化,需要将数据轮询,随机或者其他方式,将数据分散写入三个shard中。
2).优势
- 极大的分散了数据的查询压力,拓展了查询范围上限。由于是将查询通过Distributed表分散到每个shard做查询,在存入合理的情况下,每个shard只需要承担自己所存储的那部分数据的查询压力。
- 架构简单,并且使用这个部署方式,也可以在节点中创建单机表,针对我们的项目中所需要的业务选择单机使用的引擎,例如MergeTree,这样就是单机使用,一种部署方式多种使用途径
- 数据安全,每个shard中都有2+个数据副本,做了数据冗余,减少了数据丢失的隐患。另外数据的查询并行度没有改变,但是因为副本的存在,shard查询的时候节点的选择性多了,即使其中一个副本较忙,或者挂了,也不影响集群的查询服务。
3).需要注意的点
- 写本地表,读分布式表,这样可以极大的降低因Keeper性能瓶颈所造成的数据查询延缓
- 结合业务及数据的特性,及部署服务器的性能,合理选择分布式表的建表节点,可以每个节点都创建一个Distributed表,也可以单独在某个节点建一个Distributed
- 建立标准的Keeper目录使用规范,防止目录冲突,副本的复制依赖Keeper集群,在集群部署中推荐使用宏来规定当前数据库的信息,目前集团使用的引擎提统一是ReplicatedMergeTree('/clickhouse/tables/{shard}/{database}/{table}', '{replica}')
- CK默认的查询并发是100,当并发达到一定的程度,会存在一个排队的现象,介于多shard,多副本的情况,做查询的负载均衡能很好的提高查询的并发有限的问题
- 尽量做1000条以上批量的写入,避免逐行insert或小批量的insert,update,delete操作,因为ClickHouse底层会不断的做异步的数据合并,会影响查询性能,这个在做实时数据写入的时候要尽量避开;
- 如果是写Distributed表,建议设置<internal_replication>true</internal_replication>,即只写一个shard里面的一个副本,这样不用同一条数据每个数据副本都写一遍,而是只写一个副本,另外的副本等待同步。
- 建议关闭swap内存交换。
3.引擎使用
1).MergeTree使用
创建表语句:
CREATE TABLE [IF NOT EXISTS] [db.]table_name [ON CLUSTER cluster]
(
name1 [type1] [[NOT] NULL] [DEFAULT|MATERIALIZED|ALIAS|EPHEMERAL expr1] [COMMENT ...] [CODEC(codec1)] [STATISTIC(stat1)] [TTL expr1] [PRIMARY KEY] [SETTINGS (name = value, ...)],
name2 [type2] [[NOT] NULL] [DEFAULT|MATERIALIZED|ALIAS|EPHEMERAL expr2] [COMMENT ...] [CODEC(codec2)] [STATISTIC(stat2)] [TTL expr2] [PRIMARY KEY] [SETTINGS (name = value, ...)],
) ENGINE = MergeTree()
ORDER BY expr
[PARTITION BY expr]
[PRIMARY KEY expr]
[SAMPLE BY expr]
[TTL expr
[DELETE|TO DISK 'xxx'|TO VOLUME 'xxx' [, ...] ]
[WHERE conditions]
[GROUP BY key_expr [SET v1 = aggr_func(v1) [, v2 = aggr_func(v2) ...]] ] ]
[SETTINGS name = value, ...]
COMMENT 'comment for table'
参数说明:
- ENGINE:引擎的名称,MergeTree无参数
- ORDER BY:排序键,可以是列名或者是任意表达式,如果未指定PRIMARY KEY,则该数据会被指定为主键。如果不需要排序键,则可以使用ORDER BY tuple(),在这种情况下,ClickHouse按照插入的顺序存储数据。
- PARTITION BY:分区键,选填,官网建议如果需要分区,则最好按照月级别及以上的颗粒度分区,分区并不会加速查询。
- PRIMARY KEY:主键,选填,如果不填,则与ORDER BY一致,选择第一个为主键索引,后面的为稀疏索引,主键索引确定搜索的颗粒度(类似于叶子节点),稀疏索引可以提高查询效率
- SAMPLE BY:采样表达式,选填,项目中没有使用到。
- TTL:指定行的存储期限,并且可以定义磁盘和卷之间移动逻辑的规则,选填,表达式必须是Date或者DateTime的列(或者可以转化成该格式的列)。如:TTL date + INTERVAL 1 DAY,具体规则可以参照:
TTL expr
[DELETE|RECOMPRESS codec_name1|TO DISK 'xxx'|TO VOLUME 'xxx'][, DELETE|RECOMPRESS codec_name2|TO DISK 'aaa'|TO VOLUME 'bbb'] ...
[WHERE conditions]
[GROUP BY key_expr [SET v1 = aggr_func(v1) [, v2 = aggr_func(v2) ...]] ]
其中:
-
- DELETE:删除过期行(默认的),可以和wehre条件一起使用,根据过滤条件仅删除部分过期行
- RECOMPRESS :重新压缩数据(进行更深层次的压缩以减少存储压力)
- TO DISK 'aaa':将数据的part移动到磁盘aaa(aaa必须是config.xml中所存在的)
- TO VOLUME 'bbb':将数据的part移动到卷bbb(bbb必须是config.xml中所存在的)
- GROUP BY :按照规则聚合过期行,条件表达式必须是表的主键
- SETTINGS:可以自定义控制该表的附加参数,而不是使用通用或者默认的值,一般而言我们会配置storage_policy,使用config.xml中定义的名称,就可以使用我们定义好的多个存储设备进行数据存储。其余的配置可以参考官网
2).ReplicatedMergeTree使用
因为是MergeTree家族,所以除ENGINE部分的参数可以与上面的MergeTree一样。主要修改引擎的配置
创建表语句:
CREATE TABLE
table_name ( ... )
ENGINE = ReplicatedMergeTree('zookeeper_name_configured_in_auxiliary_zookeepers:path', 'replica_name')
...
上面的ReplicatedMergeTree()里面的参数是我们创建表时需要重点关注的,集团内部默认使用ReplicatedMergeTree('/clickhouse/tables/{shard}/{database}/{table}', '{replica}'),根据config.xml中配置的默认宏去指定shard名称和replica名称,/clickhouse/tables/是公共前缀。我们建议直接使用这个,其余的{}可以让clickhouse自己填写。
或者,可以直接使用配置文件定义
<default_replica_path>/clickhouse/tables/{shard}/{database}/{table}</default_replica_path>
<default_replica_name>{replica}</default_replica_name>
在这种情况下,建表可以省略参数。
ReplicatedMergeTree引擎使用单独的线程池进行复制提取。池的大小受到background_fetches_pool_size设置的限制,该设置可以通过服务器重新启动进行调整。
3).Distributed使用
创建表语句:
CREATE TABLE [IF NOT EXISTS] [db.]table_name [ON CLUSTER cluster]
(
name1 [type1] [DEFAULT|MATERIALIZED|ALIAS expr1],
name2 [type2] [DEFAULT|MATERIALIZED|ALIAS expr2],
...
) ENGINE = Distributed(cluster, database, table[, sharding_key[, policy_name]])
[SETTINGS name=value, ...]
参数说明:
- cluster:服务器配置文件中配置的集群名称
- database:逻辑表所在数据库的名称
- table:逻辑表的名称
- sharding_key:分片键,选填,目前项目尚未使用到,可以不填。
- policy_name:分布式存储的策略名称,集团使用rand(),推荐大家也同样使用
- settings:分布式表的配置,一般不使用,具体可以参考
4.使用案例:
以下我司使用的标准建表语句:
CREATE TABLE IF NOT EXISTS xxx_to_xxxx.xxx_log_static_min_local ON CLUSTER cluster_2shards_1replicas ( `api_id` String COMMENT '能力唯一主键', `app_id` String COMMENT '调用应用唯一主键', `response_code` Nullable(String) COMMENT '响应码', `time` DateTime COMMENT '调用时间', `cnt` String COMMENT '调用时间内的调用总量', `avg_cost` String COMMENT '平均花费时间', `create_dt` DateTime COMMENT '当前记录的创建时间' ) ENGINE = ReplicatedMergeTree('/clickhouse/tables/{shard}/{database}/{table}', '{replica}') PARTITION BY toYYYYMM(time) ORDER BY (time,api_id,app_id) SETTINGS storage_policy = 'new_storage_only' COMMENT 'xxx分钟汇聚本地表'; CREATE TABLE IF NOT EXISTS xxx_to_xxx.xxx_log_static_min_all ON CLUSTER cluster_2shards_1replicas ( `api_id` String COMMENT '能力唯一主键', `app_id` String COMMENT '调用应用唯一主键', `response_code` Nullable(String) COMMENT '响应码', `time` DateTime COMMENT '调用时间', `cnt` String COMMENT '调用时间内的调用总量', `create_dt` DateTime COMMENT '当前记录的创建时间' ) ENGINE = Distributed(cluster_2shards_1replicas, xxx_to_xxx, xxx_log_static_min_local,rand()) COMMENT 'XXX分钟汇聚分布式表';
- 本文标签: 数据库
- 本文链接: https://www.yynhworld.cn/article/33
- 版权声明: 本文由御影年华原创发布,转载请遵循《署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0)》许可协议授权