ClickHouse-1介绍

https://clickhouse.com/docs/zh/

一、clickhouse简介

ClickHouse 是俄罗斯的Yandex开源的用于在线分析处理查询(OLAP :Online Analytical Processing)的列式存储数据库(DBMS:Database Management System),能够使用 SQL 查询实时生成分析数据报告。ClickHouse的全称是Click Stream,Data WareHouse。

clickhouse可以做用户行为分析,流批一体

线性扩展和可靠性保障能够原生支持 shard + replication

clickhouse没有走hadoop生态,采用 Local attached storage 作为存储

二、clickhouse特点

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为什么快?

C++可以利用硬件优势

摒弃了hadoop生态

数据底层以列式存储

利用单节点的多核并行处理

为数据建立索引一级、二级、稀疏索引

使用大量的算法处理数据

支持向量化处理

预先设计运算模型-预先计算

分布式处理数据

四、引擎作用:

表引擎是 ClickHouse 的一大特色。可以说, 表引擎决定了如何存储表的数据。包括:

  • 数据的存储方式和位置
  • 支持哪些查询以及如何支持
  • 并发数据访问
  • 索引的使用
  • 是否可以执行多线性请求
  • 数据复制参数

五、ClickHouse引擎:

引擎决定了数据的存储位置、存储结构、表的特征(是否修改操作DDL、DDL、是否支持并发操作)

1、数据库引擎:数据库引擎 | ClickHouse文档

目前支持的数据库引擎有5种:

  • Ordinary:默认引擎,在绝大多数情况下我们都会使用默认引擎,使用时无须刻意声明。在此数据库下可以使用任意类型的表引擎。
  • Dictionary:字典引擎,此类数据库会自动为所有数据字典创建它们的数据表
  • Memory:内存引擎,用于存放临时数据。此类数据库下的数据表只会停留在内存中,不会涉及任何磁盘操作,当服务重启后数据会被清除
  • Lazy:日志引擎,此类数据库下只能使用Log系列的表引擎
  • MySQL:MySQL引擎,将远程的MySQL服务器中的表映射到ClickHouse中,常用语数据的合并。
  • MaterializeMySQL:MySQL数据同步;将MySQL数据全量或增量方式同步到clickhouse中,解决mysql服务并发访问压力过大的问题

2、表引擎:表引擎 | ClickHouse文档

 

Copy, Replicate, Duplicate 三者的不同:

1. Copy 仅仅是照抄、照搬,可以是从头到尾全部丝毫不变地从A照搬到B上,也可以是丝毫不差地照抄照搬A的某一、或某几部分到 B上。所以平时一说文件写作与编辑中的“复制、照搬”,都说 copy。口语中“复制”也一般都说 copy。某个电影供放映的一整套胶片,也被称作 copy。

2. Replicate 含义较复杂些:它作为“照搬、复制”来用时,一种用法是“按照某种预设的步骤或程序来彼此补齐、更新”。比如你的:你电脑上的Outlook, 必须不断与邮件服务器 mail server 沟通,人家给你的邮件才会出现在你电脑上,你发的邮件才能从你的电脑上经过邮件服务器发走。把你的电脑与服务器“同步一下”的这个过程,就是 replicate/replicating,而不能说 copying,因为这是 2 个现有的数据库之间,你的给我,我的给你,这样相互补差,你我彼此的资料就全都补齐更新、完全一致了。这个过程就是 replicate。

Replicate 另一(用中文来说)“复制”的用法,是指“照搬大的轮廓、基本概念,但需做很多小调整”。比如 Google had trouble replicating its world-wide success in China. Google的运作原理和大战略在世界任何地方都是一样的,但在中国市场它显然需要做很多本土化的战略战术和运作上调整。所以这也不是简单 copy 的那种照搬。

3. Duplicate: 强调的是从头到尾丝毫不差 --- To make an exact copy of something, totally and absolutely the same, from A through Z。比如复制数据库,就是由一个 database 变成 2 个或数个、数十个一模一样的 databases,这个过程就是 duplicate / duplication。如果银行的同一个数据库,从上海的服务器上复制到北京服务器上的过程中由于什么原因而多了或少了一两样东西,那问题就大了! "This is a duplicate copy of what I gave you yesterday" ---- 这话强调的就是"这份东西与我昨天给你的那份是完全一模一样的"。

什么是ClickHouse?

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

在传统的行式数据库系统中,数据按如下顺序存储:

RowWatchIDJavaEnableTitleGoodEventEventTime
#0 89354350662 1 Investor Relations 1 2016-05-18 05:19:20
#1 90329509958 0 Contact us 1 2016-05-18 08:10:20
#2 89953706054 1 Mission 1 2016-05-18 07:38:00
#N

处于同一行中的数据总是被物理的存储在一起。

常见的行式数据库系统有:MySQLPostgresMS SQL Server

在列式数据库系统中,数据按如下的顺序存储:

Row:#0#1#2#N
WatchID: 89354350662 90329509958 89953706054
JavaEnable: 1 0 1
Title: Investor Relations Contact us Mission
GoodEvent: 1 1 1
EventTime: 2016-05-18 05:19:20 2016-05-18 08:10:20 2016-05-18 07:38:00

这些示例只显示了数据的排列顺序。来自不同列的值被单独存储,来自同一列的数据被存储在一起。

常见的列式数据库有: Vertica、 Paraccel (Actian Matrix,Amazon Redshift)、 Sybase IQ、 Exasol、 Infobright、 InfiniDB、 MonetDB (VectorWise, Actian Vector)、 LucidDB、 SAP HANA、 Google Dremel、 Google PowerDrill、 Druid、 kdb+。

