1.ClickHouse的基础介绍

一、ClickHouse基础介绍

1.1 介绍

​ Click Stream,Data Warehouse 点击流数据仓库

​ 在采集数据过程中,一次页面click,会产生一个event。 ----》 基于页面点击事件流,面向数据仓库进行OLAP分析

​ Clickhouse 是一个开源的,完全列式存储的,关系型数据库管理系统。面向数据仓库,主要用于联机分析处理(OLAP,Online Analytical Processing)。

1.2 优点

​ 关系型联机分析处理 ROLAP, On-Line Analytical Processing

​ 在线实时查询 https://clickhouse.tech/benchmark/dbms/

​ 完整的DBMS

	DDL:可以动态的创建、修改或者删除数据库、表和视图,而无须重启服务;

	DML:可以动态的查询、插入、修改、或者删除数据;

	权限控制:可以按照用户粒度设置数据库或者表的操作权限,保证数据的安全性;

	数据备份与恢复:提高数据备份导出与导入恢复机制;

	分布式管理:提供集群模式,能够自动管理多个数据库节点。

​ 完全列式存储(减少数据扫描范围,数据传输时大小) ----》 数据高效压缩

	假设一张数据表A中字段A1~A50,100行数据。
		按列查找:SELECT A1,A2,A3,A4,A5 FROM A;			有效的减少了查询时所需扫描的数据量;
		按行查找:数据库首先追行扫描,并且获取每行数据的所有50个字段,再从每一行的数据中返回A1~A5这五个字段.
		压缩前:abcdefghi_bcdefghi;
		压缩后:abcdefghi_(9,8)。
	压缩本质:按照一定步长对数据进行匹配扫描,当发现重复部分的时候进行编码转化。重复值越多,压缩比越高。
	数据压缩:上述(9,8)表示如果从下划线开始向前移动9个字节,会匹配到8个字节长度的重复项,即bcdefghi

​ 向量化执行引擎

消除程序中循环的优化
grep -q sse4_2 /proc/cpuinfo && echo "SSE 4.2 supported" || echo "SSE 4.2 not supported"

​ SQL查询

​ 多样化的数据库引擎和表引擎

不同的引擎,不同的数据存储位置和特点
数据库引擎:
Ordinary		默认引擎
Dictionary		字典引擎		此类数据库会自动为所有数据字典创建他们的数据表。
Memory			内存引擎		用于存放临时数据,此类数据库下的数据表只会停留在内存,重启数据丢失。
Lazy			日志引擎		此类数据库下只能使用log系列的表引擎。
Mysql			Mysql引擎		 此类数据库会自动拉取远端Mysql中的数据,并创建Mysql表引擎的数据表。
表引擎:
Memory、MergeTree

​ 多线程与分布式

​ 多主架构

	采用Multi-Master主从架构,节点角色对等,客户端访问任意节点得到效果相同,规避了单点故障

1.3 缺点

​ 不支持事务。

​ 不支持二级索引。

​ 元数据管理需要人工干预维护。

​ 尽量做批量的insert操作。

​ 支持有限操作系统。

​ 不支持典型K/V存储

​ 并发查询资源控制不好处理

1.4 适用场景

​ 适用于商业智能领域(BI),应用于广告流量、web、APP流量、物联网等其他领域。

​ 多主架构模式(Multi-Master),适用于多数据中心,异地多活的场景

1.5 不适用场景

​ 不支持事务 ----》 OLTP事务性操作场景

​ 不擅长根据主键按行粒度进行查询(支持) ----》 不应该把CK当作Key-Value数据库使用

​ 不擅长按行删除数据(支持)

二、ClickHouse架构设计

1.ClickHouse的基础介绍_第1张图片

  1. Column与Field

Column和Field是ClickHouse数据最基础的映射单元。作为一款百分之百的列式存储数据库,ClickHouse按列存储数据,内存中的一列数据由一个Column对象表示。Column对象分为接口和实现两个部分,在IColumn接口对象中,定义了对数据进行各种关系运算的方法,例如插入数据的insertRangeFrom和insertFrom方法、用于分页的cut,以及用于过滤的filter方法等。而这些方法的具体实现对象则根据数据类型的不同,由相应的对象实现,例如ColumnString、ColumnArray和ColumnTuple等。在大多数场合,ClickHouse都会以整列的方式操作数据,但凡事也有例外。如果需要操作单个具体的数值 ( 也就是单列中的一行数据 ),则需要使用Field对象,Field对象代表一个单值。与Column对象的泛化设计思路不同,Field对象使用了聚合的设计模式。在Field对象内部聚合了Null、UInt64、String和Array等13种数据类型及相应的处理逻辑。

  1. DataType

