ClickHouse

ClickHouse

软件介绍

什么是ClickHouse

ClickHouse 是俄罗斯的 Yandex 于2016年开源的列式存储数据库管理系统(DBMS),主要用于在线分析处理查询(OLAP),能够使用SQL查询,实时生成分析数据报告。

特点

  • 开源的列存储数据库管理系统,支持线性扩展,简单方便,高可靠性
  • 容错跑分快:比Vertica快5倍,比Hive快279倍,比MySQL快800倍,其可处理的数据级别已达到10亿级别
  • 功能多:支持数据统计分析各种场景,支持类SQL查询,异地复制部署

优点

真正的面向列的DBMS

数据压缩

磁盘存储数据

多核并行处理

在多个服务器上分布式处理

支持SQL

向量化引擎

实时数据更新

支持近似计算

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

缺点

没有完整的事务支持,不支持Transaction想快就别Transaction

缺少完整Update/Delete操作,缺少高频率、低延迟的修改或删除已存在数据的能力,仅用于批量删除或修改数据。

聚合结果必须小于一台机器的内存大小

支持有限操作系统,正在慢慢完善

不适合Key-value存储,不支持Blob等文档型数据库

系统架构

ClickHouse 是一个真正的列式数据库管理系统(DBMS)。在 ClickHouse 中,数据始终是按列存储的,包括矢量(向量或列块)执行的过程

系统架构

**field:**列中某一个值

column:—列数据

DataType:描述了列的数据类型

Block:属于column datatype列名的组合体,表操作的对象是Block

BlockStream:负责块数据的读取和写出

Format:向客户端展示数据的方式

数据读写IO:有一个缓冲区负责数据的读写

数据表table:多个列的一个集合体读取数据的时候以表为单位进行操作操作的时候以BlockStream操作Block

解析器Parser:对Sql语句进行解析

解释器Interpreter:解析SQL语句

函数Functions:提供两类函数—普通函数(Functions)和聚合函数(Aggregate Functions)

普通函数Functions:普通函数不会改变行数 - 它们的执行看起来就像是独立地处理每一行数据

聚合函数Aggregate Functions:相比无状态的普通函数,聚合函数是状态函数。它们将传入的值激活到某个状态,并允许你从该状态获取结果。聚合状态可以被序列化和反序列化,可以在分布式查询执行期间通过网络传递或者在内存不够的时候将其写到硬盘。

Cluster与Replication: ClickHouse的集群由分片 ( Shard ) 组成,而每个分片又通过副本 ( Replica ) 组成。

2.1 Column与Field

Column和Field是ClickHouse数据最基础的映射单元.

内存中的一列数据由一个Column对象表示.

2.2 数据类型DataType

IDataType 负责序列化和反序列化:读写二进制或文本形式的列或单个值构成的块。 IDataType
直接与表的数据类型相对应。
IDataType 与 IColumn 之间的关联并不大。不同的数据类型在内存中能够用相同的 IColumn 实
现来表示。
IDataType 仅存储元数据。
数据的序列化和反序列化工作由DataType负责。IDataType接口定义了许多正反序列化的方法,它
们成对出现。IDataType也使用了泛化的设计模式,具体方法的实现逻辑由对应数据类型的实例承
载。
DataType虽然负责序列化相关工作,但它并不直接负责数据的读取,而是转由从Column或Field对
象获取。

2.3 块Block

Block 是表示内存中表的子集(chunk)的容器,是由三元组: (IColumn, IDataType, 列名)
构成的集合。
ClickHouse内部的数据操作是面向Block对象进行的,并且采用了流的形式。Block对象可以看作数
据表的子集。
Block并没有直接聚合Column和DataType对象,而是通过ColumnWithTypeAndName对象进行间
接引用。

2.4 块流BlockStreams

块流用于处理数据。我们可以使用块流从某个地方读取数据,执行数据转换,或将数据写到某个地
方。
Block流操作有两组顶层接口:
IBlockInputStream负责数据的读取和关系运算, IBlockInputStream 具有 read 方法,其
能够在数据可用时获取下一个块。
IBlockOutputStream负责将数据输出到下一环节。 IBlockOutputStream 具有 write 方
法,其能够将块写到某处。
IBlockInputStream接口总共有60多个实现类,这些实现类大致可以分为三类:
第一类用于处理数据定义的DDL操作
第二类用于处理关系运算的相关操作
第三类则是与表引擎呼应,每一种表引擎都拥有与之对应的BlockInputStream实现
IBlockOutputStream的设计与IBlockInputStream如出一辙。这些实现类基本用于表引擎的相关处
理,负责将数据写入下一环节或者最终目的地。

2.5 Formats格式

对于面向字节的输入输出,有 ReadBuffer 和 WriteBuffer 这两个抽象类。
ReadBuffer 和 WriteBuffer 由一个连续的缓冲区和指向缓冲区中某个位置的一个指针组成。
ReadBuffer 和 WriteBuffer 的实现用于处理文件、文件描述符和网络套接字(socket),也用
于实现压缩和其它用途。

2.6 数据读写I/O

在数据表的底层设计中并没有所谓的Table对象
表由 IStorage 接口表示。该接口的不同实现对应不同的表引擎。
表引擎是ClickHouse的一个显著特性,不同的表引擎由不同的子类实现。
IStorage 中最重要的方法是 read 和 write ,除此之外还有 alter 、 rename 和 drop 等方法。
表的 read 方法能够返回多个 IBlockInputStream 对象以允许并行处理数据。多个块输入流能
够从一个表中并行读取。
AST 查询被传递给 read 方法,表引擎可以使用它来判断是否能够使用索引,从而从表中读取更少的数据。

2.7 数据表Table

在数据表的底层设计中并没有所谓的Table对象
表由 IStorage 接口表示。该接口的不同实现对应不同的表引擎。
表引擎是ClickHouse的一个显著特性,不同的表引擎由不同的子类实现。
IStorage 中最重要的方法是 read 和 write ,除此之外还有 alter 、 rename 和 drop 等方
法。
表的 read 方法能够返回多个 IBlockInputStream 对象以允许并行处理数据。多个块输入流能
够从一个表中并行读取。
AST 查询被传递给 read 方法,表引擎可以使用它来判断是否能够使用索引,从而从表中读取更少
的数据

2.8 解析器Parser

查询由一个手写递归下降解析器解析。比如, ParserSelectQuery 只是针对查询的不同部分递归
地调用下层解析器。
解析器创建 AST 。 AST 由节点表示,节点是 IAST 的实例。

2.9 解释器Interpreter

解释器负责从 AST 创建查询执行流水线。
简单的解释器,如 InterpreterExistsQuery 和 InterpreterDropQuery
复杂的解释器,如 InterpreterSelectQuery 。
查询执行流水线由块输入或输出流组成。
SELECT 查询的解释结果是从 FROM 字句的结果集中读取数据的 IBlockInputStream ;
INSERT 查询的结果是写入需要插入的数据的 IBlockOutputStream ;
Parser分析器可以将一条SQL语句以递归下降的方法解析成AST语法树的形式。不同的SQL语句,
会经由不同的Parser实现类解析。