不同的数据存储方式适用不同的业务场景,数据访问的场景包括:进行了何种查询、多久查询一次以及各类查询的比例;每种类型的查询(行、列和字节)读取多少数据;读取数据和更新之间的关系;使用的数据集大小以及如何使用本地的数据集;是否使用事务,以及它们是如何进行隔离的;数据的复制机制与数据的完整性要求;每种类型的查询要求的延迟与吞吐量等等。

系统负载越高,依据使用场景进行定制化就越重要,并且定制将会变的越精细。没有一个系统能够同时适用所有不同的业务场景。如果系统适用于广泛的场景,在负载高的情况下,要兼顾所有的场景,那么将不得不做出选择。是要平衡还是要效率?

OLAP场景的关键特征

  • 绝大多数是读请求
  • 数据以相当大的批次(> 1000行)更新,而不是单行更新;或者根本没有更新。
  • 已添加到数据库的数据不能修改。
  • 对于读取,从数据库中提取相当多的行,但只提取列的一小部分。
  • 宽表,即每个表包含着大量的列
  • 查询相对较少(通常每台服务器每秒查询数百次或更少)
  • 对于简单查询,允许延迟大约50毫秒
  • 列中的数据相对较小:数字和短字符串(例如,每个URL 60个字节)
  • 处理单个查询时需要高吞吐量(每台服务器每秒可达数十亿行)
  • 事务不是必须的
  • 对数据一致性要求低
  • 每个查询有一个大表。除了他以外,其他的都很小。
  • 查询结果明显小于源数据。换句话说,数据经过过滤或聚合,因此结果适合于单个服务器的RAM中

很容易可以看出,OLAP场景与其他通常业务场景(例如,OLTP或K/V)有很大的不同, 因此想要使用OLTP或Key-Value数据库去高效的处理分析查询场景,并不是非常完美的适用方案。例如,使用OLAP数据库去处理分析请求通常要优于使用MongoDB或Redis去处理分析请求。

列式数据库更适合OLAP场景的原因

列式数据库更适合于OLAP场景(对于大多数查询而言,处理速度至少提高了100倍),下面详细解释了原因(通过图片更有利于直观理解):

行式

Row oriented

列式

Column oriented

看到差别了么?下面将详细介绍为什么会发生这种情况。

输入/输出

  1. 针对分析类查询,通常只需要读取表的一小部分列。在列式数据库中你可以只读取你需要的数据。例如,如果只需要读取100列中的5列,这将帮助你最少减少20倍的I/O消耗。
  2. 由于数据总是打包成批量读取的,所以压缩是非常容易的。同时数据按列分别存储这也更容易压缩。这进一步降低了I/O的体积。
  3. 由于I/O的降低,这将帮助更多的数据被系统缓存。

例如,查询«统计每个广告平台的记录数量»需要读取«广告平台ID»这一列,它在未压缩的情况下需要1个字节进行存储。如果大部分流量不是来自广告平台,那么这一列至少可以以十倍的压缩率被压缩。当采用快速压缩算法,它的解压速度最少在十亿字节(未压缩数据)每秒。换句话说,这个查询可以在单个服务器上以每秒大约几十亿行的速度进行处理。这实际上是当前实现的速度。

CPU

由于执行一个查询需要处理大量的行,因此在整个向量上执行所有操作将比在每一行上执行所有操作更加高效。同时这将有助于实现一个几乎没有调用成本的查询引擎。如果你不这样做,使用任何一个机械硬盘,查询引擎都不可避免的停止CPU进行等待。所以,在数据按列存储并且按列执行是很有意义的。

有两种方法可以做到这一点:

  1. 向量引擎:所有的操作都是为向量而不是为单个值编写的。这意味着多个操作之间的不再需要频繁的调用,并且调用的成本基本可以忽略不计。操作代码包含一个优化的内部循环。

  2. 代码生成:生成一段代码,包含查询中的所有操作。

这是不应该在一个通用数据库中实现的,因为这在运行简单查询时是没有意义的。但是也有例外,例如,MemSQL使用代码生成来减少处理SQL查询的延迟(只是为了比较,分析型数据库通常需要优化的是吞吐而不是延迟)。

请注意,为了提高CPU效率,查询语言必须是声明型的(SQL或MDX), 或者至少一个向量(J,K)。 查询应该只包含隐式循环,允许进行优化。

ClickHouse的特性

真正的列式数据库管理系统

在一个真正的列式数据库管理系统中,除了数据本身外不应该存在其他额外的数据。这意味着为了避免在值旁边存储它们的长度«number»,你必须支持固定长度数值类型。例如,10亿个UInt8类型的数据在未压缩的情况下大约消耗1GB左右的空间,如果不是这样的话,这将对CPU的使用产生强烈影响。即使是在未压缩的情况下,紧凑的存储数据也是非常重要的,因为解压缩的速度主要取决于未压缩数据的大小。

这是非常值得注意的,因为在一些其他系统中也可以将不同的列分别进行存储,但由于对其他场景进行的优化,使其无法有效的处理分析查询。例如: HBase,BigTable,Cassandra,HyperTable。在这些系统中,你可以得到每秒数十万的吞吐能力,但是无法得到每秒几亿行的吞吐能力。

需要说明的是,ClickHouse不单单是一个数据库, 它是一个数据库管理系统。因为它允许在运行时创建表和数据库、加载数据和运行查询,而无需重新配置或重启服务。

数据压缩

在一些列式数据库管理系统中(例如:InfiniDB CE 和 MonetDB) 并没有使用数据压缩。但是, 若想达到比较优异的性能,数据压缩确实起到了至关重要的作用。

除了在磁盘空间和CPU消耗之间进行不同权衡的高效通用压缩编解码器之外,ClickHouse还提供针对特定类型数据的专用编解码器,这使得ClickHouse能够与更小的数据库(如时间序列数据库)竞争并超越它们。

数据的磁盘存储

许多的列式数据库(如 SAP HANA, Google PowerDrill)只能在内存中工作,这种方式会造成比实际更多的设备预算。