数据的序列化和反序列化工作由DataType负责。IDataType接口定义了许多正反序列化的方法,它们成对出现,例如serializeBinary和deserializeBinary、serializeTextJSON和deserializeTextJSON等,涵盖了常用的二进制、文本、JSON、XML、CSV和Protobuf等多种格式类型。IDataType也使用了泛化的设计模式,具体方法的实现逻辑由对应数据类型的实例承载,例如DataTypeString、DataTypeArray及DataTypeTuple等。

DataType虽然负责序列化相关工作,但它并不直接负责数据的读取,而是转由从Column或Field对象获取。在DataType的实现类中,聚合了相应数据类型的Column对象和Field对象。例如,DataTypeString会引用字符串类型的ColumnString,而DataTypeArray则会引用数组类型的ColumnArray,以此类推。

  1. Block与Block流

ClickHouse内部的数据操作是面向Block对象进行的,并且采用了流的形式。虽然Column和Filed组成了数据的基本映射单元,但对应到实际操作,它们还缺少了一些必要的信息,比如数据的类型及列的名称。于是ClickHouse设计了Block对象,Block对象可以看作数据表的子集。Block对象的本质是由数据对象、数据类型和列名称组成的三元组,即Column、DataType及列名称字符串。Column提供了数据的读取能力,而DataType知道如何正反序列化,所以Block在这些对象的基础之上实现了进一步的抽象和封装,从而简化了整个使用的过程,仅通过Block对象就能完成一系列的数据操作。在具体的实现过程中,Block并没有直接聚合Column和DataType对象,而是通过ColumnWithTypeAndName对象进行间接引用。

有了Block对象这一层封装之后,对Block流的设计就是水到渠成的事情了。流操作有两组顶层接口:IBlockInputStream负责数据的读取和关系运算,IBlockOutputStream负责将数据输出到下一环节。Block流也使用了泛化的设计模式,对数据的各种操作最终都会转换成其中一种流的实现。IBlockInputStream接口定义了读取数据的若干个read虚方法,而具体的实现逻辑则交由它的实现类来填充。

IBlockInputStream接口总共有60多个实现类,它们涵盖了ClickHouse数据摄取的方方面面。这些实现类大致可以分为三类:第一类用于处理数据定义的DDL操作,例如DDLQueryStatusInputStream等;第二类用于处理关系运算的相关操作,例如LimitBlockInput-Stream、JoinBlockInputStream及AggregatingBlockInputStream等;第三类则是与表引擎呼应,每一种表引擎都拥有与之对应的BlockInputStream实现,例如MergeTreeBaseSelect-BlockInputStream ( MergeTree表引擎 )、TinyLogBlockInputStream ( TinyLog表引擎 ) 及KafkaBlockInputStream ( Kafka表引擎 ) 等。

IBlockOutputStream的设计与IBlockInputStream如出一辙。IBlockOutputStream接口同样也定义了若干写入数据的write虚方法。它的实现类比IBlockInputStream要少许多,一共只有20多种。这些实现类基本用于表引擎的相关处理,负责将数据写入下一环节或者最终目的地,例如MergeTreeBlockOutputStream 、TinyLogBlockOutputStream及StorageFileBlock-OutputStream等。

  1. Table

在数据表的底层设计中并没有所谓的Table对象,它直接使用IStorage接口指代数据表。表引擎是ClickHouse的一个显著特性,不同的表引擎由不同的子类实现,例如IStorageSystemOneBlock ( 系统表 )、StorageMergeTree ( 合并树表引擎 ) 和StorageTinyLog ( 日志表引擎 ) 等。IStorage接口定义了DDL ( 如ALTER、RENAME、OPTIMIZE和DROP等 ) 、read和write方法,它们分别负责数据的定义、查询与写入。在数据查询时,IStorage负责根据AST查询语句的指示要求,返回指定列的原始数据。后续对数据的进一步加工、计算和过滤,则会统一交由Interpreter解释器对象处理。对Table发起的一次操作通常都会经历这样的过程,接收AST查询语句,根据AST返回指定列的数据,之后再将数据交由Interpreter做进一步处理。

  1. Parser与Interpreter

Parser和Interpreter是非常重要的两组接口:Parser分析器负责创建AST对象;而Interpreter解释器则负责解释AST,并进一步创建查询的执行管道。它们与IStorage一起,串联起了整个数据查询的过程。Parser分析器可以将一条SQL语句以递归下降的方法解析成AST语法树的形式。不同的SQL语句,会经由不同的Parser实现类解析。例如,有负责解析DDL查询语句的ParserRenameQuery、ParserDropQuery和ParserAlterQuery解析器,也有负责解析INSERT语句的ParserInsertQuery解析器,还有负责SELECT语句的ParserSelectQuery等。

