写点什么

云原生小课堂 | 一文入门性能凶悍的开源分析数据库 ClickHouse

作者:York
  • 2022 年 4 月 28 日
  • 本文字数:11987 字

    阅读完需:约 39 分钟

 


clickhouse 简介

ClickHouse 是一个开源的,面向列的 MPP 架构数据分析数据库(大规模并行处理),由俄罗斯 Yandex 为 OLAP 和大数据用例创建。

ClickHouse 全称是 Click Stream,Data Warehouse,简称 ClickHouse 就是基于页面的点击事件流,面向数据仓库进行 OLAP 分析。

ClickHouse 对实时查询处理的支持使其适用于需要亚秒级分析结果的应用程序。

ClickHouse 的查询语言是 SQL 的一种方言,它支持强大的声明性查询功能,同时为最终用户提供熟悉度和较小的学习曲线。

ck 的优势和不足

优势:

  1. 查询速度非常快,每台服务器每秒处理上亿或上百亿行的数据。

  2. 可以充分利用硬件多线程实现单个查询的分支处理性能超过每秒 2tb

  3. 数据存储经过压缩,能够减少 io

  4. 存储引擎强大

  5. 性能好:

    一亿性能对比:



   十亿性能对比



  1. 容错与高可靠:

    支持分布式集群

    支持多主机异步复制,可跨多数据中心部署

    所有节点都相等避免出现单点故障

    单个节点或整个数据中心停机时间不影响系统读写可用性

    包括许多企业健全功能和针对认为错误的故障安全机制

不足:

  1. 不支持事务(这其实也是大部分 OLAP 数据库的缺点)

  2. 不擅长根据主键按行粒度查询(但是支持这种操作),它是按列存储,按列查询,故并不很适合按行查询的场景。

  3. 不擅长按行删除数据(但是支持这种操作),按行删除数据性能较低

ClickHouse 的特性

  • 定位是分析性数据库,即 OLAP,不是严格的关系型

    OLAP(关系型的联机分析处理'基于大批量数据聚合统计分析,侧重于查询',和它一起比较的还有 OLTP 联机事务处理'基于事务的数据处理分析,侧重于事务',我们常见的 ERP,CRM 系统就属于 OLTP)

  • 完整的 DBMS(关系数据库)

    具有 database、table、row、DDL、DML、用户、权限控制、数据备份、数据恢复、分布式管理的功能、支持大规模并行运算、每个节点存储对应分区数据

  • 列式存储数据库

    相同的列的数据存在一起,查询的时候只查需要的列,其他的列不关注,不做 io,避免全表扫描,有更好的压缩比。

  • 在线实时查询

  • 不需要任何数据预处理

  • 支持批量更新

  • 拥有完善的 SQl 支持和函数

  • 支持高可用

  • 不依赖 Hadoop 复杂生态(像 ES 一样,开箱即用)

ClickHouse 应用场景

ck 比较适用于广告流量,web 流量,app 浏览,金融,电子商务,信息安全,电信网络游戏和物联网等领域

非常适合大数据分析的场景,可以用于电信数据的存储和统计,用户行为的数据记录和分析,信息安全日志分析,商业智能与广告网络价值的数据挖掘,以及网络游戏和物联网的数据分析和处理。

ClickHouse 作为一款高性能 OLAP 数据库,但并不适合事务性场景