2.10 函数

普通函数不会改变行数 - 它们的执行看起来就像是独立地处理每一行数据。
实际上,函数不会作用于一个单独的行上,而是作用在以 Block 为单位的数据上,以实现向量查
询执行。
普通函数由IFunction接口定义,拥有数十种函数实现,采用向量化的方式直接作用于一整列数
据。

聚合函数是状态函数。它们将传入的值激活到某个状态,并允许你从该状态获取结果。
聚合函数由IAggregateFunction接口定义,相比无状态的普通函数,聚合函数是有状态的。
以COUNT聚合函数为例,其AggregateFunctionCount的状态使用整型UInt64记录。
聚合状态可以被序列化和反序列化,以在分布式查询执行期间通过网络传递或者在内存不够的时候
将其写到硬盘。

2.11 Cluster与Replication.

ClickHouse的集群由分片 ( Shard ) 组成,而每个分片又通过副本 ( Replica ) 组成。
这种分层的概念,在一些流行的分布式系统中十分普遍。
ClickHouse的1个节点只能拥有1个分片,也就是说如果要实现1分片、1副本,则至少需要部
署2个服务节点。
分片只是一个逻辑概念,其物理承载还是由副本承担的

环境搭建

启动服务

[root@node01 ~]# systemctl start clickhouse-server.service
[root@node01 ~]# clickhouse-client -h 192.168.88.101 -u default --password
默认用户 default 密码 123456
查看节点的信息
select * from system.clusters;

数据定义

数据类型

基本数据类型
整型
  • 固定长度的整型,包括有符号整型或无符号整型。

    整型范围(-2n-1~2n-1-1):
    Int8 - [-128 : 127]
    Int16 - [-32768 : 32767]
    Int32 - [-2147483648 : 2147483647]
    Int64 - [-9223372036854775808 : 9223372036854775807]

  • 无符号整型范围(0~2n-1):

  • 无符号数据表示数量,只有正值

    UInt8 - [0 : 255]
    UInt16 - [0 : 65535]
    UInt32 - [0 : 4294967295]
    UInt64 - [0 : 18446744073709551615]
    使用场景: 个数、数量、也可以存储型 id。

浮点型

Float32 - float
Float64 – double
建议尽可能以整数形式存储数据。例如,将固定精度的数字转换为整数值,如时间用毫秒为单位表示,因为浮点型进行计算时可能引起四舍五入的误差

使用场景:一般数据值比较小,不涉及大量的统计计算,精度要求不高的时候。比如
保存商品的重量。

布尔型

没有单独的类型来存储布尔值。可以使用 UInt8 类型,取值限制为 0 或 1

Decimal 定点数型

有符号的浮点数,可在加、减和乘法运算过程中保持精度。对于除法,最低有效数字会被丢弃(不舍入)。

有三种声明:
➢ Decimal32(s),相当于 Decimal(9-s,s),有效位数为 1~9
➢ Decimal64(s),相当于 Decimal(18-s,s),有效位数为 1~18
➢ Decimal128(s),相当于 Decimal(38-s,s),有效位数为 1~38

​ s 标识小数位
使用场景: 一般金额字段、汇率、利率等字段为了保证小数点精度,都使用 Decimal进行存储。

字符串
  • string

    字符串可以任意长度的。它可以包含任意的字节集,包含空字节。

  • FixedString(N)

    相当于Char固定长度 N 的字符串,N 必须是严格的正自然数。当服务端读取长度小于 N 的字符
    串时候,通过在字符串末尾添加空字节来达到 N 字节长度。 当服务端读取长度大于 N 的
    字符串时候,将返回错误消息。

    与 String 相比,极少会使用 FixedString,因为使用起来不是很方便

**使用场景:**名称、文字描述、字符型编码。 固定长度的可以保存一些定长的内容,比如一些编码,性别等但是考虑到一定的变化风险,带来收益不够明显,所以定长字符串使用
意义有限。

  • UUID:32位,格式8-4-4-4-12,如果未被赋值,则用0填充
时间类型

目前 ClickHouse 有三种时间类型

➢ Date 接受年-月-日的字符串比如 ‘2019-12-16’
➢ Datetime 接受年-月-日 时:分:秒的字符串比如 ‘2019-12-16 20:50:10’
➢ Datetime64 接受年-月-日 时:分:秒.亚秒的字符串比如‘2019-12-16 20:50:10.66’
日期类型,用两个字节存储,表示从 1970-01-01 (无符号) 到当前的日期值。

复合数据类型
枚举类型

包括 Enum8 和 Enum16 类型。Enum 保存 ‘string’= integer 的对应关系。
Enum8 用 ‘String’= Int8 对描述。
Enum16 用 ‘String’= Int16 对描述。

使用场景:对一些状态、类型的字段算是一种空间优化,也算是一种数据约束。但是实际使用中往往因为一些数据内容的变化增加一定的维护成本,甚至是数据丢失问题。所以谨慎使用

数组

Array(T)::由 T 类型元素组成的数组 创建数据:array(T)或[],类型必须相同

T 可以是任意类型,包含数组类型。 但不推荐使用多维数组,ClickHouse 对多维数组
的支持有限。例如,不能在 MergeTree 表中存储多维数组。
(1)创建数组方式 1,使用 array 函数

array(T)
hadoop102 :) SELECT array(1, 2) AS x, toTypeName(x) ;
元组
  • 由多个元素组成,允许不同类型
  • 创建数据:(T1, T2, …),Tuple(T1, T2, …)
嵌套类型
  • Nested(Name1 Type1,Name2 Type2,…)
  • 相当于表中嵌套一张表,插入时相当于一个多维数组的格式,一个字段对应一个数组
其他类型
Nullable(TypeName)
  • 只能与基础数据类型搭配使用,表示某个类型的值可以为NULL;Nullable(Int8)表示可以存储
    Int8类型的值,没有值时存NULL
  • 注意:
    • 不能与复合类型数据一起使用、
    • 不能作为索引字段
    • 尽量避免使用,字段被Nullable修饰后会额外生成[Column].null.bin 文件保存Null值,增加开销
Domain
  • Pv4 使用 UInt32 存储。如 116.253.40.133
  • IPv6 使用 FixedString(16) 存储。如 2a02:aa08:e000:3100::2

数据库

数据库起到了命名空间的作用,可以有效规避命名冲突的问题,也为后续的数据隔离提供了支撑。任何一张数据表,都必须归属在某个数据库之下。

数据库引擎:

  • Ordinary:默认引擎
    • 在绝大多数情况下我们都会使用默认引擎,使用时无须刻意声明。在此数据库下可以使
      用任意类型的表引擎。
  • Dictionary:字典引擎
    • 此类数据库会自动为所有数据字典创建它们的数据表
  • Memory:内存引擎
    • 用于存放临时数据。此类数据库下的数据表只会停留在内存中,不会涉及任何磁盘操
      作,当服务重启后数据会被清除。
  • Lazy:日志引擎
    • 此类数据库下只能使用log系列的表引擎。
  • MySQL:MySQL引擎
    • 此类数据库下会自动拉取远端MySQL中的数据,并为它们创建MySQL表引擎的数据表,

