clickhouse集群安装与实践
一、产品介绍
ClickHouse是俄罗斯的Yandex于2016年开源的一个用于联机分析(OLAP:Online Analytical Processing)的列式数据库管理系统(DBMS:Database Management System),简称CK , 主要用于在线分析处理查询(OLAP),能够使用SQL查询实时生成分析数据报告。
ClickHouse是一个完全的列式数据库管理系统,允许在运行时创建表和数据库,加载数据和运行查询,而无需重新配置和重新启动服务器,支持线性扩展,简单方便,高可靠性,容错。它在大数据领域没有走 Hadoop 生态,而是采用 Local attached storage 作为存储,这样整个 IO 可能就没有 Hadoop 那一套的局限。它的系统在生产环境中可以应用到比较大的规模,因为它的线性扩展能力和可靠性保障能够原生支持 shard + replication 这种解决方案。它还提供了一些 SQL 直接接口,有比较丰富的原生 client。另外就是它比较快。
二、特点
1、列式存储:
行式存储的好处:
想查找某个人所有的属性时,可以通过一次磁盘查找加顺序读取就可以;但是当想查所有人的年龄时,需要不停的查找,或者全表扫描才行,遍历的很多数据都是不需要的。
列式存储的好处
- 对于列的聚合、计数、求和等统计操作优于行式存储
- 由于某一列的数据类型都是相同的,针对于数据存储更容易进行数据压缩,每一列选择更优的数据压缩算法,大大提高了数据的压缩比重
- 数据压缩比更好,一方面节省了磁盘空间,另一方面对于cache也有了更大的发挥空间
- 列式存储不支持事务
2、DBMS功能:
几乎覆盖了标准 SQL 的大部分语法,包括 DDL 和 DML、,以及配套的各种函数;用户管理及权限管理、数据的备份与恢复
3、多样化引擎:
目前包括合并树、日志、接口和其他四大类20多种引擎。
4、高吞吐写入能力:
ClickHouse采用类LSM Tree的结构,数据写入后定期在后台Compaction。通过类 LSM tree的结构, ClickHouse在数据导入时全部是顺序append写,写入后数据段不可更改,在后台compaction时也是多个段merge sort后顺序写回磁盘。顺序写的特性,充分利用了磁盘的吞吐能力。
5、数据分区与线程及并行:
ClickHouse将数据划分为多个partition,每个partition再进一步划分为多个index granularity(索引粒度),然后通过多个CPU核心分别处理其中的一部分来实现并行数据处理。在这种设计下, 单条 Query 就能利用整机所有 CPU。 极致的并行处理能力,极大的降低了查询延时。
所以, ClickHouse 即使对于大量数据的查询也能够化整为零平行处理。但是有一个弊端就是对于单条查询使用多cpu,就不利于同时并发多条查询。所以对于高 qps 的查询业务并不是强项。
6、关联查询:
ClickHouse 像很多 OLAP 数据库一样,单表查询速度优于关联查询,而且 ClickHouse的两者差距更为明显。
关联查询:clickhouse会将右表加载到内存。
三、ClickHouse引擎
引擎决定了数据的存储位置、存储结构、表的特征(是否修改操作DDL、DDL、是否支持并发操作)
1、数据库引擎
目前支持的数据库引擎有5种:
- Ordinary:默认引擎,在绝大多数情况下我们都会使用默认引擎,使用时无须刻意声明。在此数据库下可以使用任意类型的表引擎。
- Dictionary:字典引擎,此类数据库会自动为所有数据字典创建它们的数据表
- Memory:内存引擎,用于存放临时数据。此类数据库下的数据表只会停留在内存中,不会涉及任何磁盘操作,当服务重启后数据会被清除
- Lazy:日志引擎,此类数据库下只能使用Log系列的表引擎
- MySQL:MySQL引擎,将远程的MySQL服务器中的表映射到ClickHouse中,常用语数据的合并。
- MaterializeMySQL:MySQL数据同步;将MySQL数据全量或增量方式同步到clickhouse中,解决mysql服务并发访问压力过大的问题
五、使用背景
因项目需要,将阿里云中间件tablestore替换成clickhouse,并将tablestore中的亿级数据迁移到clickhouse。
六、集群搭建
1、基础环境
clickhouse集群依赖jdk和Zookeeper,请先安装基础环境;
数据量较大时,建议挂载大容量磁盘;
2、服务器准备
IP | 路径 |
---|---|
172.23.1.233 | /clickhouse |
172.23.1.234 | /clickhouse |
172.23.1.235 | /clickhouse |
2、安装单机版clickhouse(每台机器都安装)
#设置变量LATEST_VERSION,安装的版本为22.2.2.1(此版本比较稳定)
export LATEST_VERSION=22.2.2.1
#下载安装包到服务器
cd /clickhouse
curl -O https://repo.clickhouse.com/tgz/stable/clickhouse-common-static-22.2.2.1.tgz
curl -O https://repo.clickhouse.com/tgz/stable/clickhouse-common-static-dbg-22.2.2.1.tgz
curl -O https://repo.clickhouse.com/tgz/stable/clickhouse-server-22.2.2.1.tgz
curl -O https://repo.clickhouse.com/tgz/stable/clickhouse-client-22.2.2.1.tgz
#解压包
tar -xzvf clickhouse-common-static-$LATEST_VERSION.tgz
tar -xzvf clickhouse-common-static-dbg-$LATEST_VERSION.tgz
tar -xzvf clickhouse-server-$LATEST_VERSION.tgz
tar -xzvf clickhouse-client-$LATEST_VERSION.tgz
#运行安装命令
sudo clickhouse-common-static-$LATEST_VERSION/install/doinst.sh
sudo clickhouse-common-static-dbg-$LATEST_VERSION/install/doinst.sh
#该步骤可以设置默认用户的密码 修改default用户密码:/etc/clickhouse-server/users.d/default-password.xml
sudo clickhouse-server-$LATEST_VERSION/install/doinst.sh
sudo clickhouse-client-$LATEST_VERSION/install/doinst.sh
3、修改ClickHouse配置为集群版(每台机器都修改)
备份默认配置文件:cp /etc/clickhouse-server/config.xml /etc/clickhouse-server/config.xml.bak
然后编辑默认配置文件 /etc/clickhouse-server/config.xml 并删除集群相关的配置
-
- 文件中<remote_servers></remote_servers>标签里的全部内容
- 文件中标签里的全部内容
- 文件中标签里的全部内容
添加自定义配置文件:vi /etc/clickhouse-server/config.d/config.xml 内容如下
<yandex>
<zookeeper>
<node index="1">
<host>此处改为ZK的HOST</host>
<port>2181</port>
</node>
<identity>zk_username:zk_password</identity>
<session_timeout_ms>600000</session_timeout_ms>
</zookeeper>
<remote_servers>
<test>
<shard>
<internal_replication>true</internal_replication>
<replica>
<host>172.23.1.233</host>
<port>9000</port>
<user>default</user>
<password>123456</password>
</replica>
</shard>
<shard>
<internal_replication>true</internal_replication>
<replica>
<host>172.23.1.234</host>
<port>9000</port>
<user>default</user>
<password>123456</password>
</replica>
</shard>
<shard>
<internal_replication>true</internal_replication>
<replica>
<host>172.23.1.235</host>
<port>9000</port>
<user>default</user>
<password>123456</password>
</replica>
</shard>
</test>
</remote_servers>
<networks>
<ip>::/0</ip>
</networks>
<macros>
<shard>01</shard>
<replica>node1</replica>
</macros>
</yandex>
zookeeper 配置
-
- 如果Zookeeper为集群版,直接增加node节点即可
remote_servers 配置
-
- remote_servers下级节点为集群,可配置多个集群
- 集群下级节点为分片(shard),可配置多个shard,不同shard不能用同一个ClickHouse实例
- 分片下级为副本(replica),可对分片配置多个副本,默认最少0个,不同副本不能用同一个ClickHouse实例
- internal_replication 用来控制当数据写入时(必须是Replicated*的表),由分片自己负责副本间的数据复制,否则分布式表的副本数据写入需要由Distributed引擎来负责
macros 配置
-
- 本质上就是针对当前实例的全局变量的定义,可以被某些地方来引用
- 此配置需要在集群中全局唯一
- 此处的参数会在创建Replicated*的表时被引用
- shard的值为当前节点在在集群中的分片编号,需要在集群中唯一
- replica是副本的唯一标识,需要在单个分片的多个副本中唯一
七、修改clickhouse数据和日志存储路径
1、默认的数据目录包含在/var/lib/clickhouse中,将该文件夹移动到需要更改的新目录
vim /clickhouse/clickhouse-server-22.2.2.1/etc/clickhouse-server/config.xml
更改全部的/var/lib/clickhouse改为/clickhouse
2、修改启动命令
vim /clickhouse/clickhouse-server-22.2.2.1/install/doinst.sh
更改全部的/var/lib/clickhouse改为/clickhouse
3、修改服务脚本的目录
vim /etc/init.d/clickhouse-server
更改全部的/var/lib/clickhouse改为/clickhouse
4、重新安装服务启动
sudo clickhouse-client-$LATEST_VERSION/install/doinst.sh
sudo /etc/init.d/clickhouse-server restart
八、分布式表创建
1、创建本地表
建表语句基本语法如下:
CREATE TABLE [IF NOT EXISTS] [db.]table_name ON CLUSTER cluster
(
name1 [type1] [DEFAULT|MATERIALIZED|ALIAS expr1],
name2 [type2] [DEFAULT|MATERIALIZED|ALIAS expr2],
...
INDEX index_name1 expr1 TYPE type1(...) GRANULARITY value1,
INDEX index_name2 expr2 TYPE type2(...) GRANULARITY value2
) ENGINE = engine_name()
[PARTITION BY expr]
[ORDER BY expr]
[PRIMARY KEY expr]
[SAMPLE BY expr]
[SETTINGS name=value, ...];
选项描述:
- db:指定数据库名称,如果当前语句没有包含‘db’,则默认使用当前选择的数据库为‘db’。
- cluster:指定集群名称,目前固定为default。ON CLUSTER 将在每一个节点上都创建一个本地表。
- type:该列数据类型,例如 UInt32。
- DEFAULT:该列缺省值。如果INSERT中不包含指定的列,那么将通过表达式计算它的默认值并填充它。
- MATERIALIZED:物化列表达式,表示该列不能被INSERT,是被计算出来的; 在INSERT语句中,不需要写入该列;在SELECT *查询语句结果集不包含该列。
- ALIAS :别名列。这样的列不会存储在表中。 它的值不能够通过INSERT写入,同时使用SELECT查询星号时,这些列也不会被用来替换星号。 但是它们可以用于SELECT中,在这种情况下,在查询分析中别名将被替换。
- 物化列与别名列的区别: 物化列是会保存数据,查询的时候不需要计算,而别名列不会保存数据,查询的时候需要计算,查询时候返回表达式的计算结果
以下选项与表引擎相关,只有MergeTree系列表引擎支持:
- PARTITION BY:指定分区键。通常按照日期分区,也可以用其他字段或字段表达式。
- ORDER BY:指定 排序键。可以是一组列的元组或任意的表达式。
- PRIMARY KEY: 指定主键,默认情况下主键跟排序键相同。因此,大部分情况下不需要再专门指定一个 PRIMARY KEY 子句。
- SAMPLE BY :抽样表达式,如果要用抽样表达式,主键中必须包含这个表达式。
- SETTINGS:影响 性能的额外参数。
- GRANULARITY :索引粒度参数。
说明 :
高可用集群(双副本),要用ReplicatedMergeTree等Replicated系列引擎,否则副本之间不进行数据复制,导致数据查询结果不一致。
{shard},{replica} 参数不需要赋值。
2、创建分布式表
基于本地表创建一个分布式表。
创建分布式表基本语法:
CREATE TABLE [db.]table_name ON CLUSTER default
AS db.local_table_name ENGINE = Distributed(<cluster>, <database>, <shard table> [, sharding_key])
参数说明:
- db:数据库名。
- local_table_name:对应的已经创建的本地表表名。
- shard table:同上,对应的已经创建的本地表表名。
- sharding_key:分片表达式。可以是一个字段,例如user_id(integer类型),通过对余数值进行取余分片;也可以是一个表达式,例如rand(),通过rand()函数返回值/shards总权重分片;为了分片更均匀,可以加上hash函数,如intHash64(user_id)。
说明 : db_name 是数据库的名字,需要填写。
九、数据更新场景
因项目需要数据同步,主键存在更新,主键不存在新增,所以在创建分布式表时选择了ReplacingMergeTree。
Clickhouse作为一个OLAP数据库,它对事务的支持非常有限。Clickhouse提供了MUTATION操作(通过ALTER TABLE语句)来实现数据的更新、删除,但这是一种“较重”的操作,它与标准SQL语法中的UPDATE、DELETE不同,是异步执行的,对于批量数据不频繁的更新或删除比较有用。
除了MUTATION操作,Clickhouse还可以通过CollapsingMergeTree、VersionedCollapsingMergeTree、ReplacingMergeTree结合具体业务数据结构来实现数据的更新、删除,这三种方式都通过INSERT语句插入最新的数据,新数据会“抵消”或“替换”掉老数据,但是“抵消”或“替换”都是发生在数据文件后台Merge时,也就是说,在Merge之前,新数据和老数据会同时存在。因此,我们需要在查询时做一些处理,避免查询到老数据。Clickhouse官方文档提供了使用CollapsingMergeTree、VersionedCollapsingMergeTree的指导,相比于CollapsingMergeTree、VersionedCollapsingMergeTree需要标记位字段、版本字段,用ReplacingMergeTree来实现数据的更新删除会更加方便,这里着重介绍一下如何用ReplacingMergeTree来实现数据的更新删除。
#先创建本地表
CREATE TABLE local_user_info_local ON CLUSTER test
(
id UInt64,
phone String,
contrast_date DateTime
)ENGINE = ReplicatedReplacingMergeTree(
'/clickhouse/tables/local_user_info_local/{shard}',
'{replica}',contrast_date)
ORDER BY id
PARTITION BY toYYYYMM(contrast_date)
PRIMARY KEY id;
#基于本地表创建分布式表
CREATE TABLE local_user_info ON CLUSTER test AS local_user_info_local
ENGINE = Distributed(test, default, local_user_info_local, intHash64(id));
local_user_info_local为本地表,local_user_info为对应的分布式表。其中local_user_info_local使用ReplicatedReplacingMergeTree表引擎,第三个参数‘contrast_date’表示相同主键的多条数据,只会保留contrast_date最大的一条,我们正是利用ReplacingMergeTree的这一特性来实现数据的更新删除。因此,在选择主键时,我们需要确保主键唯一。这里我们选择id来作为主键。