clickhouse 的数据访问流程

  • server

    ck 服务器实现了多个不同的接口:

    1.用与外部客户端的 http 接口

    2.用于数据传输拷贝的接口

    3.用于本机的 ClickHouse 客户端接口,也作为在分布式查询执行中跨服务器通信的 TCP 接口

  • Parser 分析器

    负责创建 AST 对象(抽象语法树)

    将一条 SQL 解析成 AST 语法树的形式,不同的 SQL 有不同的 Parser 分析器来解析

  • Intercepter 解释器

    负责解释 AST 对象,创建查询的执行通道

  • IStorage 存储接口

    负责根据 AST 语句的要求返回指定列的原始数据

    定义了 DDL、read、write 方法,负责数据定义查询和写入

  • Block

    ClickHouse 内部的数据操作均是通过操作 Block 对象完成的。

    Block 对象包含了数据对象(Column)、数据类型(DataType)、列名组成的三元组,Block 对象进一步抽象和封装了该三元组,使得通过 Block 对象可以完成一系列的数据操作

  • Column 和 Field

    Column 提供了数据的读取能力

    Column 和 Field 是 ck 最基础的映射单元

    ck 按列存储数据,每列数据由一个 Column 对象表示,Field 表示 Column 的一个单值 ( 也就是单列中的一行数据 )

  • DataType

    数据类型由 DataType 负责,提供了序列化和反序列化,从 Column 和 Field 获取数据

  • Function

    ck 提供两类函数(普通函数和聚合函数)

    普通函数由 IFunction 接口定义

    聚合函数有状态,由 IAggregateFunction 定义,以 COUNT 聚合函数为例,其 AggregateFunctionCount 的状态使用整型 UInt64 记录。状态支持序列化和反序列化,在分布式节点间可以进行传输,实现增量计算。



安装单点 clickhouse

支持的平台:x86_64 、AArch64、Power9

#官方预构建的二进制文件通常为 x86_64 编译并利用 SSE 4.2 指令集,需要进行检测grep -q sse4_2 /proc/cpuinfo && echo "SSE 4.2 supported" || echo "SSE 4.2 not supported"#来自 DEB 包的安装方式:sudo apt-get install -y apt-transport-https ca-certificates dirmngrsudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 8919F6BD2B48D754
echo "deb https://packages.clickhouse.com/deb stable main" | sudo tee \    /etc/apt/sources.list.d/clickhouse.listsudo apt-get update
sudo apt-get install -y clickhouse-server clickhouse-client#启动 clickhouse-server 默认端口9000sudo service clickhouse-server start#启动 clickhouse-client链接serverclickhouse-client # or "clickhouse-client --password" if you've set up a password.

#可以用以下方式安装对应版本sudo apt-get install clickhouse-server=21.8.5.7 clickhouse-client=21.8.5.7 clickhouse-common-static=21.8.5.7

# arm 系统需要使用wget获取对应版本包 wget --progress=bar:force:noscroll "https://builds.dev.altinity.cloud/apt-repo/pool/main/clickhouse-client_21.8.5.7.dev_all.deb" -P /tmp/clickhouse_debs      wget --progress=bar:force:noscroll "https://builds.dev.altinity.cloud/apt-repo/pool/main/clickhouse-common-static_21.8.5.7.dev_arm64.deb" -P /tmp/clickhouse_debs      wget --progress=bar:force:noscroll "https://builds.dev.altinity.cloud/apt-repo/pool/main/clickhouse-server_21.8.5.7.dev_all.deb" -P /tmp/clickhouse_debs 

复制代码


部署中可能用到的一些额外配置

  • 文件句柄数量配置:/etc/security/limits.d/clickhouse.conf

    在 linux 下每一个 tcp 连接都要占一个文件描述符,如果达到上限,就会出现错误:“Socket/File:Can’t open so many files”。

    如果配置太小可能会影响性能。

  • 定时任务配置:/etc/corn.d/clickhouse-server

与 MySQL 类似,启动 clickhouse 程序会启动一个服务进程,默认端口 9000,一台主机上可启动多实例,需要使用不同端口。

服务启动后会默认创建 system 与 default 两个数据库。

ck 的常见命令

#基本和mysql差不多SHOW databases;SHOW tables;USE [database];DESC  [table];SELECT * from [database].[table];SHOW CREATE insert_select_testtable;CREATE TABLE insert_select_testtable(    `a` Int8,    `b` String,    `c` Int8)ENGINE = MergeTree()ORDER BY a ;INSERT INTO insert_select_testtable (*) VALUES (1, 'a', 1) ;
复制代码