数据表

创建表

ClickHouse目前提供了三种最基本的建表方法:

  • 第一种是常规定义方法

    使用[db_name.]参数可以为数据表指定数据库,如果不指定此参数,则默认会使用default数据库

  • 第二种定义方法是复制其他表的结构

    支持在不同的数据库之间复制表结构

  • 第三种定义方法是通过SELECT子句的形式创建

    根据SELECT子句建立相应的表结构,同时还会将SELECT子句查询的数据顺带写入

删除表

ClickHouse和大多数数据库一样,使用DESC查询可以返回数据表的定义结构
如果想删除一张数据表,则可以使用下面的DROP语句:

DROP TABLE [IF EXISTS] [db_name.]table_name
临时表

ClickHouse也有临时表的概念,创建临时表的方法是在普通表的基础之上添加TEMPORARY关键字

特点:

它的生命周期是会话绑定的,所以它只支持Memory表引擎,如果会话结束,数据表就会被销毁

临时表不属于任何数据库,所以在它的建表语句中,既没有数据库参数也没有表引擎参数。

临时表的优先级是大于普通表。当两张数据表名称相同的时候,会优先读取临时表的数据

分区表
  • 数据分区(partition)和数据分片(shard)是完全不同的两个概念。
    • 数据分区是针对本地数据而言的,是数据的一种纵向切分。
    • 数据分片是数据的一种横向切分
数据表操作
追加新字段
ALTER TABLE xxx add column item String DEFAULT 'mac'        默认最后一行加
ALTER TABLE xxx add column app  String AFTER ID             在ID列后面加新字段

修改数据类型
ALTER TABLE content MODIFY COLUMN ip IPv4

修改备注
ALTER TABLE content comment column ID '主键列'

删除已有字段
ALTER TABLE content DROP COLUMN [IF EXISTS] name

移动数据表
RENAME TABLE default.content TO test.test

清空数据表
drop table content              物理删除,立即释放空间;删除表数据+表空间
truncate table content          物理删除,立即释放空间;删除表数据,保留表结构
delete table content [FROM]     逻辑删除,删除的数据做标记;逐行删除表数据,保留表空间


查看表定义结构:
show create table content           原样输出        
desc test                           表格显示

删除表:
drop table content              物理删除,立即释放空间;删除表数据+表空间
truncate table content          物理删除,立即释放空间;删除表数据,保留表结构
delete table content [FROM]     逻辑删除,删除的数据做标记;逐行删除表数据,保留表空间

视图

ClickHouse拥有两种视图,普通视图物化视图,其中物化视图拥有独立的存储,而普通视图只是一层简单的查询代理。

  • 普通视图
    • 普通视图不会存储任何数据它只是一层单纯的SELECT查询映射起着简化查询、明晰语义
      的作用,对查询性能不会有任何增强
  • 物化视图
    • 物化视图支持表引擎,相当于特殊的表,数据保存形式由它的表引擎决定
    • 物化视图创建好之后,如果源表被写入新数据,那么物化视图也会同步更新
    • POPULATE修饰符决定了物化视图的初始化策略:
      • 如果使用了POPULATE修饰符,那么在创建视图的过程中,会连带将源表中已存在的数
        据一并导入,如同执行了SELECT INTO一般;
      • 反之,如果不使用POPULATE修饰符,那么物化视图在创建之后是没有数据的,它只会
        同步在此之后被写入源表的数据。
      • 物化视图目前并不支持同步删除,如果在源表中删除了数据,物化视图的数据仍会保
        留。

数据的CRUD

INSERT语句支持三种语法范式,三种范式各有不同,可以根据写入的需求灵活运用

  • 第一种是使用VALUES格式的常规语法:

    INSERT INTO [db.]table [(c1, c2, c3…)] VALUES (v11, v12, v13…), (v21,
    v22, v23…), ...
    
    INSERT INTO partition_v2 VALUES ('A0011','www.nauu.com', '2019-10-01'),
    ('A0012','www.nauu.com', '2019-11-20')
    INSERT INTO partition_v2 VALUES ('A0014',toString(1+2), now())
    
    
  • 第二种是使用指定格式的语法:

    INSERT INTO [db.]table [(c1, c2, c3…)] FORMAT format_name data_set
    
    INSERT INTO partition_v2 FORMAT CSV \
    'A0017','www.nauu.com', '2019-10-01' \
    'A0018','www.nauu.com', '2019-10-01'
    
  • 第三种是使用SELECT子句形式的语法:

    INSERT INTO [db.]table [(c1, c2, c3…)] SELECT ...
    
    INSERT INTO partition_v2 SELECT * FROM partition_v1
    INSERT INTO partition_v2 SELECT 'A0020', 'www.jack.com', now()
    

表引擎(MergeTree)

表引擎的使用

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

    ➢ 数据的存储方式和位置,写到哪里以及从哪里读取数据。
    ➢ 支持哪些查询以及如何支持。
    ➢ 并发数据访问。
    ➢ 索引的使用(如果存在)。
    ➢ 是否可以执行多线程请求。
    ➢ 数据复制参数。

  • 表引擎的使用方式就是必须显式在创建表时定义该表使用的引擎,以及引擎使用的相关参数。

  • 特别注意:引擎的名称大小写敏感

TinyLog

  • 以列文件的形式保存在磁盘上,不支持索引,没有并发控制。一般保存少量数据的小表,
    生产环境上作用有限。可以用于平时练习测试用。

  • 如:

    create table t_tinylog ( id String, name String) engine=TinyLog;

Memory

  • 内存引擎,数据以未压缩的原始形式直接保存在内存当中,服务器重启数据就会消失。读写操作不会相互阻塞,不支持索引。简单查询下有非常非常高的性能表现(超过 10G/s)。
  • 一般用到它的地方不多,除了用来测试,就是在需要非常高的性能,同时数据量又不太大(上限大概 1 亿行)的场景。

MergeTree

  • ClickHouse 中最强大的表引擎当属 MergeTree(合并树)引擎及该系列(MergeTree)中的其他引擎,支持索引和分区,地位可以相当于 innodb 之于 Mysql。而且基于 MergeTree,还衍生出了很多小弟,也是非常有特色的引擎。

创建与存储

  • MergeTree在写入一批数据时,数据总会以数据片段的形式写入磁盘,且数据片段不可修改
  • 为了避免片段过多,ClickHouse会通过后台线程,定期合并这些数据片段,属于相同分区的数据片段会被合成一个新的片段
  • 这种数据片段往复合并的特点,也正是合并树名称的由来。
语法

1)建表语句

create table t_order_mt(
id UInt32,
sku_id String,
total_amount Decimal(16,2),
create_time Datetime
) engine =MergeTree
partition by toYYYYMMDD(create_time)
primary key (id)
order by (id,sku_id);

2)插入数据