ClickHouse被设计用于工作在传统磁盘上的系统,它提供每GB更低的存储成本,但如果可以使用SSD和内存,它也会合理的利用这些资源。

多核心并行处理

ClickHouse会使用服务器上一切可用的资源,从而以最自然的方式并行处理大型查询。

多服务器分布式处理

上面提到的列式数据库管理系统中,几乎没有一个支持分布式的查询处理。 在ClickHouse中,数据可以保存在不同的shard上,每一个shard都由一组用于容错的replica组成,查询可以并行地在所有shard上进行处理。这些对用户来说是透明的

支持SQL

ClickHouse支持一种基于SQL的声明式查询语言,它在许多情况下与ANSI SQL标准相同。

支持的查询GROUP BYORDER BYFROMJOININ以及非相关子查询。

相关(依赖性)子查询和窗口函数暂不受支持,但将来会被实现。

向量引擎

为了高效的使用CPU,数据不仅仅按列存储,同时还按向量(列的一部分)进行处理,这样可以更加高效地使用CPU。

实时的数据更新

ClickHouse支持在表中定义主键。为了使查询能够快速在主键中进行范围查找,数据总是以增量的方式有序的存储在MergeTree中。因此,数据可以持续不断地高效的写入到表中,并且写入的过程中不会存在任何加锁的行为。

索引

按照主键对数据进行排序,这将帮助ClickHouse在几十毫秒以内完成对数据特定值或范围的查找。

适合在线查询

在线查询意味着在没有对数据做任何预处理的情况下以极低的延迟处理查询并将结果加载到用户的页面中。

支持近似计算

ClickHouse提供各种各样在允许牺牲数据精度的情况下对查询进行加速的方法:

  1. 用于近似计算的各类聚合函数,如:distinct values, medians, quantiles
  2. 基于数据的部分样本进行近似查询。这时,仅会从磁盘检索少部分比例的数据。
  3. 不使用全部的聚合条件,通过随机选择有限个数据聚合条件进行聚合。这在数据聚合条件满足某些分布条件下,在提供相当准确的聚合结果的同时降低了计算资源的使用。

Adaptive Join Algorithm

ClickHouse支持自定义JOIN多个表,它更倾向于散列连接算法,如果有多个大表,则使用合并-连接算法

支持数据复制和数据完整性

ClickHouse使用异步的多主复制技术。当数据被写入任何一个可用副本后,系统会在后台将数据分发给其他副本,以保证系统在不同副本上保持相同的数据。在大多数情况下ClickHouse能在故障后自动恢复,在一些少数的复杂情况下需要手动恢复。

更多信息,参见 数据复制

角色的访问控制

ClickHouse使用SQL查询实现用户帐户管理,并允许角色的访问控制,类似于ANSI SQL标准和流行的关系数据库管理系统。

限制

  1. 没有完整的事务支持。
  2. 缺少高频率,低延迟的修改或删除已存在数据的能力。仅能用于批量删除或修改数据,但这符合 GDPR
  3. 稀疏索引使得ClickHouse不适合通过其键检索单行的点查询。

 

性能

根据Yandex的内部测试结果,ClickHouse表现出了比同类可比较产品更优的性能。你可以在 这里 查看具体的测试结果。

许多其他的测试也证实这一点。你可以使用互联网搜索到它们,或者你也可以从 我们收集的部分相关连接 中查看。

单个大查询的吞吐量

吞吐量可以使用每秒处理的行数或每秒处理的字节数来衡量。如果数据被放置在page cache中,则一个不太复杂的查询在单个服务器上大约能够以2-10GB/s(未压缩)的速度进行处理(对于简单的查询,速度可以达到30GB/s)。如果数据没有在page cache中的话,那么速度将取决于你的磁盘系统和数据的压缩率。例如,如果一个磁盘允许以400MB/s的速度读取数据,并且数据压缩率是3,则数据的处理速度为1.2GB/s。这意味着,如果你是在提取一个10字节的列,那么它的处理速度大约是1-2亿行每秒。

对于分布式处理,处理速度几乎是线性扩展的,但这受限于聚合或排序的结果不是那么大的情况下。

处理短查询的延迟时间

如果一个查询使用主键并且没有太多行(几十万)进行处理,并且没有查询太多的列,那么在数据被page cache缓存的情况下,它的延迟应该小于50毫秒(在最佳的情况下应该小于10毫秒)。 否则,延迟取决于数据的查找次数。如果你当前使用的是HDD,在数据没有加载的情况下,查询所需要的延迟可以通过以下公式计算得知: 查找时间(10 ms) * 查询的列的数量 * 查询的数据块的数量。

处理大量短查询的吞吐量

在相同的情况下,ClickHouse可以在单个服务器上每秒处理数百个查询(在最佳的情况下最多可以处理数千个)。但是由于这不适用于分析型场景。因此我们建议每秒最多查询100次。

数据的写入性能

我们建议每次写入不少于1000行的批量写入,或每秒不超过一个写入请求。当使用tab-separated格式将一份数据写入到MergeTree表中时,写入速度大约为50到200MB/s。如果您写入的数据每行为1Kb,那么写入的速度为50,000到200,000行每秒。如果您的行更小,那么写入速度将更高。为了提高写入性能,您可以使用多个INSERT进行并行写入,这将带来线性的性能提升。

ClickHouse历史

ClickHouse最初是为 YandexMetrica 世界第二大Web分析平台 而开发的。多年来一直作为该系统的核心组件被该系统持续使用着。目前为止,该系统在ClickHouse中有超过13万亿条记录,并且每天超过200多亿个事件被处理。它允许直接从原始数据中动态查询并生成报告。本文简要介绍了ClickHouse在其早期发展阶段的目标。

Yandex.Metrica基于用户定义的字段,对实时访问、连接会话,生成实时的统计报表。这种需求往往需要复杂聚合方式,比如对访问用户进行去重。构建报表的数据,是实时接收存储的新数据。