ck 的核心目录

  • 服务端配置文件目录: /etc/clickhouse-server

  • 数据目录(默认目录): /var/lib/clickhouse

    ck 的表数据存于/var/lib/clickhouse/data/[database]/[table]目录下

  • 表的元数据目录:/var/lib/clickhouse-server/metadata

  • 日志目录:/var/log/clickhouse-server

    clickhouse-server.err.logclickhouse-server.log文件

  • 可执行文件目录:/user/bin

    clickhouse 主程序的可执行文件

    clickhouse-client 客户端可执行文件

    clickhouse-server 服务端可执行文件

    clickhouse-compressor 内置的解压缩工具

CK 的索引

默认提供两种索引,稀疏索引和跳数索引,

根据索引所覆盖的行数产生索引标记来记录数据的区间信息

稀疏索引

按主键或者排序键进行排序后保存,默认粒度 8192 行。稀疏索引只需要使用少量的索引标记就可以记录大量数据的区间位置信息,这样索引文件足够小,常驻内存取用更快

跳数索引

由数据的聚合产生(Max/min 等)数据经过间隔力度构建索引后,跳数索引会根据索引粒度对数据进行汇总(跳跃)。

跳数索引的粒度其实相当于对已构建的索引的合并。

ck 的数据类型

不同版本的 clickhouse 的数据类型有所区别,具体可以使用以下命令查询:

select * from system.data_type_families;# 数据类型的官方名称 是否区分大小写 别名 别名和官名用哪个都行┌─name────────────────────────────┬─case_insensitive─┬─alias_to────┐│ BLOB                            │                1 │ String      │└─────────────────────────────────┴──────────────────┴─────────────┘
复制代码


比较基础的数据类型

这里列举了一些和其他的数据库没有太大区别的数据类型。

UInt8, UInt16, UInt32, UInt64, UInt128, UInt256, Int8, Int16, Int32, Int64, Int128, Int256

uuid

Datetime64,Date32

Float32, Float64

Decimal(P, S), Decimal32(S), Decimal64(S), Decimal128(S), Decimal256(S)

string

注意 ck 并没有提供 bool 数据类型,可以使用 uint8 取值限制为 0 或者 1

FixedString(N)

存储固定长度(按字节数计)字符串

  • 如果字符串包含的字节数少于字符串,则用空字节补充字符串N

  • Too large value for FixedString(N)如果字符串包含多于N字节,则引发异常。

  • 查询数据时,ClickHouse 不会删除字符串末尾的空字节。如果使用WHERE子句,则需要手动添加空字节以匹配该FixedString

    比如:当 FixedString(2)的数据 k 值为 a,使用where k = 'a'无法查到该叙述,需要使用where k ='a\0'

  • FixedString(N)的长度是个常量。仅由空字符组成的字符串,函数 length 返回值为N,而函数 empty 的返回值为1,即有长度的空字符串

LowCardinality(data_type)

把其它数据类型转变为字典编码类型。

它适合长度和定义域都可变,但总体基数不是特别大的列

低基数是一种修饰类型,即用法为 LowCardinality(type)。其中 type 表示的原始类型可以是 String、FixedString、Date、DateTime,以及除了 Decimal 之外的所有数值类型。一般来说 string 比较常用。

它使用字典位置进行过滤、分组、加速某些函数(例如 length()).

使用 LowCarditality 数据类型的效率依赖于数据的多样性。如果一个字典包含少于 10000 个不同的值,那么 ClickHouse 可以进行更高效的数据存储和处理。反之如果字典多于 10000,效率会表现的更差。

当使用字符类型的时候,可以考虑使用 LowCardinality 代替 enum。LowCardinality 通常更加灵活和高效。

CREATE TABLE lc_t(    `id` UInt16,    `strings` LowCardinality(String))ENGINE = MergeTree()ORDER BY id
复制代码


array(T)

在同一数组内可以包含多种数据类型,但是数据类型必须要兼容;定义表的时候数组需要明确指定元素类型.

数组的最大大小限制为一百万个元素。

如果 ClickHouse 无法确定数据类型,则会生成异常。当尝试同时创建包含字符串和数字的数组时会发生这种情况。

举例:

CREATE TABLE t_arr (`arr` Array(Array(Array(UInt32)))) ENGINE = MergeTree ORDER BY tuple();
INSERT INTO t_arr VALUES ([[[12, 13, 0, 1],[12]]]);
SELECT arr.size0, arr.size1, arr.size2 FROM t_arr;#结果:┌─arr.size0─┬─arr.size1─┬─arr.size2─┐│         1 │ [2]       │ [[4,1]]   │└───────────┴───────────┴───────────┘
复制代码


Enum

由命名值组成的枚举类型。

命名值必须声明为'string' = integer对。ClickHouse 仅存储数字,但支持通过名称对值进行操作。

ClickHouse 自动选择Enum插入数据的类型。支持使用Enum8Enum16类型来确定存储的大小。

#创建一个带有一个枚举 Enum8('hello' = 1, 'world' = 2) 类型的列:CREATE TABLE t_enum(  x Enum8('hello' = 1, 'world' = 2))ENGINE = TinyLog#这个 `x` 列只能存储类型定义中列出的值:`'hello'`或`'world'`。如果尝试保存任何其他值,ClickHouse 抛出异常。
复制代码


Tuple

组类型由 1~n 个元素组成,每个元素之间允许设置不同的数据类型,且彼此之间不要求兼容。元组支持类型推断,其推断依据以最小存储代价为原则。

SELECT tuple(1,'a') AS x, toTypeName(x)┌─x───────┬─toTypeName(tuple(1, 'a'))─┐│ (1,'a') │ Tuple(UInt8, String)      │└─────────┴───────────────────────────┘
SELECT tuple(1, NULL) AS x, toTypeName(x)┌─x────────┬─toTypeName(tuple(1, NULL))──────┐│ (1,NULL) │ Tuple(UInt8, Nullable(Nothing)) │└──────────┴─────────────────────────────────┘
CREATE TABLE named_tuples (`a` Tuple(s String, i Int64)) ENGINE = Memory;INSERT INTO named_tuples VALUES (('y', 10)), (('x',-10));#使用名称查找元素SELECT a.s FROM named_tuples;┌─a.s─┐│ y   ││ x   │└─────┘#使用索引查找元素SELECT a.2 FROM named_tuples;┌─tupleElement(a, 2)─┐│                 10 ││                -10 │└────────────────────┘
复制代码


Nested(name1 Type1, Name2 Type2, …)

嵌套数据结构类似于嵌套表。一张数据表,可以定义任意多个嵌套类型字段,但每个字段的嵌套层级只支持一级,即嵌套表内不能继续使用嵌套类型。

CREATE TABLE dept (    name String,    people Nested(        id UInt8,        name String    )) ENGINE = Memory;INSERT INTO dept VALUES ('研发部',[001,002,003],['小李','小张','小刘']);INSERT INTO dept VALUES ('测试部',[001,002],['小李','小张']);
SELECT name, dept.id, dept.name FROM nested_test;┌─name──┬──people.id────┬─people.name──────────┐│ 研发部 │ [001,002,003] │ ['小李','小张','小刘'] │└───────┴───────────────┴──────────────────────┘┌─name──┬─people.id─┬─people.name────┐│ 测试部 │ [001,002] │ ['小李','小张'] │└───────┴───────────┴────────────────┘
INSERT INTO dept VALUES ('研发部',003,'小刘');# 报错:DB::Exception: Type mismatch in IN or VALUES section. Expected: Array(UInt8). Got: UInt64INSERT INTO dept VALUES ('研发部',[001,002],['小李','小张','小刘']);# DB::Exception: Elements 'people.id' and 'people.name' of Nested data structure 'people' (Array columns) have different array sizes..
复制代码


AggregateFunction

AggregateFunction 是 clickhouse 提供的一种特殊的数据类型,它能够以二进制的形式存储中间状态结果。

其使用方法也十分特殊,对于 AggregateFunction 类型的列字段,数据的写入和查询都与寻常不同。在写入数据时,需要调用 State 函数。而在查询数据时,则需要调用相应的 Merge 函数。