insert into t_order_mt values
(101,'sku_001',1000.00,'2020-06-01 12:00:00') ,
(102,'sku_002',2000.00,'2020-06-01 11:00:00'),
(102,'sku_004',2500.00,'2020-06-01 12:00:00'),
(102,'sku_002',2000.00,'2020-06-01 13:00:00'),
(102,'sku_002',12000.00,'2020-06-01 13:00:00'),
(102,'sku_002',600.00,'2020-06-02 12:00:00');
  • MergeTree 其实还有很多参数(绝大多数用默认值即可),但是三个参数是更加重要的,也涉及了关于 MergeTree 的很多概念。

  • PARTITION BY [选填]:分区键,用于指定表数据以何种标准进行分区。

    分区键既可以是单个列字段,也可以通过元组的形式使用多个列字段,同时它也支持使
    用列表达式。
    如果不声明分区键,则ClickHouse会生成一个名为all的分区。
    合理使用数据分区,可以有效减少查询时数据文件的扫描范围。

  • ORDER BY [必填]:排序键,用于指定在一个数据片段内,数据以何种标准排序。

    默认情况下主键(PRIMARY KEY)与排序键相同。
    排序键既可以是单个列字段,例如ORDER BY CounterID,也可以通过元组的形式使用
    多个列字段,例如ORDERBY(CounterID,EventDate)。
    当使用多个列字段排序时,以ORDERBY(CounterID,EventDate)为例,在单个数据片
    段内,数据首先会以CounterID排序,相同CounterID的数据再按EventDate排序。

  • PRIMARY KEY [选填]:主键,顾名思义,声明后会依照主键字段生成一级索引,用于加速表查询。

    默认情况下,主键与排序键(ORDER BY)相同,所以通常直接使用ORDER BY代为指定主
    键,无须刻意通过PRIMARY KEY声明。
    所以在一般情况下,在单个数据片段内,数据与一级索引以相同的规则升序排列。
    与其他数据库不同,MergeTree主键允许存在重复数据(ReplacingMergeTree可以去
    重)。

存储格式
  • MergeTree表引擎中的数据是拥有物理存储的,数据会按照分区目录的形式保存到磁盘之上

在这里插入图片描述

在这里插入图片描述

  • 一张数据表的完整物理结构分为3个层级,依次是数据表目录分区目录及各分区下具体的数据文件

数据分区

数据分区规则
  • MergeTree数据分区的规则由分区ID决定,而具体到每个数据分区所对应的ID,则是由分区键的取值决定的。

  • 分区键支持使用任何一个或一组字段表达式声明,其业务语义可以是年、月、日或者组织单位等任何一种规则。

  • 针对取值数据类型的不同,分区ID的生成逻辑目前拥有四种规则:

    • 不指定分区键

      如果不使用分区键,即不使用PARTITION BY声明任何分区表达式,则分区ID默认取名为all,所有的数据都会被写入这个all分区。

    • 使用整型:

      如果分区键取值属于整型(兼容UInt64,包括有符号整型和无符号整型),且无法转换为日期类型YYYYMMDD格式则直接按照该整型的字符形式输出,作为分区ID的取值。

    • 使用日期类型:

      如果分区键取值属于日期类型,或者是能够转换为YYYYMMDD格式的整型
      则使用按照YYYYMMDD进行格式化后的字符形式输出,并作为分区ID的取值。

    • 使用其他类型:

      如果分区键取值既不属于整型,也不属于日期类型
      例如String、Float等,则通过128位Hash算法取其Hash值作为分区ID的取值。

分区目录命名
  • 一个完整分区目录的命名公式
    -在这里插入图片描述

​ 201905表示分区目录的ID;
​ 1_1分别表示最小的数据块编号与最大的数据块编号;
​ 而最后的_0则表示目前合并的层级。

  • PartitionID_MinBlockNum_MaxBlockNum_Level

    PartitionID:分区ID
    MinBlockNum和MaxBlockNum:顾名思义,最小数据块编号最大数据块编号
    Level:合并的层级,可以理解为某个分区被合并过的次数,或者这个分区的年龄。数值越高
    表示年龄越大。

分区目录合并
  • MergeTree的分区目录和传统意义上其他数据库有所不同。

  • 首先,MergeTree的分区目录并不是在数据表被创建之后就存在的,而是在数据写入过程中被创建的

    也就是说如果一张数据表没有任何数据,那么也不会有任何分区目录存在

  • 其次,它的分区目录在建立之后也并不是一成不变的。

    • 在其他某些数据库的设计中,追加数据后目录自身不会发生变化,只是在相同分区目录中追加新的数据文件。
    • 而MergeTree完全不同,伴随着每一批数据的写入(一次INSERT语句),MergeTree都会生成一批新的分区目录。
    • 即便不同批次写入的数据属于相同分区,也会生成不同的分区目录。
    • 也就是说,对于同一个分区而言,也会存在多个分区目录的情况。
    • 在之后的某个时刻(写入后的10~15分钟,也可以手动执行optimize查询语句)
    • ClickHouse会通过后台任务再将属于相同分区的多个目录合并成一个新的目录。
    • 已经存在的旧分区目录并不会立即被删除,而是在之后的某个时刻通过后台任务被删除(默认8分钟)。
  • 新目录名称的合并方式遵循规则:

    MinBlockNum:取同一分区内所有目录中最小的MinBlockNum值。
    MaxBlockNum:取同一分区内所有目录中最大的MaxBlockNum值。
    Level:取同一分区内最大Level值并加1。

一级索引

  • MergeTree的主键使用PRIMARY KEY定义,待主键定义之后,MergeTree会依据index_granularity间隔(默认8192行),为数据表生成一级索引并保存至primary.idx文件内,索引数据按照PRIMARYKEY排序。
稀疏索引
  • primary.idx文件内的一级索引采用稀疏索引实现。

    稠密索引中每一行索引标记都会对应到一行具体的数据记录
    稀疏索引中每一行索引标记对应的是一段数据,而不是一行

在这里插入图片描述

  • 稀疏索引的优势是显而易见的,它仅需使用少量的索引标记就能够记录大量数据的区间位置信息,且数据量越大优势越为明显

    以默认的索引粒度(8192)为例,MergeTree只需要12208行索引标记就能为1亿行数据记录提供索引。
    由于稀疏索引占用空间小,所以primary.idx内的索引数据常驻内存,取用速度自然极快。

  • 索引文件查看命令

    od -An -i -w4 primary.idx
    
索引粒度

在这里插入图片描述

  • 索引粒度就如同标尺一般,会丈量整个数据的长度,并依照刻度对数据进行标注,最终将数据标记成多个间隔的小段
索引规则
  • 由于是稀疏索引,所以MergeTree需要间隔index_granularity行数据才会生成一条索引记录,其索
    引值会依据声明的主键字段获取。

  • 单主键

在这里插入图片描述

​ 第0(81920)行CounterID取值57,第8192(81921)行CounterID取值1635,而第16384(8192*2)行CounterID取值3266
​ 最终索引数据将会是5716353266。

  • 多主键