Interpreter解释器的作用就像Service服务层一样,起到串联整个查询过程的作用,它会根据解释器的类型,聚合它所需要的资源。首先它会解析AST对象;然后执行"业务逻辑" ( 例如分支判断、设置参数、调用接口等 );最终返回IBlock对象,以线程的形式建立起一个查询执行管道。

  1. Functions 与Aggregate Functions

ClickHouse主要提供两类函数—普通函数和聚合函数。普通函数由IFunction接口定义,拥有数十种函数实现,例如FunctionFormatDateTime、FunctionSubstring等。除了一些常见的函数 ( 诸如四则运算、日期转换等 ) 之外,也不乏一些非常实用的函数,例如网址提取函数、IP地址脱敏函数等。普通函数是没有状态的,函数效果作用于每行数据之上。当然,在函数具体执行的过程中,并不会一行一行地运算,而是采用向量化的方式直接作用于一整列数据。

聚合函数由IAggregateFunction接口定义,相比无状态的普通函数,聚合函数是有状态的。以COUNT聚合函数为例,其AggregateFunctionCount的状态使用整型UInt64记录。聚合函数的状态支持序列化与反序列化,所以能够在分布式节点之间进行传输,以实现增量计算。

  1. Cluster与Replication

ClickHouse的集群由分片 ( Shard ) 组成,而每个分片又通过副本 ( Replica ) 组成。这种分层的概念,在一些流行的分布式系统中十分普遍。例如,在Elasticsearch的概念中,一个索引由分片和副本组成,副本可以看作一种特殊的分片。如果一个索引由5个分片组成,副本的基数是1,那么这个索引一共会拥有10个分片 ( 每1个分片对应1个副本 )。

	为什么ClickHouse如此之快?
		0.c++,c语言和硬件交互优势
		1.采用列式存储,
		4.方便实时的数据结构 MergeTree
		2.使用了向量化引擎
		3.软件架构设计采用自底向上方式。				追求自底向上、追求极致的设计思路

			硬件---》算法---》特殊优化---》版本发布

硬件:

在这里插入图片描述

	ClickHouse会在内存中GROUP BY,并且使用HashTable装载数据。

	在意CPU L3级别缓存,因为一次L3级别缓存失效会带来70~100ns的延迟。意味着单核CPU上,浪费4000万次/秒的运算;32线程的CPU上,浪费5亿次/秒的运算。ClickHouse在基准查询中能做到1.75亿次/秒的数据扫描性能。

算法:

	对于常量,使用了Volnitsky算法;对于非常量,使用CPU的向量化执行SIMD,暴力优化;正则匹配使用re2和hyperscan算法。

特殊优化:

	针对同一场景不同状况,选择使用不同的实现方式。

		例如去重计数uniqCombined函数,会根据数据量的不同选择不同的算法:

			当数据量较小的时候,会选择Array保存;

			当数据量中等的时候,会选择HashSet;

			当数据量很大的时候,会使用HyperLogLog算法

		对于数据结构比较清晰的场景,会通过代码生成技术实现循环展开,以减少循环次数。

版本发布:

	基本每个月都能发布一个版本,意味着拥有一个持续验证,持续改进的机制。

三、客户端访问接口

3.1 ClickHouse的底层访问接口

支持TCP和HTTP两种协议。

TCP协议拥有更好的性能,其端口为9000,主要用于集群间内部通信及CLI(Command Line Interface,命令行接口)客户端;

CLI两种执行模式:

1.交互式执行			clickhouse-client -h clickhouse-11 --port 9000

2.非交互式执行		clickhouse-client --query

标准输入:cat /root/test.tsv |clickhouse-client --query "INSERT INTO test FORMAT TSV"

标准输出:clickhouse-client --query="select * from test" > /root/test.csv

默认情况下,clickhouse-client后面只能运行一条SQL语句,执行多条情况下:

clickhouse-client -h clickhouse-1 --port 9000 --multiquery --query="select1;select 2;select 3;"

HTTP协议拥有更好的兼容性,其端口为8123,可以通过REST服务的形式被广泛用于JAVA,Python等编程语言的客户端。

更推荐通过CLI或者JDBC这些封装接口连接,更加简单易用。

clickhouse-client参数

--host / -h 		:服务器地址						  config.xml中::1

    															   127.0.0.1

--port				:服务器TCP端口,默认9000			config.xml中9000

--user/ - u			:登录的用户名,默认default		

--password			:登录的密码,默认为空				   users.xml中

--database / -d		:登录的数据库,默认为default		   config.xml中default

--query / -q		:只能非交互查询时使用,指定SQL语句

--multiquery / -n	:在非交互式查询中,允许一次运行多个SQL语句