-- 建表语句 这里uniq 和sum是指定的聚合函数,而uniqState、uniqMerge 是函数对应的State和Merge函数CREATE TABLE t(  id String, code AggregateFunction(uniq,String),  value AggregateFunction(sum,UInt32), ) ENGINE = ...-- 写入测试数据;INSERT INTO TABLE t SELECT 'A000', uniqState('code1'), sumState(toUInt32(100));INSERT INTO TABLE t SELECT 'A000', uniqState('code1'), sumState(toUInt32(100));INSERT INTO TABLE t SELECT 'A001', uniqState('code1'), sumState(toUInt32(100));INSERT INTO TABLE t SELECT 'A001', uniqState('code2'), sumState(toUInt32(50));-- 查询数据SELECT id,uniqMerge(code),sumMerge(value) FROM t GROUP BY id;┌─id────┬─uniqMerge(code)─┬─sumMerge(value)─┐│ A001  │               2 │             150 ││ A000  │               1 │             200 │└───────┴─────────────────┴─────────────────┘
复制代码


Nullable(typename)

允许将表示“缺失值”的特殊标记(NULL)与允许的正常值一起存储TypeName。例如,Nullable(Int8)类型列可以存储Int8类型值,而没有值的行将存储NULL.

对于 a TypeName,您不能使用复合数据类型[Array]和[Tuple]。复合数据类型可以包含Nullable类型值,例如Array(Nullable(Int8)).

注意:为了存储空值会额外占用存储空间,会对性能有影响。

CREATE TABLE t_null(x Int8, y Nullable(Int8)) ENGINE TinyLogINSERT INTO t_null VALUES (1, NULL), (2, 3)SELECT x + y FROM t_null┌─plus(x, y)─┐│       ᴺᵁᴸᴸ ││          5 │└────────────┘# 如果对应的是null返回1 否则返回0SELECT y.null FROM t_null;┌─n.null─┐│      1 ││      0 │└────────┘
复制代码


Domain

域名类型分为 IPv4 和 IPv6 两类,本质上它们是对整型和字符串的进一步封装

IPv4 使用 UInt32 存储,相比 String 更加紧凑,占用的空间更小,查询性能更快

CREATE TABLE IP4_TEST (    url String,    ip IPv4) ENGINE = Memory;INSERT INTO IP4_TEST VALUES ('www.baidu.com','192.0.0.0');SELECT url,ip,toTypeName(ip) FROM IP4_TEST;#错误的ip无法写入INSERT INTO IP4_TEST VALUES ('www.baidu.com','192.0.0');# DB::Exception: Invalid IPv4 value.
复制代码


IPv6 类型是基于 FixedString(16)封装的,它的使用方法与 IPv4 一样。

数据库引擎

延时引擎 Lazy