在这里插入图片描述

索引查询过程
  • MarkRange

    MarkRange在ClickHouse中是用于定义标记区间的对象。
    MergeTree按照index_granularity的间隔粒度,将一段完整的数据划分成了多个小的间隔数据段,一个具体的数据段即是一个MarkRange。
    MarkRange与索引编号对应,使用start和end两个属性表示其区间范围。
    通过与start及end对应的索引编号的取值,即能够得到它所对应的数值区间。而数值区间表示了此MarkRange包含的数据范围。

  • 案例分析

在这里插入图片描述

​ 主键ID为String类型,ID的取值从A000开始,后面依次为A001、A002……直至A189为止。
​ MergeTree的索引粒度index_granularity=3
​ MergeTree会将此数据片段划分成189/3=63个小的MarkRange

  • 索引查询其实就是两个数值区间的交集判断。

    一个区间是由基于主键的查询条件转换而来的条件区间;
    一个区间是刚才所讲述的与MarkRange对应的数值区间。

  • 查询步骤

在这里插入图片描述

  • 生成查询条件区间:首先,将查询条件转换为条件区间。
  • 递归交集判断:以递归的形式,依次对MarkRange的数值区间与条件区间做交集判断。从最大的区间[A000,+inf)开始:
    • 如果不存在交集,则直接通过剪枝算法优化此整段MarkRange。
    • 如果存在交集,且MarkRange步长大于8(end-start),则将此区间进一步拆分成8个子区间,并重复此规则,继续做递归交集判断。
    • 如果存在交集,且MarkRange不可再分解(步长小于8),则记录MarkRange并返回。
  • 合并MarkRange区间:将最终匹配的MarkRange聚在一起,合并它们的范围。

二级索引

  • 二级索引又称跳数索引,由数据的聚合信息构建而成。

  • 根据索引类型的不同,其聚合信息的内容也不同。跳数索引的目的与一级索引一样,也是帮助查询时减少数据扫描的范围。

  • 跳数索引在默认情况下是关闭的,需要设置allow_experimental_data_skipping_indices

    SET allow_experimental_data_skipping_indices = 1

  • 跳数索引需要在CREATE语句内定义,它支持使用元组和表达式的形式声明,其完整的定义语法

    INDEX index_name expr TYPE index_type(…) GRANULARITY granularity

粒度
  • granularity定义了一行跳数索引能够跳过多少个index_granularity区间的数据。
    • 首先,按照index_granularity粒度间隔将数据划分成n段,总共有[0,n-1]个区间
      (n=total_rows/index_granularity,向上取整)
    • 接着,根据索引定义时声明的表达式,从0区间开始,依次按index_granularity粒度从数据中获取聚合信息,每次向前移动1步,聚合信息逐步累加。
    • 最后,当移动granularity次区间时,则汇总并生成一行跳数索引数据。
分类
  • MergeTree共支持4种跳数索引,分别是minmax、set、ngrambf_v1和tokenbf_v1。一张数据表支持同时声明多个跳数索引。

  • minmax:

    minmax索引记录了一段数据内的最小和最大极值,其索引的作用类似分区目录的minmax索引,能够快速跳过无用的数据区间

  • set:

    set索引直接记录了声明字段或表达式的取值(唯一值,无重复),其完整形式为set(max_rows),其中max_rows是一个阈值,表示在一个index_granularity内,索引最多记录的数据行数。

  • ngrambf_v1:

    • ngrambf_v1索引记录的是数据短语的布隆表过滤器,只支持String和FixedString数据类型。
    • ngrambf_v1只能够提升in、notIn、like、equals和notEquals查询的性能
    • 其完整形式为
      ngrambf_v1(n,size_of_bloom_filter_in_bytes,number_of_hash_functions,random_seed)。
      • 这些参数是一个布隆过滤器的标准输入,如果你接触过布隆过滤器,应该会对此十分熟悉。它们具体的含义如下:
      • n:token长度,依据n的长度将数据切割为token短语。
      • size_of_bloom_filter_in_bytes:布隆过滤器的大小。
      • number_of_hash_functions:布隆过滤器中使用Hash函数的个数。
      • random_seed:Hash函数的随机种子。
  • tokenbf_v1:

    tokenbf_v1索引是ngrambf_v1的变种,同样也是一种布隆过滤器索引
    tokenbf_v1除了短语token的处理方法外,其他与ngrambf_v1是完全一样的。
    tokenbf_v1会自动按照非字符的、数字的字符串分割token

数据存储

列式存储
  • MergeTree中,数据按列存储。具体到每个列字段,每个列字段都拥有一个与之对应的.bin数据文件(物理存储)。.bin文件只会保存当前分区片段内的这一部分数据。**存储方式**大致流程是:首先数据压缩 -> 按照ORDER BY的声明排序 -> 数据以多个压缩数据块的形式被组织写入.bin文件
  • 优点:
    • 一是可以更好地进行数据压缩
    • 二是能够最小化数据扫描的范围
数据压缩
  • 一个压缩数据块由**头信息压缩数据两部分组成。头信息固定使用9位字节表示,具体由1个UInt8(1字节)整型和2个UInt32(4字节)整型组成,分别代表使用的压缩算法类型**、压缩后的数据大小压缩前的数据大小
  • bin压缩文件是由多个压缩数据块组成的,而每个压缩数据块的头信息则是基于
    CompressionMethod_CompressedSize_UncompressedSize公式生成的

在这里插入图片描述

  • 数据写入过程:
  • MergeTree在数据具体的写入过程中,会依照索引粒度(默认情况下,每次取8192行),按批次获取数据并进行处理
  • **单个批次数据size<64KB :**如果单个批次数据小于64KB,则继续获取下一批数据,直至累积到size>=64KB时,生成下一个压缩数据块。
  • **单个批次数据64KB<=size<=1MB :**如果单个批次数据大小恰好在64KB与1MB之间,则直接生成下一个压缩数据块。
  • **单个批次数据size>1MB :**如果单个批次数据直接超过1MB,则首先按照1MB大小截断并生成
    下一个压缩数据块。剩余数据继续依照上述规则执行。

在这里插入图片描述

  • 优点:
    • 其一,虽然数据被压缩后能够有效减少数据大小,降低存储空间并加速数据传输效率,但数据
      的压缩和解压动作,其本身也会带来额外的性能损耗。所以需要控制被压缩数据的大小,以求
      在性能损耗和压缩率之间寻求一种平衡
    • 其二,在具体读取某一列数据时(.bin文件),首先需要将压缩数据加载到内存并解压,这样
      才能进行后续的数据处理。通过压缩数据块,可以在不读取整个.bin文件的情况下将读取粒度
      降低到压缩数据块级别,从而进一步缩小数据读取的范围。
      在这里插入图片描述

数据标记

数据标记的生成规则

数据标记作为衔接一级索引和数据的桥梁,其像极了做过标记小抄的书签,而且书本中每个一级章节都拥有各自的书签。