截至2014年4月,Yandex.Metrica每天跟踪大约120亿个事件(用户的点击和浏览)。为了可以创建自定义的报表,我们必须存储全部这些事件。同时,这些查询可能需要在几百毫秒内扫描数百万行的数据,或在几秒内扫描数亿行的数据。

Yandex.Metrica以及其他Yandex服务的使用案例

在Yandex.Metrica中,ClickHouse被用于多个场景中。 它的主要任务是使用原始数据在线的提供各种数据报告。它使用374台服务器的集群,存储了20.3万亿行的数据。在去除重复与副本数据的情况下,压缩后的数据达到了2PB。未压缩前(TSV格式)它大概有17PB。

ClickHouse还被使用在:

  • 存储来自Yandex.Metrica的会话重放数据。
  • 处理中间数据
  • 与Analytics一起构建全球报表。
  • 为调试Yandex.Metrica引擎运行查询
  • 分析来自API和用户界面的日志数据

ClickHouse在其他Yandex服务中至少有12个安装:search verticals, Market, Direct, business analytics, mobile development, AdFox, personal services等。

聚合与非聚合数据

有一种流行的观点认为,想要有效的计算统计数据,必须要聚合数据,因为聚合将降低数据量。

但是数据聚合是一个有诸多限制的解决方案,例如:

  • 你必须提前知道用户定义的报表的字段列表
  • 用户无法自定义报表
  • 当聚合条件过多时,可能不会减少数据,聚合是无用的。
  • 存在大量报表时,有太多的聚合变化(组合爆炸)
  • 当聚合条件有非常大的基数时(如:url),数据量没有太大减少(少于两倍)
  • 聚合的数据量可能会增长而不是收缩
  • 用户不会查看我们为他生成的所有报告,大部分计算将是无用的
  • 各种聚合可能违背了数据的逻辑完整性

如果我们直接使用非聚合数据而不进行任何聚合时,我们的计算量可能是减少的。

然而,相对于聚合中很大一部分工作被离线完成,在线计算需要尽快的完成计算,因为用户在等待结果。

Yandex.Metrica 有一个专门用于聚合数据的系统,称为Metrage,它可以用作大部分报表。 从2009年开始,Yandex.Metrica还为非聚合数据使用专门的OLAP数据库,称为OLAPServer,它以前用于报表构建系统。 OLAPServer可以很好的工作在非聚合数据上,但是它有诸多限制,导致无法根据需要将其用于所有报表中。如,缺少对数据类型的支持(只支持数据),无法实时增量的更新数据(只能通过每天重写数据完成)。OLAPServer不是一个数据库管理系统,它只是一个数据库。

为了消除OLAPServer的这些局限性,解决所有报表使用非聚合数据的问题,我们开发了ClickHouse数据库管理系统。

安装

系统要求

ClickHouse可以在任何具有x86_64,AArch64或PowerPC64LE CPU架构的Linux,FreeBSD或Mac OS X上运行。

官方预构建的二进制文件通常针对x86_64进行编译,并利用SSE 4.2指令集,因此,除非另有说明,支持它的CPU使用将成为额外的系统需求。下面是检查当前CPU是否支持SSE 4.2的命令:

$ grep -q sse4_2 /proc/cpuinfo && echo "SSE 4.2 supported" || echo "SSE 4.2 not supported"

要在不支持SSE 4.2AArch64PowerPC64LE架构的处理器上运行ClickHouse,您应该通过适当的配置调整从源代码构建ClickHouse

由于macos不支持直接安装clickhouse,只能通过docker容器本地安装clickhouse。

一、安装Docker
两种安装方式:参考 MacOS Docker 安装 | 菜鸟教程

二、Docker镜像源配置
新版Docker的镜像配置为Preferences -> Docker Engine,在文本框中加入镜像地址:

"registry-mirrors": [
"https://6kx4zyno.mirror.aliyuncs.com"
]
如图:

 

 

 

三、docker下载clickhouse客户端
终端输入指令 $ docker pull yandex/clickhouse-client

四、docker下载clickhouse服务端
终端输入指令 $ docker pull yandex/clickhouse-server

五、启动Clickhouse
执行 $ docker network create ck_net ,创建用于clickhouse的网络给docker用

执行 $ docker network ls ,查看网络

可以看到 ck-net的网络已经创建成功

接下来运行clickhouse-server容器

$ docker run -d --name ck-server --network=ck_net --ulimit nofile=262144:262144 --volume=/Users/woxingwosu0100/Documents/clickhouse/some_clickhouse_database:/var/lib/clickhouse yandex/clickhouse-server

查看服务 $ docker ps

进入到这个容器中 $ docker exec -it ck-server /bin/bash

然后执行 $ clickhouse-client

进入容器成功,再执行 :) show databases; 即可查看数据库

 

执行 :) CREATE TABLE default.user_table(id UInt16, name String, age UInt16 ) ENGINE = TinyLog(); 创建数据表

 

 

 

执行 :) SELECT * FROM default.user_table; 查询表数据

关闭容器:执行 $ docker stop 后面加上容器ID

之后再启动这个容器的时候,执行$ docker start 加容器的ID 就可以了

如果想删除该容器,可执行 $ docker container rm 加容器的ID 就可以了

六、宿主机连接docker中的clickhouse
docker创建的clickhouse由于默认配置原因无法被宿主机直接访问,所以需要修改clickhouse的配置文件

配置 config.xml

修改配置有两种方式:

不挂载配置文件, 每次进入容器修改配置, 比较拉跨 (注意 这个镜像基于不完整的ubuntu, 需要手动装一下vim编辑器,$ apt-get update $ apt-get install vim -y)
先不挂载配置文件启动一个容器,执行 $ docker cp clickhouse-server:/etc/clickhouse-server/ /Users/woxingwosu0100/clickhouse/etc 将容器中的 /etc/clickhouse-server下的内容复制到宿主机, 修改配置文件/etc/clickhouse-server/config.xml 中 65行 注释去掉<listen_host>::</listen_host>,然后再基于这个复制过来的文件挂载配置文件也可以解决,执行 $ docker run -d --name ck-server --network=ck_net --ulimit nofile=262144:262144 -p 8123:8123 -p 9000:9000 -p 9009:9009 --volume=/Users/woxingwosu0100/Documents/clickhouse/some_clickhouse_database:/var/lib/clickhouse -v /Users/woxingwosu0100/Documents/clickhouse/etc/config.xml:/etc/clickhouse-server/config.xml yandex/clickhouse-server

ClickHouse教程

从本教程中可以获得什么?

通过学习本教程,您将了解如何设置一个简单的ClickHouse集群。它会很小,但是可以容错和扩展。然后,我们将使用其中一个示例数据集来填充数据并执行一些演示查询。

单节点设置

为了延迟演示分布式环境的复杂性,我们将首先在单个服务器或虚拟机上部署ClickHouse。ClickHouse通常是从debrpm包安装,但对于不支持它们的操作系统也有其他方法

例如,您选择deb安装包,执行:

{% include 'install/deb.sh' %}

在我们安装的软件中包含这些包:

  • clickhouse-client 包,包含clickhouse-client客户端,它是交互式ClickHouse控制台客户端。
  • clickhouse-common 包,包含一个ClickHouse可执行文件。
  • clickhouse-server 包,包含要作为服务端运行的ClickHouse配置文件。

服务器配置文件位于/etc/clickhouse-server/。在继续之前,请注意config.xml中的<path>元素。它决定了数据存储的位置,因此它应该位于磁盘容量的卷上;默认值是/var/lib/clickhouse/。如果你想调整配置,直接编辑config是不方便的。考虑到它可能会在将来的包更新中被重写。建议重写配置元素的方法是在配置中创建config.d文件夹,作为config.xml的重写方式。

你可能已经注意到了,clickhouse-server安装后不会自动启动。 它也不会在更新后自动重新启动。 您启动服务端的方式取决于您的初始系统,通常情况下是这样:

sudo service clickhouse-server start

sudo /etc/init.d/clickhouse-server start

服务端日志的默认位置是/var/log/clickhouse-server/。当服务端在日志中记录Ready for connections消息,即表示服务端已准备好处理客户端连接。

一旦clickhouse-server启动并运行,我们可以利用clickhouse-client连接到服务端,并运行一些测试查询,如SELECT "Hello, world!";.

Clickhouse-client的快速提示

导入示例数据集

现在是时候用一些示例数据填充我们的ClickHouse服务端。 在本教程中,我们将使用Yandex.Metrica的匿名数据,它是在ClickHouse成为开源之前作为生产环境运行的第一个服务(关于这一点的更多内容请参阅ClickHouse历史)。多种导入Yandex.Metrica数据集方法,为了本教程,我们将使用最现实的一个。

下载并提取表数据

curl https://datasets.clickhouse.com/hits/tsv/hits_v1.tsv.xz | unxz --threads=`nproc` > hits_v1.tsv
curl https://datasets.clickhouse.com/visits/tsv/visits_v1.tsv.xz | unxz --threads=`nproc` > visits_v1.tsv

提取的文件大小约为10GB。

创建表

与大多数数据库管理系统一样,ClickHouse在逻辑上将表分组为数据库。包含一个default数据库,但我们将创建一个新的数据库tutorial:

clickhouse-client --query "CREATE DATABASE IF NOT EXISTS tutorial"