最近一次访问间隔 expiration_time_in_seconds`秒,将数据存到内存里,只能用于*Log 表。

它是为存储许多小的*Log 表而优化的,对于这些表,两次访问之间的时间间隔很长。

CREATE DATABASE testlazy ENGINE = Lazy(expiration_time_in_seconds);
复制代码


Atomic

默认引擎

它支持非阻塞的[DROP TABLE]和[RENAME TABLE]查询和原子的[EXCHANGE TABLES t1 AND t2]查询。

数据库Atomic中的所有表都有唯一的 UUID,并将数据存储在目录/clickhouse_path/store/xxx/xxxyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy/,其中xxxyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy是该表的 UUID。

RENAME TABLES是在不更改 UUID 和移动表数据的情况下执行的。这些查询不会等待使用表的查询完成,而是会立即执行。

DROP TABLE时,不删除任何数据,数据库Atomic只是通过将元数据移动到/clickhouse_path/metadata_dropped/将表标记为已删除,并通知后台线程。最终表数据删除前的延迟由[database_atomic_delay_before_drop_table_sec]设置指定。

可以使用SYNC修饰符指定同步模式。使用[database_atomic_wait_for_drop_and_detach_synchronously]设置执行此操作,DROP等待运行 SELECTINSERT和其他使用表完成的查询。表在不使用时将被实际删除。

MySQL

MySQL 引擎用于将远程的 MySQL 服务器中的表映射到 ClickHouse 中,并允许对表进行INSERTSELECT查询,以方便在 ClickHouse 与 MySQL 之间进行数据交换

MySQL数据库引擎会将对其的查询转换为 MySQL 语法并发送到 MySQL 服务器中,因此可以执行诸如SHOW TABLESSHOW CREATE TABLE之类的操作。

但是不能使用RENAMECREATE TABLEALTER

CREATE DATABASE [IF NOT EXISTS] db_name [ON CLUSTER cluster]ENGINE = MySQL('host:port', ['database' | database], 'user', 'password')
复制代码


表引擎

表引擎有以下作用

1.数据如何存储,放在哪儿

2.支持哪些查询,如何支持

3.对并发访问的支持,能否多线程请求方式执行语句

4.所支持的查询种类

5.是否支持索引

6.主备复制的支持

CK 提供了近 30 种表引擎,分为四类:log、MergeTree、Special、Integration

Log(日志)

用于做小表的数据分析(100 万行左右)。

TinyLog、StripeLog、Log

共同特点

  • 数据存储在磁盘上。

  • 写入时将数据追加在文件末尾。

  • 不支持突变操作。

  • 不支持索引(这意味着 SELECT 在范围查询时效率不高。)

  • 非原子地写入数据(如果某些事情破坏了写操作,例如服务器的异常关闭,表内数据不完整)

差异:

Log 和 StripeLog 引擎都支持:

  • 并发访问数据的锁。(INSERT 请求执行过程中表会被锁定,并且其他的读写数据的请求都会等待直到锁定被解除。如果没有写数据的请求,任意数量的读请求都可以并发执行。)

  • 并行读取数据。(在读取数据时,ClickHouse 使用多线程。每个线程处理不同的数据块。)

TinyLog:

对并发访问没有限制(没有锁)

如果同时从表中读取并在不同的查询中写入,则读取操作将抛出异常 如果同时写入多个查询中的表,则数据将被破坏。

Log:

Log «标记» 的小文件与列文件存在一起。

这些标记写在每个数据块上,并且包含偏移量,这些偏移量指示从哪里开始读取文件以便跳过指定的行数。这使得可以在多个线程中读取表数据。

对于并发数据访问,可以同时执行读取操作,而写入操作则阻塞读取和其它写入。

Log引擎适用于临时数据,write-once 表以及测试或演示目的。

MergeTree(合并树)

该类型引擎用于做大数据量分析,是 ClickHouse 官方主要推荐的引擎类型,支持几乎所有的 ClickHouse 核心功能。

常用的存储引擎:

MergeTree:

该引擎中数据按列存储,每个列数据以二进制形式单独压缩,性能最好

ReplacingMergeTree:

基于 MergeTree 复制引擎,在分布式高可用汇总结合 zk 做副本复制

Distributed:

分布式引擎,此类表不存储数据,相当于视图功能,写入数据到分布式表中,会把请求分不到集群的各个分片中;在查询的时候做聚合查询再返回

Special

为特定的场景定制,例如:内存、缓存和文件

Integration

用于对其他外部数据库表的集成

比如将外部数据导入或者直接读取其他的数据源

ClickHouse 集群

ClickHouse 集群解决的是高可用与负载均衡的问题,一个集群可以用多个节点组成,当某集群节点出现故障后不影响整个集群的正常使用

什么是分区

在 clickhouse 中对于一张表做分区,则是对数据的纵向切分,数据以目录的形式存在,在写入时创建,相同分区的数据最终合并到同一个分区目录,不同分区的数据不会被合并到一起。

分区不能解决各个节点的数据分布问题,只可以降低数据文件的搜索范围。

CK 分布式集群

该分布式集群有两个分布式节点,每个分布式节点上有一个本地表,都有一个分布式表(Distributed),读取数据的时候分布式表会在两个本地表(也可以说是分片)中都读取数据汇总后返回给用户,收到的读写任务时,分布式表将任务分发到各个本地表。

原理如下图:



本地表

存放用户实际的数据,一个本地表等同于一份数据分片

分布式表

是一个逻辑概念,不存储任何数据,是本地表的访问代理,类似于 MySQL 中的视图,作用类似于分库中间件

多个节点上有多个本地表,集群中有一个分布式表,数据存入的时候 会由分布式表将数据随机分散到多个分片中,这时如果有一个节点坏掉,集群中数据会丢失一部分(即坏掉的分片的数据没了)

使用 on cluster 语句在集群的某台机器上执行以下代码,即可在每台机器上创建本地表和分布式表,其中⼀张本地表对应着⼀个数据分⽚,分布式表通常以本地表加“_all”命名。它与本地表形成⼀对多的映射关系,之后可以通过分布式表代理操作多张本地表。

这里有个要注意的点,就是分布式表的表结构尽量和本地表的结构一致。如果不一致,在建表时不会报错,但在查询或者插入时可能会抛出异常。在集群中使用,我们要加上 on cluster <cluster_name>的 ddl,这样我们的建表语句在某一台 clickhouse 实例上执行一次即可分发到集群中所有实例上执行。

先在每一个分片上创建本地表:

--使用ReplicatedMergeTree引擎创建本地表test_logcreate table test_log on cluster ck_cluster(    totalDate Date,    unikey    String)    engine = ReplicatedMergeTree('/clickhouse/test/tables/{shard}/test_log', '{replica}')        PARTITION BY totalDate        ORDER BY unikey        SETTINGS index_granularity = 8192;

复制代码


分布式表引擎的创建模板:

ENGINE = Distributed(cluster, database, table, [sharding_key])
复制代码


参数描述:

cluster:集群名称,在对分布式表执⾏读写的过程中,它会使⽤集群的配置信息来找到相应的 host 节点。database,table:数据库和本地表名称,用于将分布式表映射到本地表上。sharding_key: 分⽚键,分布式表会按照这个规则,将数据分发到各个本地表中。

--创建分布式表test_log_all,数据在读写时会根据rand()随机函数的取值,决定数据写⼊哪个分⽚,也可以用hash取值。create table test_log_all on cluster ck_cluster(    totalDate Date,    unikey    String)    engine = Distributed('ck_cluster', 'test', 'test_log', rand());
复制代码



CK 的副本复制


对每个分片复制了一份副本,放在其他的节点上,即做了数据备份,当某节点坏掉,会从其他节点的副本读取数据。

ClickHouse 只允许一个实例持有一个分片,所以在生产环境中,一般采用两个甚至多个对等的集群互相复制和热备(依靠 ReplicatedMergeTree 引擎族实现复制表),当某集群上的某节点挂掉后,可以由其他集群上持有对应分片的节点顶上,实现高可用。



CK 的副本复制不可避免降低了性能,假设说一个 2 核的机器,本来创建一个 ck 实例,这个 ck 实例可以用 2 核的 cpu 计算能力,但是当增加副本时,每个副本只能使用 1 个 cpu 的计算能力了。

但是其实对于大多数使用 ClickHouse 的场景来说,ck 都是被用于数据分析使用,在这样的场景下数据都是从其他的生产库中周期抽取,进行大数据分析的,所以数据其实是被允许丢失的。

但是如果数据丢失后,需要重新将所有的数据同步过来,可能会付出较大的代价,所以一般会创建一个副本来使用,保证集群的可用性。

分布式集群的读写规则

数据写入:

如果直接写入的是 A1/B1/C1,那么对应的复本表会写同样的数据

如果写入的是分布式表,则根据规则随机平分写入或者单独写入到某个分片中,副本进行复制

数据读取:

会从 A/B/C 同时读取数据,A 中的数据随机从 A1 或者 A2 读取,读取到所有数据后合并到一起返回结果,如果 A1 挂了会从 A2 读,不影响集群读取

常见的 CK 分布式方案

方案 1:纯分片

该方案在不同节点上创建分片,使用 Distributed+MergeTree 结构。

优点:

架构简单

并行查询分布式表,查询速度非常快

缺点:

如果某个分片节点损坏,会丢失数据且无法恢复,查询会报错,整个集群会瘫掉

适用场景:

不经常做分析,但是有数据分析需求,集群不需要持续运行提供服务,但是数据量较大(数据少可以直接用单点),采用该方式提高查询分析的效率,满足需要。



方案 2 :分片+副本复制

该方案在不同节点上创建分片,并对每个分片做副本复制,使用 Distributed+MergeTree 结构,副本复制由 Distributed 控制

优点:

数据安全有保障,不存在某一数据节点故障后无法查询,集群瘫掉的问题

并行查询分布式表,速度快

缺点:

某个节点存储损坏后,节点再次上线或者用其他的新节点上线,都会被视为空节点,损坏前该损坏节点所存储的数据无法恢复,只能保证新数据的,当另外一个分片坏掉时(低概率事件),依然会面临集群数据丢失集群瘫痪的问题

复制和分片数据分发均为 Distributed 组件控制,Distributed 出现问题,集群就会出问题。

适用场景:

这种架构适用于有新数据周期性写入,只对新数据做分析不对老数据分析的场景(老数据会舍弃)



方案 3:分片+副本复制+高可用

该方案在不同节点上创建分片,并对每个分片做副本复制,使用 ReplicatedMergeTree+Distributed+Zookeeper 结构。

本地表使用 ReplicatedMergeTree 方式创建

Zookeeper 是一个分布式服务框架,主要用于解决分布式应用中遇到的一些数据管理问题,例:统一命名服务,状态同步服务、集群管理,分布式应用配置项的管理。

优点:

共享同一个 Zookeeper 路径的表,相互同步数据,数据安全有保障,不存在节点故障,新节点上线 Zookeeper 会把损坏前的数据同步。

并行查询分布式表,速度快

缺点:

如果一次几百万写入,容易导致 Zookeeper 集群压力过大出现异常(可以采用写入本地表,读取分布式表的方案,可保证每秒几百个 insert 操作)

相比非复制表,insert 的延迟略大,需要等待 Zookeeper 集群驱动复制。

成本较高

适用场景:

适合数据量大,安全性要求高的生产环境,是最优的分布式高可用集群方案



生产环境方案总结

使用方案 3



  • 副本数量至少为两个,即两份数据

  • 通过外围负载均衡分发方式 写表写本地表,读表读分布式表,表管理统一入口

  • 默认并发 100 左右

  • 批量写入不建议小批量,小批量写入会造成过多的数据合并,性能会下降

  • 建议不要在一台主机内使用多实例,ck 使用时很容易跑满 cpu

  • 建议 zk 5 节点,不和 ck 在同一主机

  • 节点配置:CPU48 核、内存 128G\192G、磁盘 SSD RAID5/10、数据量 1TB/40 亿左右


关于【云原生小课堂】


【云原生小课堂】是由灵雀云、Kube-OVN 社区、云原生技术社区联合开设的公益性技术分享类专题,将以丰富详实的精品内容和灵活多样的呈现形式,持续为您分享云原生前沿技术,带您了解更多云原生实践干货。

在数字化转型的背景下,云原生已经成为企业创新发展的核心驱动力。作为国内最早将 Kubernetes 产品化的厂商之一,灵雀云从出生便携带“云原生基因”,致力于通过革命性的技术帮助企业完成数字化转型,我们期待着云原生给这个世界带来更多改变。

关注我们,学习更多云原生知识,一起让改变发生。

发布于: 2022 年 04 月 28 日阅读数: 4
用户头像

York

关注

云原生的美男子YORK 2021.01.07 加入

云原生技术社区为云原生技术实践联盟(CNBPA)旗下技术社区,专注泛云原生全栈云前沿技术和落地实践的布道。分享容器、Kubernetes、DevOps、Service Mesh、Serverless、数据库、中间件等技术干货。

评论

发布
暂无评论
云原生小课堂 | 一文入门性能凶悍的开源分析数据库ClickHouse_数据库_York_InfoQ写作社区