在这里插入图片描述

  • 数据标记文件和索引区间是对齐的。都是按照index_ granularity的粒度间隔划分

  • 数据标记文件和.bin文件也是一一对应。每一个列字段[column].bin文件都有一个对应的[column].mrk数据标记文件,用于记录数据在.bin文件中偏移量信息

  • 一行标记数据使用元组表示,包含两个整型数据的偏移信息(压缩数据块的起始偏移量压缩块解压后未压缩数据的起始偏移量)。

  • 每一行标记数据都表示了一个片段的数据(默认8192行)在.bin压缩文件中的读取位置信息。标记数据与一级索引不同,它不能常驻内存,而是使用LRU(最近最少使用)缓存策略加快其取用速度。

  • 标记查看命令

    • od -An -l name.mrk
      
工作方式

**MergeTree在读取数据时,必须通过标记数据的位置信息才能够找到所需要的数据。**整个查找过程
大致可以分为读取压缩数据块读取数据两个步骤

在这里插入图片描述

  • 数据理解
    • 1B*8192=8192B,64KB=65536B,65536/8192=8
    • 头信息固定由9个字节组成,压缩后大小为8个字节
    • 12016=8+12000+8
  • 读取压缩数据块:在查询某一列数据MergeTree无须一次性加载整个.bin文件。借助标记文件中的压缩文件偏移量加载指定的数据压缩块。
  • 读取数据:解压后的数据,MergeTree并不需要一次性扫描整段解压数据,借住标记文件中保存的数据块中偏移量以index_granularity的粒度加载特定一小段 primary

在这里插入图片描述

在这里插入图片描述

数据标记与数据压缩

  • 由于压缩数据块的划分,与一个间隔(index_granularity)内的数据大小相关,每个压缩数据块的体积都被严格控制在64KB~1MB。
  • 而一个间隔(index_granularity)的数据,又只会产生一行数据标记。
  • 那么根据一个间隔内数据的实际字节大小,数据标记和压缩数据块之间会产生三种不同的对应关
    系。
多对一
  • 多个数据标记对应一个压缩数据块
  • 当一个间隔(index_granularity)内的数据未压缩大小size小于64KB时
    在这里插入图片描述
一对一
  • 一个数据标记对应一个压缩数据块
  • 当一个间隔(index_granularity)内的数据未压缩大小size大于等于64KB且小于等于1MB时
    在这里插入图片描述
一对多
  • 一个数据标记对应多个压缩数据块
  • 当一个间隔(index_granularity)内的数据未压缩大小size直接大于1MB时
    在这里插入图片描述

数据读写流程

写入流程
  1. 数据写入的第一步是生成分区目录,伴随着每一批数据的写入,都会生成一个新的分区目录。
  2. 在后续的某一时刻,属于相同分区的目录会依照规则合并到一起;
  3. 接着,按照index_granularity索引粒度,会分别生成primary.idx一级索引、每一个列字段的.mrk数据标记和.bin压缩数据文件。

在这里插入图片描述

  • 从分区目录201403_1_34_3能够得知,该分区数据共分34批写入,期间发生过3次合并。在数
    据写入的过程中,依据index_granularity的粒度,依次为每个区间的数据生成索引、标记和压缩数据块。其中,索引和标记区间是对齐的,而标记与压缩块则根据区间数据大小的不同,会生成多对一、一对一和一对多三种关系。
查询数据
  1. 数据查询的本质,可以看作一个不断减小数据范围的过程。
  2. 在最理想的情况下,MergeTree首先可以依次借助分区索引、一级索引和二级索引,将数据扫描范围缩至最小。
  3. 然后再借助数据标记,将需要解压与计算的数据范围缩至最小

在这里插入图片描述

MergeTree Famil

MergeTree

该引擎没有特殊功能,适合保存历史明细.

数据TTL
  • TTL即Time To Live,顾名思义,它表示数据的存活时间。在MergeTree中,可以为某个列字段或整张表设置TTL。
    • 当时间到达时,如果是列字段级别的TTL,则会删除这一列的数据;
    • 如果是表级别的TTL,则会删除整张表的数据;
    • 如果同时设置了列级别和表级别的TTL,则会以先到期的那个为主。
  • 设置TTL
    • INTERVAL完整的操作包括SECOND、MINUTE、HOUR、DAY、WEEK、MONTH、QUARTER和YEAR。
  • TTL的运行机制
    • 如果一张MergeTree表被设置了TTL表达式,那么在写入数据时,会以数据分区为单位,在每
      个分区目录内生成一个名为ttl.txt的文件
    • ttl.txt文件中通过一串JSON配置保存了TTL的相关信息
      • {“columns”:[{“name”:“code”,“min”:1557478860,“max”:1557651660}],“table”:{“min”:1557565200,“max”:1557738000}}
      • columns用于保存列级别TTL信息;
      • table用于保存表级别TTL信息;
      • min和max则保存了当前数据分区内,TTL指定日期字段的最小值、最大值分别与INTERVAL表达式计算后的时间戳。
多路径存储策略
  • 19.15版本之前,MergeTree只支持单路径存储,所有的数据都会被写入config.xml配置中path指
    定的路径下,即使服务器挂载了多块磁盘,也无法有效利用这些存储空间。
  • 19.15版本开始,MergeTree实现了自定义存储策略的功能,支持以数据分区为最小移动单元,将
    分区目录写入多块磁盘目录。
  • 存储策略
    • 默认策略:
      • MergeTree原本的存储策略,无须任何配置,所有分区会自动保存到config.xml配置中
        path指定的路径下
    • JBOD策略:
      • 这种策略适合服务器挂载了多块磁盘,但没有做RAID的场景。
      • JBOD的全称是Just a Bunch of Disks,它是一种轮询策略,每执行一次INSERT或者
        MERGE,所产生的新分区会轮询写入各个磁盘。
    • HOT/COLD策略:
      • 这种策略适合服务器挂载了不同类型磁盘的场景。
      • 将存储磁盘分为HOT与COLD两类区域。
        • HOT区域使用SSD这类高性能存储媒介,注重存取性能;
        • COLD区域则使用HDD这类高容量存储媒介,注重存取经济性。
      • 数据在写入MergeTree之初,首先会在HOT区域创建分区目录用于保存数据,当分区数
        据大小累积到阈值时,数据会自行移动到COLD区域。
  • 配置方式
    • 存储配置需要预先定义在config.xml配置文件中,由storage_configuration标签表示。
    • 在storage_configuration之下又分为disks和policies两组标签,分别表示磁盘与存储策略。

ReplacingMergeTree

该引擎适合于经常要根据’主键’进行数据更新的数据(upsert),主键加引号是因为,其实是根据order by定义的字段而不是根据primary key的字段去重的.

  • MergeTree拥有主键,但是它的主键却没有唯一键的约束。这意味着即便多行数据的主键相同,它
    们还是能够被正常写入。
  • ReplacingMergeTree就是在这种背景下为了数据去重而设计的,它能够在合并分区时删除重复的数据。
  • ReplacingMergeTree是以分区为单位删除重复数据的。
    • 只有在相同的数据分区内重复的数据才可以被删除,而不同数据分区之间的重复数据依然不能
      被剔除。
    • 如果要求主键完全不重复,那么这张表就不能分区