--time / -t 		:在非交互式执行中,会打印每条SQL执行时间

clickhouse-client -t -n -q “select RequestNum,RequestTry from test.hit limit 1000000,10;select count(*) from test.hit;select RequestNum,RequestTry from test.hit limit 100000,10;”

非交互式导入导出数据:

https://clickhouse.tech/docs/zh/getting-started/example-datasets/metrica/

导入数据:

unxz hits_v1.tsv.xz 

clickhouse-client --query "create database if not exists test"

clickhouse-client --query "create table test.hit ( 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() PARTITION BY toYYYYMM(EventDate) ORDER BY (CounterID, EventDate, intHash32(UserID)) SAMPLE BY intHash32(UserID) SETTINGS index_granularity = 8192"

cat hits_v1.tsv | clickhouse-client --query "insert into test.hit FORMAT TSV " --max_insert_block_size=100000

clickhouse-client --query "optimize table test.hit final"

clickhouse-client --query "select count(*) from test.hit"

导出数据:

clickhouse-client --query "select * from test.hit" >> /root/hit.tsv

速度之快:

SELECT count(*) FROM test.hit

┌─count()─┐

│ 8873898 │

└─────────┘

1 rows in set. Elapsed: 0.002 sec. 


SELECT 

    RequestNum, 

    RequestTry

FROM test.hit

LIMIT 1000000, 10

┌─RequestNum─┬─RequestTry─┐

│        240 │          1 │

│          5 │          0 │

│          4 │          1 │

│       1188 │          0 │

│       1829 │          0 │

│       1229 │          0 │

│       1508 │          0 │

│       1348 │          0 │

│       1418 │          2 │

│       1988 │          0 │

└────────────┴────────────┘

10 rows in set. Elapsed: 0.007 sec. Processed 1.57 million rows, 7.86 MB (223.91 million rows/s., 1.12 GB/s.) 

3.2 内置实用工具

clickhouse-local 单机版 需要指定数据源+stdin标准输入+非交互式执行+只能使用File表引擎

clickhouse-benchmark SQL语句性能测试 自动运行SQL查询,并生成相应运行指标报告

clickhouse-benchmark参数:

-i / --iterations			SQL语句查询执行的次数,默认值为0

-c / --concurrency			同时执行查询的并发数,默认值是1

-r / --randomize			再执行多条SQL语句的时候,按照随机顺序执行,

-h / --host					服务端地址,默认值localhost;对比测试时,需要指定两个服务端

​ SQL语句性能测试

echo "select * from test.hit limit 1000000,10" |clickhouse-benchmark -i 5 

Loaded 1 queries.

Queries executed: 1.

localhost:9000, queries 1, QPS: 1.121, RPS: 1203315.971, MiB/s: 1131.422, result RPS: 11.213, result MiB/s: 0.011.

0.000%		0.892 sec.	

10.000%		0.892 sec.	

20.000%		0.892 sec.	

30.000%		0.892 sec.	

40.000%		0.892 sec.	

50.000%		0.892 sec.	

60.000%		0.892 sec.	

70.000%		0.892 sec.	

80.000%		0.892 sec.	

90.000%		0.892 sec.	

95.000%		0.892 sec.	

99.000%		0.892 sec.	

99.900%		0.892 sec.	

99.990%		0.892 sec.	

Queries executed: 2.

localhost:9000, queries 1, QPS: 1.651, RPS: 1772268.853, MiB/s: 1673.131, result RPS: 16.515, result MiB/s: 0.015.

0.000%		0.606 sec.	

10.000%		0.606 sec.	

20.000%		0.606 sec.	

30.000%		0.606 sec.	

40.000%		0.606 sec.	

50.000%		0.606 sec.	

60.000%		0.606 sec.	

70.000%		0.606 sec.	

80.000%		0.606 sec.	

90.000%		0.606 sec.	

95.000%		0.606 sec.	

99.000%		0.606 sec.	

99.900%		0.606 sec.	

99.990%		0.606 sec.	

Queries executed: 5.

localhost:9000, queries 5, QPS: 1.183, RPS: 1257808.054, MiB/s: 1184.972, result RPS: 11.829, result MiB/s: 0.012.

0.000%		0.606 sec.	

10.000%		0.651 sec.	

20.000%		0.696 sec.	

30.000%		0.753 sec.	

40.000%		0.823 sec.	

50.000%		0.892 sec.	

60.000%		0.894 sec.	

70.000%		0.897 sec.	

80.000%		0.941 sec.	

90.000%		1.027 sec.	

95.000%		1.070 sec.	

99.000%		1.104 sec.	

99.900%		1.112 sec.	

99.990%		1.113 sec.	

你可能感兴趣的:(数据库)