与创建数据库相比,创建表的语法要复杂得多(请参阅参考资料. 一般CREATE TABLE声明必须指定三个关键的事情:

  1. 要创建的表的名称。
  2. 表结构,例如:列名和对应的数据类型
  3. 表引擎及其设置,这决定了对此表的查询操作是如何在物理层面执行的所有细节。

Yandex.Metrica是一个网络分析服务,样本数据集不包括其全部功能,因此只有两个表可以创建:

  • hits 表包含所有用户在服务所涵盖的所有网站上完成的每个操作。
  • visits 表包含预先构建的会话,而不是单个操作。

让我们看看并执行这些表的实际创建表查询:

CREATE TABLE tutorial.hits_v1
(
`WatchID` UInt64,
`JavaEnable` UInt8,
`Title` String,
`GoodEvent` Int16,
`EventTime` DateTime,
`EventDate` Date,
`CounterID` UInt32,
`ClientIP` UInt32,
`ClientIP6` FixedString(16),
`RegionID` UInt32,
`UserID` UInt64,
`CounterClass` Int8,
`OS` UInt8,
`UserAgent` UInt8,
`URL` String,
`Referer` String,
`URLDomain` String,
`RefererDomain` String,
`Refresh` UInt8,
`IsRobot` UInt8,
`RefererCategories` Array(UInt16),
`URLCategories` Array(UInt16),
`URLRegions` Array(UInt32),
`RefererRegions` Array(UInt32),
`ResolutionWidth` UInt16,
`ResolutionHeight` UInt16,
`ResolutionDepth` UInt8,
`FlashMajor` UInt8,
`FlashMinor` UInt8,
`FlashMinor2` String,
`NetMajor` UInt8,
`NetMinor` UInt8,
`UserAgentMajor` UInt16,
`UserAgentMinor` FixedString(2),
`CookieEnable` UInt8,
`JavascriptEnable` UInt8,
`IsMobile` UInt8,
`MobilePhone` UInt8,
`MobilePhoneModel` String,
`Params` String,
`IPNetworkID` UInt32,
`TraficSourceID` Int8,
`SearchEngineID` UInt16,
`SearchPhrase` String,
`AdvEngineID` UInt8,
`IsArtifical` UInt8,
`WindowClientWidth` UInt16,
`WindowClientHeight` UInt16,
`ClientTimeZone` Int16,
`ClientEventTime` DateTime,
`SilverlightVersion1` UInt8,
`SilverlightVersion2` UInt8,
`SilverlightVersion3` UInt32,
`SilverlightVersion4` UInt16,
`PageCharset` String,
`CodeVersion` UInt32,
`IsLink` UInt8,
`IsDownload` UInt8,
`IsNotBounce` UInt8,
`FUniqID` UInt64,
`HID` UInt32,
`IsOldCounter` UInt8,
`IsEvent` UInt8,
`IsParameter` UInt8,
`DontCountHits` UInt8,
`WithHash` UInt8,
`HitColor` FixedString(1),
`UTCEventTime`DateTime,
`Age` UInt8,
`Sex` UInt8,
`Income` UInt8,
`Interests` UInt16,
`Robotness` UInt8,
`GeneralInterests` Array(UInt16),
`RemoteIP` UInt32,
`RemoteIP6` FixedString(16),
`WindowName` Int32,
`OpenerName` Int32,
`HistoryLength` Int16,
`BrowserLanguage` FixedString(2),
`BrowserCountry` FixedString(2),
`SocialNetwork` String,
`SocialAction` String,
`HTTPError` UInt16,
`SendTiming` Int32,
`DNSTiming` Int32,
`ConnectTiming` Int32,
`ResponseStartTiming` Int32,
`ResponseEndTiming` Int32,
`FetchTiming` Int32,
`RedirectTiming` Int32,
`DOMInteractiveTiming` Int32,
`DOMContentLoadedTiming` Int32,
`DOMCompleteTiming` Int32,
`LoadEventStartTiming` Int32,
`LoadEventEndTiming` Int32,
`NSToDOMContentLoadedTiming` Int32,
`FirstPaintTiming` Int32,
`RedirectCount` Int8,
`SocialSourceNetworkID` UInt8,
`SocialSourcePage` String,
`ParamPrice` Int64,
`ParamOrderID` String,
`ParamCurrency` FixedString(3),
`ParamCurrencyID` UInt16,
`GoalsReached` Array(UInt32),
`OpenstatServiceName` String,
`OpenstatCampaignID` String,
`OpenstatAdID` String,
`OpenstatSourceID` String,
`UTMSource` String,
`UTMMedium` String,
`UTMCampaign` String,
`UTMContent` String,
`UTMTerm` String,
`FromTag` String,
`HasGCLID` UInt8,
`RefererHash` UInt64,
`URLHash` UInt64,
`CLID` UInt32,
`YCLID` UInt64,
`ShareService` String,
`ShareURL` String,
`ShareTitle` String,
`ParsedParams` Nested(
Key1 String,
Key2 String,
Key3 String,
Key4 String,
Key5 String,
ValueDouble Float64),
`IslandID` FixedString(16),
`RequestNum` UInt32,
`RequestTry` UInt8
)
ENGINE= MergeTree()
PARTITIONBY toYYYYMM(EventDate)
ORDERBY(CounterID, EventDate, intHash32(UserID))
SAMPLE BY intHash32(UserID)
CREATE TABLE tutorial.visits_v1
(
`CounterID` UInt32,
`StartDate` Date,
`Sign` Int8,
`IsNew` UInt8,
`VisitID` UInt64,
`UserID` UInt64,
`StartTime` DateTime,
`Duration` UInt32,
`UTCStartTime` DateTime,
`PageViews` Int32,
`Hits` Int32,
`IsBounce` UInt8,
`Referer` String,
`StartURL` String,
`RefererDomain` String,
`StartURLDomain` String,
`EndURL` String,
`LinkURL` String,
`IsDownload` UInt8,
`TraficSourceID` Int8,
`SearchEngineID` UInt16,
`SearchPhrase` String,
`AdvEngineID` UInt8,
`PlaceID` Int32,
`RefererCategories` Array(UInt16),
`URLCategories` Array(UInt16),
`URLRegions` Array(UInt32),
`RefererRegions` Array(UInt32),
`IsYandex` UInt8,
`GoalReachesDepth` Int32,
`GoalReachesURL` Int32,
`GoalReachesAny` Int32,
`SocialSourceNetworkID` UInt8,
`SocialSourcePage` String,
`MobilePhoneModel` String,
`ClientEventTime` DateTime,
`RegionID` UInt32,
`ClientIP` UInt32,
`ClientIP6` FixedString(16),
`RemoteIP` UInt32,
`RemoteIP6` FixedString(16),
`IPNetworkID` UInt32,
`SilverlightVersion3` UInt32,
`CodeVersion` UInt32,
`ResolutionWidth` UInt16,
`ResolutionHeight` UInt16,
`UserAgentMajor` UInt16,
`UserAgentMinor` UInt16,
`WindowClientWidth` UInt16,
`WindowClientHeight` UInt16,
`SilverlightVersion2` UInt8,
`SilverlightVersion4` UInt16,
`FlashVersion3` UInt16,
`FlashVersion4` UInt16,
`ClientTimeZone` Int16,
`OS` UInt8,
`UserAgent` UInt8,
`ResolutionDepth` UInt8,
`FlashMajor` UInt8,
`FlashMinor` UInt8,
`NetMajor` UInt8,
`NetMinor` UInt8,
`MobilePhone` UInt8,
`SilverlightVersion1` UInt8,
`Age` UInt8,
`Sex` UInt8,
`Income` UInt8,
`JavaEnable` UInt8,
`CookieEnable` UInt8,
`JavascriptEnable` UInt8,
`IsMobile` UInt8,
`BrowserLanguage` UInt16,
`BrowserCountry` UInt16,
`Interests` UInt16,
`Robotness` UInt8,
`GeneralInterests` Array(UInt16),
`Params` Array(String),
`Goals` Nested(
ID UInt32,
Serial UInt32,
EventTime DateTime,
Price Int64,
OrderID String,
CurrencyID UInt32),
`WatchIDs` Array(UInt64),
`ParamSumPrice` Int64,
`ParamCurrency` FixedString(3),
`ParamCurrencyID` UInt16,
`ClickLogID` UInt64,
`ClickEventID` Int32,
`ClickGoodEvent` Int32,
`ClickEventTime`DateTime,
`ClickPriorityID` Int32,
`ClickPhraseID` Int32,
`ClickPageID` Int32,
`ClickPlaceID` Int32,
`ClickTypeID` Int32,
`ClickResourceID` Int32,
`ClickCost` UInt32,
`ClickClientIP` UInt32,
`ClickDomainID` UInt32,
`ClickURL` String,
`ClickAttempt` UInt8,
`ClickOrderID` UInt32,
`ClickBannerID` UInt32,
`ClickMarketCategoryID` UInt32,
`ClickMarketPP` UInt32,
`ClickMarketCategoryName` String,
`ClickMarketPPName` String,
`ClickAWAPSCampaignName` String,
`ClickPageName` String,
`ClickTargetType` UInt16,
`ClickTargetPhraseID` UInt64,
`ClickContextType` UInt8,
`ClickSelectType` Int8,
`ClickOptions` String,
`ClickGroupBannerID` Int32,
`OpenstatServiceName` String,
`OpenstatCampaignID` String,
`OpenstatAdID` String,
`OpenstatSourceID` String,
`UTMSource` String,
`UTMMedium` String,
`UTMCampaign` String,
`UTMContent` String,
`UTMTerm` String,
`FromTag` String,
`HasGCLID` UInt8,
`FirstVisit`DateTime,
`PredLastVisit`Date,
`LastVisit`Date,
`TotalVisits` UInt32,
`TraficSource` Nested(
ID Int8,
SearchEngineID UInt16,
AdvEngineID UInt8,
PlaceID UInt16,
SocialSourceNetworkID UInt8,
Domain String,
SearchPhrase String,
SocialSourcePage String),
`Attendance` FixedString(16),
`CLID` UInt32,
`YCLID` UInt64,
`NormalizedRefererHash` UInt64,
`SearchPhraseHash` UInt64,
`RefererDomainHash` UInt64,
`NormalizedStartURLHash` UInt64,
`StartURLDomainHash` UInt64,
`NormalizedEndURLHash` UInt64,
`TopLevelDomain` UInt64,
`URLScheme` UInt64,
`OpenstatServiceNameHash` UInt64,
`OpenstatCampaignIDHash` UInt64,
`OpenstatAdIDHash` UInt64,
`OpenstatSourceIDHash` UInt64,
`UTMSourceHash` UInt64,
`UTMMediumHash` UInt64,
`UTMCampaignHash` UInt64,
`UTMContentHash` UInt64,
`UTMTermHash` UInt64,
`FromHash` UInt64,
`WebVisorEnabled` UInt8,
`WebVisorActivity` UInt32,
`ParsedParams` Nested(
Key1 String,
Key2 String,
Key3 String,
Key4 String,
Key5 String,
ValueDouble Float64),
`Market` Nested(
Type UInt8,
GoalID UInt32,
OrderID String,
OrderPrice Int64,
PP UInt32,
DirectPlaceID UInt32,
DirectOrderID UInt32,
DirectBannerID UInt32,
GoodID String,
GoodName String,
GoodQuantity Int32,
GoodPrice Int64),
`IslandID` FixedString(16)
)
ENGINE= CollapsingMergeTree(Sign)
PARTITIONBY toYYYYMM(StartDate)
ORDERBY(CounterID, StartDate, intHash32(UserID), VisitID)
SAMPLE BY intHash32(UserID)

您可以使用clickhouse-client的交互模式执行这些查询(只需在终端中启动它,而不需要提前指定查询)。或者如果你愿意,可以尝试一些替代接口

正如我们所看到的, hits_v1使用 MergeTree引擎,而visits_v1使用 Collapsing引擎。

导入数据

数据导入到ClickHouse是通过INSERT INTO方式完成的,查询类似许多SQL数据库。然而,数据通常是在一个提供支持序列化格式而不是VALUES子句(也支持)。

我们之前下载的文件是以制表符分隔的格式,所以这里是如何通过控制台客户端导入它们:

clickhouse-client --query "INSERT INTO tutorial.hits_v1 FORMAT TSV" --max_insert_block_size=100000 < hits_v1.tsv
clickhouse-client --query "INSERT INTO tutorial.visits_v1 FORMAT TSV" --max_insert_block_size=100000 < visits_v1.tsv

ClickHouse有很多要调整的设置在控制台客户端中指定它们的一种方法是通过参数,就像我们看到上面语句中的--max_insert_block_size。找出可用的设置、含义及其默认值的最简单方法是查询system.settings 表:

SELECT name, value, changed, description
FROM system.settings
WHERE name LIKE '%max_insert_b%'
FORMAT TSV

max_insert_block_size 1048576 0 "The maximum block size for insertion, if we control the creation of blocks for insertion."

您也可以OPTIMIZE导入后的表。使用MergeTree-family引擎配置的表总是在后台合并数据部分以优化数据存储(或至少检查是否有意义)。 这些查询强制表引擎立即进行存储优化,而不是稍后一段时间执行:

clickhouse-client --query "OPTIMIZE TABLE tutorial.hits_v1 FINAL"
clickhouse-client --query "OPTIMIZE TABLE tutorial.visits_v1 FINAL"

这些查询开始I/O和CPU密集型操作,所以如果表一直接收到新数据,最好不要管它,让合并在后台运行。

现在我们可以检查表导入是否成功:

clickhouse-client --query "SELECT COUNT(*) FROM tutorial.hits_v1"
clickhouse-client --query "SELECT COUNT(*) FROM tutorial.visits_v1"

查询示例

SELECT
StartURL AS URL,
AVG(Duration) AS AvgDuration
FROM tutorial.visits_v1
WHERE StartDate BETWEEN '2014-03-23' AND '2014-03-30'
GROUP BY URL
ORDER BY AvgDuration DESC
LIMIT 10
SELECT
sum(Sign) AS visits,
sumIf(Sign, has(Goals.ID, 1105530)) AS goal_visits,
(100. * goal_visits) / visits AS goal_percent
FROM tutorial.visits_v1
WHERE (CounterID = 912887) AND (toYYYYMM(StartDate) = 201403) AND (domain(StartURL) = 'yandex.ru')

集群部署

ClickHouse集群是一个同质集群。 设置步骤:

  1. 在群集的所有机器上安装ClickHouse服务端
  2. 在配置文件中设置集群配置
  3. 在每个实例上创建本地表
  4. 创建一个分布式表

分布式表实际上是一种view,映射到ClickHouse集群的本地表。 从分布式表中执行SELECT查询会使用集群所有分片的资源。 您可以为多个集群指定configs,并创建多个分布式表,为不同的集群提供视图。

具有三个分片,每个分片一个副本的集群的示例配置:

<remote_servers>
<perftest_3shards_1replicas>
<shard>
<replica>
<host>example-perftest01j.yandex.ru</host>
<port>9000</port>
</replica>
</shard>
<shard>
<replica>
<host>example-perftest02j.yandex.ru</host>
<port>9000</port>
</replica>
</shard>
<shard>
<replica>
<host>example-perftest03j.yandex.ru</host>
<port>9000</port>
</replica>
</shard>
</perftest_3shards_1replicas>
</remote_servers>

为了进一步演示,让我们使用和创建hits_v1表相同的CREATE TABLE语句创建一个新的本地表,但表名不同:

CREATE TABLE tutorial.hits_local (...) ENGINE = MergeTree() ...

创建提供集群本地表视图的分布式表:

CREATE TABLE tutorial.hits_all AS tutorial.hits_local
ENGINE = Distributed(perftest_3shards_1replicas, tutorial, hits_local, rand());

常见的做法是在集群的所有计算机上创建类似的分布式表。 它允许在群集的任何计算机上运行分布式查询。 还有一个替代选项可以使用以下方法为给定的SELECT查询创建临时分布式表远程表功能。

让我们运行INSERT SELECT将该表传播到多个服务器。

INSERT INTO tutorial.hits_all SELECT * FROM tutorial.hits_v1;

!!! warning "注意:" 这种方法不适合大型表的分片。 有一个单独的工具 clickhouse-copier 这可以重新分片任意大表。

正如您所期望的那样,如果计算量大的查询使用3台服务器而不是一个,则运行速度快N倍。

在这种情况下,我们使用了具有3个分片的集群,每个分片都包含一个副本。

为了在生产环境中提供弹性,我们建议每个分片应包含分布在多个可用区或数据中心(或至少机架)之间的2-3个副本。 请注意,ClickHouse支持无限数量的副本。

包含三个副本的一个分片集群的示例配置:

<remote_servers>
...
<perftest_1shards_3replicas>
<shard>
<replica>
<host>example-perftest01j.yandex.ru</host>
<port>9000</port>
</replica>
<replica>
<host>example-perftest02j.yandex.ru</host>
<port>9000</port>
</replica>
<replica>
<host>example-perftest03j.yandex.ru</host>
<port>9000</port>
</replica>
</shard>
</perftest_1shards_3replicas>
</remote_servers>

启用本机复制Zookeeper是必需的。 ClickHouse负责所有副本的数据一致性,并在失败后自动运行恢复过程。建议将ZooKeeper集群部署在单独的服务器上(其中没有其他进程,包括运行的ClickHouse)。

!!! note "注意" ZooKeeper不是一个严格的要求:在某些简单的情况下,您可以通过将数据写入应用程序代码中的所有副本来复制数据。 这种方法是不建议的,在这种情况下,ClickHouse将无法保证所有副本上的数据一致性。 因此需要由您的应用来保证这一点。

ZooKeeper位置在配置文件中指定:

<zookeeper>
<node>
<host>zoo01.yandex.ru</host>
<port>2181</port>
</node>
<node>
<host>zoo02.yandex.ru</host>
<port>2181</port>
</node>
<node>
<host>zoo03.yandex.ru</host>
<port>2181</port>
</node>
</zookeeper>

此外,我们需要设置宏来识别每个用于创建表的分片和副本:

<macros>
<shard>01</shard>
<replica>01</replica>
</macros>

如果在创建复制表时没有副本,则会实例化新的第一个副本。 如果已有实时副本,则新副本将克隆现有副本中的数据。 您可以选择首先创建所有复制的表,然后向其中插入数据。 另一种选择是创建一些副本,并在数据插入之后或期间添加其他副本。

CREATE TABLE tutorial.hits_replica (...)
ENGINE = ReplcatedMergeTree(
'/clickhouse_perftest/tables/{shard}/hits',
'{replica}'
)
...

在这里,我们使用ReplicatedMergeTree表引擎。 在参数中,我们指定包含分片和副本标识符的ZooKeeper路径。

INSERT INTO tutorial.hits_replica SELECT * FROM tutorial.hits_local;

复制在多主机模式下运行。数据可以加载到任何副本中,然后系统自动将其与其他实例同步。复制是异步的,因此在给定时刻,并非所有副本都可能包含最近插入的数据。至少应该有一个副本允许数据摄入。另一些则会在重新激活后同步数据并修复一致性。请注意,这种方法允许最近插入的数据丢失的可能性很低。

 

 

 

 

posted @ 2022-03-20 15:06  hanease  阅读(248)  评论(0编辑  收藏  举报