AggregatingMergeTree

AggregatingMergeTree更为常见的应用方式是结合物化视图使用,将它作为物化视图的表引擎。

  • 数据立方体
    • 它通过以空间换时间的方法提升查询性能,将需要聚合的数据,预先计算出来,并将结果保存起来。
    • 在后续进行聚合查询的时候,直接使用结果数据。

CollapsingMergeTree

  • CollapsingMergeTree就是一种通过以增代删的思路,支持行级数据修改和删除的表引擎。它通过
    定义一个sign标记位字段,记录数据行的状态。
  • 如果sign标记为1,则表示这是一行有效的数据;如果sign标记为-1,则表示这行数据需要被删除。
  • 当CollapsingMergeTree分区合并时,同一数据分区内,sign标记为1和-1的一组数据会被抵消删除。
  • 这种1和-1相互抵消的操作,犹如将一张瓦楞纸折叠了一般。
  • 特点:
    • 折叠数据并不是实时触发的,和所有其他的MergeTree变种表引擎一样,这项特性也只有在
      分区合并的时候才会体现。
    • 所以在分区合并之前,用户还是会看到旧的数据。解决这个问题的方式有两种。
      • 在查询数据之前,使用optimize TABLE table_name FINAL命令强制分区合并,但是这
        种方法效率极低,在实际生产环境中慎用。
      • 需要改变我们的查询方式
    • 只有相同分区内的数据才有可能被折叠。
    • CollapsingMergeTree对于写入数据的顺序有着严格要求。
      • 先写入sign=1,再写入sign=-1,则能够正常折叠
      • 先写入sign=-1,再写入sign=1,则不能够折叠

VersionedCollapsingMergeTree

此引擎是CollapingMergeTree的升级版,除了一个Int8类型的flag字段还需要一个Int8型的版本字段:
应用场景,统计在线玩家:比如游戏登陆时候要把正在登陆的玩家信息放到表中,代表已经登陆,但是下线的时候再插入一条数据,使其折叠,此时合并文件后(或者手动调用optimize语句)数据消失,代表玩家已经下线.

  • VersionedCollapsingMergeTree表引擎的作用与CollapsingMergeTree完全相同
  • 它们的不同之处在于,VersionedCollapsingMergeTree对数据的写入顺序没有要求,在同一个分
    区内,任意顺序的数据都能够完成折叠操作。

MergeTree关系梳理

  • MergeTree表引擎向下派生出6个变种表引擎

常见类型表引擎

外部存储

  • 外部存储表引擎直接从其他的存储系统读取数据
  • 例如直接读取HDFS的文件或者MySQL数据库的表。
  • 这些表引擎只负责元数据管理和数据查询,而它们自身通常并不负责数据的写入,数据文件直接由
    外部系统提供。

外部存储

  • 外部存储表引擎直接从其他的存储系统读取数据
  • 例如直接读取HDFS的文件或者MySQL数据库的表。
  • 这些表引擎只负责元数据管理和数据查询,而它们自身通常并不负责数据的写入,数据文件直接由外部系统提供。
HDFS
  • 在HDFS上给ClickHouse用户授权
Mysql
  • MySQL表引擎可以与MySQL数据库中的数据表建立映射,并通过SQL向其发起远程查询,包括SELECT和INSERT

    ENGINE = MySQL(‘host:port’, ‘database’, ‘table’, ‘user’, ‘password’[,replace_query, ‘on_duplicate_clause’])

    host:port表示MySQL的地址和端口。
    database表示数据库的名称。
    table表示需要映射的表名称。
    user表示MySQL的用户名。
    password表示MySQL的密码。
    replace_query默认为0,对应MySQL的REPLACE INTO语法。如果将它设置为1,则会用
    REPLACEINTO代替INSERT INTO。
    on_duplicate_clause默认为0,对应MySQL的ON DUPLICATE KEY语法。如果需要使用该设置,则必须将replace_query设置成0。

    目前MySQL表引擎不支持任何UPDATE和DELETE操作

JDBC
  • JDBC表引擎不仅可以对接MySQL数据库,还能够与PostgreSQL、SQLite和H2数据库对接。
  • JDBC表引擎无法单独完成所有的工作,它需要依赖名为clickhouse-jdbc-bridge的查询代理服务。
  • clickhouse-jdbc-bridge是一款基于Java语言实现的SQL代理服务,它的项目地址为https://github.com/ClickHouse/clickhouse-jdbc-bridge 。
  • clickhouse-jdbc-bridge可以为ClickHouse代理访问其他的数据库,并自动转换数据类型。
Kafka
  • 目前ClickHouse还不支持恰好一次(Exactly once)的语义,因为这需要应用端与Kafka深度配合
    才能实现。
  • 再次执行SELECT查询会发现kafka_table数据表空空如也,这是因为Kafka表引擎在执行查询之
    后就会移动offset,导致数据无法重复读取。

首先是Kafka数据表A,它充当的角色是一条数据管道,负责拉取Kafka中的数据。
接着是另外一张任意引擎的数据表B,它充当的角色是面向终端用户的查询表,在生产环境中通常是MergeTree系列。
最后,是一张物化视图C,它负责将表A的数据实时同步到表B。

File
  • File表引擎能够直接读取本地文件的数据,通常被作为一种扩充手段来使用。
  • File表引擎的定义参数中,并没有包含文件路径这一项。所以,File表引擎的数据文件只能保存在config.xml配置中由path指定的路径下。
  • 每张File数据表均由目录和文件组成,其中目录以表的名称命名,而数据文件则固定以data.format命名

内存类型

  • 将数据全量放在内存中,对于表引擎来说是一把双刃剑:

    • 一方面,这意味着拥有较好的查询性能;
    • 另一方面,如果表内装载的数据量过大,可能会带来极大的内存消耗和负担。
Memory
  • Memory表引擎直接将数据保存在内存中,数据既不会被压缩也不会被格式转换,数据在内存中保存的形态与查询时看到的如出一辙。
  • 当ClickHouse服务重启的时候,Memory表内的数据会全部丢失。
  • 当数据被写入之后,磁盘上不会创建任何数据文件。
Set
  • Set表引擎是拥有物理存储的,数据首先会被写至内存,然后被同步到磁盘文件中。
  • 所以当服务重启时,它的数据不会丢失,当数据表被重新装载时,文件数据会再次被全量加载至内存。
  • Set表引擎具有去重的能力,在数据写入的过程中,重复的数据会被自动忽略。
  • Set表引擎的存储结构由两部分组成,它们分别是:
    • [num].bin数据文件:保存了所有列字段的数据。其中,num是一个自增id,从1开始。伴随着每一批数据的写入(每一次INSERT),都会生成一个新的.bin文件,num也会随之加1。
    • tmp临时目录:数据文件首先会被写到这个目录,当一批数据写入完毕之后,数据文件会被移出此目录。
Join
  • Join表引擎可以说是为JOIN查询而生的,它等同于将JOIN查询进行了一层简单封装。在Join表引擎的底层实现中,它与Set表引擎共用了大部分的处理逻辑,所以Join和Set表引擎拥有许多相似之处。
    • join_strictness:连接精度,它决定了JOIN查询在连接数据时所使用的策略,目前支持ALL、ANY和ASOF三种类型。
    • join_type:连接类型,它决定了JOIN查询组合左右两个数据集合的策略,它们所形成的结果是交集、并集、笛卡儿积或其他形式,目前支持INNER、OUTER和CROSS三种类型。当join_type被设置为ANY时,在数据写入时,join_key重复的数据会被自动忽略。
    • join_key:连接键,它决定了使用哪个列字段进行关联。

日志类型

TinyLog
  • TinyLog是日志家族系列中性能最低的表引擎,它的存储结构由数据文件和元数据两部分组成。
    • 数据文件是按列独立存储的,也就是说每一个列字段都拥有一个与之对应的.bin文件。
    • TinyLog既不支持分区,也没有.mrk标记文件
    • 由于没有标记文件,它自然无法支持.bin文件的并行读取操作,所以它只适合在非常简单的场景下使用
StripeLog
  • StripeLog表引擎的存储结构由固定的3个文件组成,它们分别是:

    data.bin:数据文件,所有的列字段使用同一个文件保存,它们的数据都会被写入data.bin。

    index.mrk:数据标记,保存了数据在data.bin文件中的位置信息。利用数据标记能够使用多个线程,以并行的方式读取data.bin内的压缩数据块,从而提升数据查询的性能。

    sizes.json:元数据文件,记录了data.bin和index.mrk大小的信息。

Log
  • Log表引擎结合了TinyLog表引擎和StripeLog表引擎的长处,是日志家族系列中性能最高的表引
    擎。

  • Log表引擎的存储结构由3个部分组成:

    [column].bin:数据文件,数据文件按列独立存储,每一个列字段都拥有一个与之对应的.bin文件。

    marks.mrk:数据标记,统一保存了数据在各个[column].bin文件中的位置信息。利用数据标记能够使用多个线程,以并行的方式读取.bin内的压缩数据块,从而提升数据查询的性能。

    sizes.json:元数据文件,记录了[column].bin和__marks.mrk大小的信息。

接口类型

Merge
  • 在数据仓库的设计中,数据按年分表存储,例如test_table_2018、test_table_2019和test_table_2020。假如现在需要跨年度查询这些数据 ?

  • Merge表引擎就如同一层使用了门面模式的代理,它本身不存储任何数据,也不支持数据写入。

    • 它的作用就如其名,即负责合并多个查询的结果集。
    • Merge表引擎可以代理查询任意数量的数据表,这些查询会异步且并行执行,并最终合成一个
      结果集返回
  • 被代理查询的数据表被要求处于同一个数据库内,且拥有相同的表结构,但是它们可以使用不同的
    表引擎以及不同的分区定义

    ENGINE = Merge(database, table_name)

    database表示数据库名称;
    table_name表示数据表的名称,它支持使用正则表达式

数据查询方式

  • 在日常运转的过程中,数据查询也是ClickHouse的主要工作之一。ClickHouse完全使用SQL作为查
    询语言,能够以SELECT查询语句的形式从数据库中选取数据,这也是它具备流行潜质的重要原
    因。虽然ClickHouse拥有优秀的查询性能,但是我们也不能滥用查询,掌握ClickHouse所支持的各
    种查询子句,并选择合理的查询形式是很有必要的。使用不恰当的SQL语句进行查询不仅会带来低
    性能,还可能导致不可预知的系统错误。

  • ClickHouse对于SQL语句的解析是大小写敏感的,这意味着SELECT a和SELECT A表示的语义是不
    相同的。

  • ClickHouse目前支持的查询子句如下所示:

    [WITH expr |(subquery)]
    SELECT [DISTINCT] expr
    [FROM [db.]table | (subquery) | table_function] [FINAL]
    [SAMPLE expr]
    [[LEFT] ARRAY JOIN]
    [GLOBAL] [ALL|ANY|ASOF] [INNER | CROSS | [LEFT|RIGHT|FULL [OUTER]] ]
    JOIN (subquery)|table ON|USING columns_list
    [PREWHERE expr]
    [WHERE expr]
    [GROUP BY expr] [WITH ROLLUP|CUBE|TOTALS]
    [HAVING expr]
    [ORDER BY expr]
    [LIMIT [n[,m]]
    [UNION ALL]
    [INTO OUTFILE filename]
    [FORMAT format]
    [LIMIT [offset] n BY columns]
    
    • 方括号包裹的查询子句表示其为可选项,所以只有SELECT子句是必须的,
    • ClickHouse对于查询语法的解析也大致是按照上面各个子句排列的顺序进行的。

副本与分片

个查询的结果集。

  • Merge表引擎可以代理查询任意数量的数据表,这些查询会异步且并行执行,并最终合成一个
    结果集返回

  • 被代理查询的数据表被要求处于同一个数据库内,且拥有相同的表结构,但是它们可以使用不同的
    表引擎以及不同的分区定义

    ENGINE = Merge(database, table_name)

    database表示数据库名称;
    table_name表示数据表的名称,它支持使用正则表达式

数据查询方式

  • 在日常运转的过程中,数据查询也是ClickHouse的主要工作之一。ClickHouse完全使用SQL作为查
    询语言,能够以SELECT查询语句的形式从数据库中选取数据,这也是它具备流行潜质的重要原
    因。虽然ClickHouse拥有优秀的查询性能,但是我们也不能滥用查询,掌握ClickHouse所支持的各
    种查询子句,并选择合理的查询形式是很有必要的。使用不恰当的SQL语句进行查询不仅会带来低
    性能,还可能导致不可预知的系统错误。

  • ClickHouse对于SQL语句的解析是大小写敏感的,这意味着SELECT a和SELECT A表示的语义是不
    相同的。

  • ClickHouse目前支持的查询子句如下所示:

    [WITH expr |(subquery)]
    SELECT [DISTINCT] expr
    [FROM [db.]table | (subquery) | table_function] [FINAL]
    [SAMPLE expr]
    [[LEFT] ARRAY JOIN]
    [GLOBAL] [ALL|ANY|ASOF] [INNER | CROSS | [LEFT|RIGHT|FULL [OUTER]] ]
    JOIN (subquery)|table ON|USING columns_list
    [PREWHERE expr]
    [WHERE expr]
    [GROUP BY expr] [WITH ROLLUP|CUBE|TOTALS]
    [HAVING expr]
    [ORDER BY expr]
    [LIMIT [n[,m]]
    [UNION ALL]
    [INTO OUTFILE filename]
    [FORMAT format]
    [LIMIT [offset] n BY columns]
    
    • 方括号包裹的查询子句表示其为可选项,所以只有SELECT子句是必须的,
    • ClickHouse对于查询语法的解析也大致是按照上面各个子句排列的顺序进行的。

副本与分片

在这里插入图片描述

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值