ClickHouse 是开源的一个极具 " 战斗力 " 的实时数据分析数据库,开发语言为C++,是一个用于联机分析 (OLAP:Online Analytical Processing) 的列式数据库管理系统(DBMS:Database Management System),简称 CK。
ClickHouse 的性能超过了目前市场上可比的面向列的DBMS。 每秒钟每台服务器每秒处理数 亿至十亿多行和数十千兆字节的数据。
目前国内社区火热,各个大厂纷纷跟进大规模使用:
Column
和Field
是ClickHouse
数据最基础的映射单元。CREATE DATABASE IF NOT EXISTS db_name [ENGINE = engine]
SHOW DATABASES
DROP DATABASE [IF EXISTS] db_name
CREATE TABLE [IF NOT EXISTS] [db_name.]table_name (
name1 [type] [DEFAULT|MATERIALIZED|ALIAS expr],
name2 [type] [DEFAULT|MATERIALIZED|ALIAS expr],
省略…
) ENGINE = engine
CREATE TABLE hits_v1 (
Title String,
URL String ,
EventTime DateTime
) ENGINE = Memory;
第二种定义方法是复制其他表的结构
CREATE TABLE [IF NOT EXISTS] [db_name1.]table_name AS [db_name2.]
table_name2 [ENGINE = engine]
--创建新的数据库
CREATE DATABASE IF NOT EXISTS new_db
--将default.hits_v1的结构复制到new_db.hits_v1
CREATE TABLE IF NOT EXISTS new_db.hits_v1 AS default.hits_v1 ENGINE =
TinyLog
第三种定义方法是通过SELECT子句的形式创建
CREATE TABLE [IF NOT EXISTS] [db_name.]table_name ENGINE = engine AS
SELECT …
CREATE TABLE IF NOT EXISTS hits_v1_1 ENGINE = Memory AS SELECT * FROM
hits_v1
DROP TABLE [IF EXISTS] [db_name.]table_name
TEMPORARY
关键字。CREATE TEMPORARY TABLE [IF NOT EXISTS] table_name ( name1 [type] [DEFAULT|MATERIALIZED|ALIAS expr], name2 [type] [DEFAULT|MATERIALIZED|ALIAS expr],)
追加新字段
ALTER TABLE tb_name ADD COLUMN [IF NOT EXISTS] name [type] [default_expr] [AFTER name_after]
ALTER TABLE testcol_v1 ADD COLUMN OS String DEFAULT 'mac'ALTER TABLE testcol_v1 ADD COLUMN IP String AFTER ID
修改字段类型
ALTER TABLE tb_name MODIFY COLUMN [IF EXISTS] name [type] [default_expr]
ALTER TABLE testcol_v1 MODIFY COLUMN IP IPv4
修改备注
ALTER TABLE tb_name COMMENT COLUMN [IF EXISTS] name 'some comment'
ALTER TABLE testcol_v1 COMMENT COLUMN ID '主键ID'
DESC testcol_v1
删除已有字段
ALTER TABLE tb_name DROP COLUMN [IF EXISTS] name
ALTER TABLE testcol_v1 DROP COLUMN URL
清空数据表
TRUNCATE TABLE [IF EXISTS] [db_name.]tb_name
TRUNCATE TABLE db_test.testcol_v2
ClickHouse拥有普通和物化两种视图,其中物化视图拥有独立的存储,而普通视图只是一层简单的查询代理。
普通视图
CREATE VIEW [IF NOT EXISTS] [db_name.]view_name AS SELECT ...
普通视图不会存储任何数据,它只是一层单纯的SELECT查询映射,起着简化查询、明晰语义 的作用,对查询性能不会有任何增强。
物化视图
CREATE [MATERIALIZED] VIEW [IF NOT EXISTS] [db.]table_name [TO[db.]name]
[ENGINE = engine] [POPULATE] AS SELECT .
物化视图支持表引擎,数据保存形式由它的表引擎决定 ;
物化视图创建好之后,如果源表被写入新数据,那么物化视图也会同步更新。
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()
ClickHouse提供了DELETE和UPDATE的能力,这类操作被称为Mutation查询,它可以看作ALTER 语句的变种。
虽然Mutation能最终实现修改和删除,但不能完全以通常意义上的UPDATE和DELETE来理解,我 们必须清醒地认识到它的不同:
DELETE语句的完整语法
ALTER TABLE [db_name.]table_name DELETE WHERE filter_expr
ALTER TABLE partition_v2 DELETE WHERE ID = 'A003'
UPDATE语句的完整语法
ALTER TABLE [db_name.]table_name UPDATE column1 = expr1 [, ...] WHERE filter_expr
ALTER TABLE partition_v2 UPDATE URL = 'www.wayne.com',OS = 'mac' WHERE ID IN(10,20,30)
合并树
、外部存储
、内存
、文件
、接口
和其他6大类20多种表引擎。主键索引
、数据分区
、数据副本
和数据采样
等基本能力;CREATE TABLE [IF NOT EXISTS] [db_name.]table_name (
name1 [type] [DEFAULT|MATERIALIZED|ALIAS expr],
name2 [type] [DEFAULT|MATERIALIZED|ALIAS expr],
省略...
) ENGINE = MergeTree()
[PARTITION BY expr]
[ORDER BY expr]
[PRIMARY KEY expr]
[SAMPLE BY expr]
[SETTINGS name=value, 省略...]
-- 实例
CREATE TABLE IF NOT EXISTS yjxxt.mt (
mid String, mname String,
createtime DateTime)
ENGINE = MergeTree()
PARTITION BY toYYYYMM(createtime)
ORDER BY (mid,mname)
PRIMARY KEY mid;
insert into yjxxt.mt values ('1','zs','2021-05-02'),('2','ls','2021-06-02'),('3','ww','2021-06-04');
-- 可以通过system.tables 查看表的元数据信息
MergeTree表引擎中的数据是拥有物理存储的,数据会按照分区目录的形式保存到磁盘之上。
一张数据表的完整物理结构分为3个层级,依次是数据表目录、分区目录及各分区下具体的数据文件;
partition:分区目录,余下各类数据文件(primary.idx、[Column].mrk、[Column].bin等)
一个完整分区目录的命名公式
PartitionID_MinBlockNum_MaxBlockNum_Level
MarkRange
索引查询其实就是两个数值区间的交集判断。
查询步骤
生成查询条件区间:首先,将查询条件转换为条件区间。
WHERE ID = 'A003'
['A003', 'A003']
递归交集判断:以递归的形式,依次对MarkRange的数值区间与条件区间做交集判断。从最 大的区间[A000,+inf)开始:
合并MarkRange区间:将最终匹配的MarkRange聚在一起,合并它们的范围。
MergeTree共支持4种跳数索引,分别是minmax、set、ngrambf_v1和tokenbf_v1。一张数据表支持同时声明多个跳数索引。
CREATE TABLE skip_test (
ID String,
URL String,
Code String,
EventTime Date,
INDEX a ID TYPE minmax GRANULARITY 5,
INDEX b(length(ID) * 8) TYPE set(2) GRANULARITY 5,
INDEX c(ID,Code) TYPE ngrambf_v1(3, 256, 2, 0) GRANULARITY 5,
INDEX d ID TYPE tokenbf_v1(256, 2, 0) GRANULARITY 5
) ENGINE = MergeTree()
minmax
set
ngrambf_v1:
tokenbf_v1:
每个压缩数据块的体积,按照其压缩前的数据字节大小,都被严格控制在64KB~1MB。
数据写入过程
数据标记和索引区间是对齐的,均按照index_granularity的粒度间隔。
为了能够与数据衔接,数据标记文件也与.bin文件一一对应。
一行标记数据使用一个元组表示,元组内包含两个整型数值的偏移量信息。
每一行标记数据都表示了一个片段的数据(默认8192行)在.bin压缩文件中的读取位置信息。标记 数据与一级索引数据不同,它并不能常驻内存,而是使用LRU(最近最少使用)缓存策略加快其取用速度。
标记查看命令
od -An -l name.mrk
TTL即Time To Live,顾名思义,它表示数据的存活时间。在MergeTree中,可以为某个列字段或整张表设置TTL。
设置TTL
INTERVAL完整的操作包括SECOND、MINUTE、HOUR、DAY、WEEK、MONTH、QUARTER 和YEAR。
ClickHouse没有提供取消TTL的方法。
列级别设置TTL
-- create_time是日期类型,列字段code与type均被设置了TTL,它们的存活时间是在
-- create_time的取值基础之上向后延续10秒
CREATE TABLE ttl_table_v1(
id String,
create_time DateTime,
code String TTL create_time + INTERVAL 10 SECOND,
type UInt8 TTL create_time + INTERVAL 10 SECOND
)
ENGINE = MergeTree
PARTITION BY toYYYYMM(create_time)
ORDER BY id;
INSERT INTO TABLE ttl_table_v1 VALUES('A000',now(),'C0',1), ('A001',now() + INTERVAL 10 MINUTE,'C1',1);
SELECT * FROM ttl_table_v1
-- 执行optimize命令强制触发TTL清理
-- 第一行数据满足TTL过期条件(当前系统时间>=create_time+10秒),它们的code和
-- type列会被还原为数据类型的默认值:
optimize TABLE ttl_table_v1 FINAL
-- 修改列字段的TTL,或是为已有字段添加TTL,则可以使用ALTER语句
ALTER TABLE ttl_table_v1 MODIFY COLUMN code String TTL create_time + INTERVAL 1 DAY
表级别设置TTL
--整张表被设置了TTL,当触发TTL清理时,那些满足过期时间的数据行将会被整行删除CREATE TABLE ttl_table_v2( id String, create_time DateTime, code String TTL create_time + INTERVAL 1 MINUTE, type UInt8)ENGINE = MergeTreePARTITION BY toYYYYMM(create_time)ORDER BY create_timeTTL create_time + INTERVAL 1 DAY-- 修改表级别的TTLALTER TABLE ttl_table_v2 MODIFY TTL create_time + INTERVAL 3 DAY
TTL的运行机制
<storage_configuration>
<disks>
<disk_name_a>
<path>/chbase/datapath><!—磁盘路径 -->
<keep_free_space_bytes>1073741824keep_free_space_bytes>
disk_name_a>
<disk_name_b>
<path>… path>
<keep_free_space_bytes>...keep_free_space_bytes>
disk_name_b>
disks>
<policies>
<default_jbod>
<volumes>
<jbod> <!—自定义名称 磁盘组 -->
<disk>disk_name_adisk>
<disk>disk_name_bdisk>
jbod>
volumes>
default_jbod>
policies>
storage_configuration>
将需要聚合的数据,预先计算出来,并将结果保存起来。
在后续进行聚合查询的时候,直接使用结果数据。
-- GROUP BY id,city
-- UNIQ(code), SUM(value)
CREATE TABLE agg_table(
id String,
city String,
code AggregateFunction(uniq,String),
value AggregateFunction(sum,UInt32),
create_time DateTime
)ENGINE = AggregatingMergeTree()
PARTITION BY toYYYYMM(create_time)
ORDER BY (id,city)
PRIMARY KEY id
AggregatingMergeTree更为常见的应用方式是结合物化视图使用,将它作为物化视图的表引擎。
是一种通过以增代删的思路,支持行级数据修改和删除的表引擎。它通过定义一个sign标记位字段,记录数据行的状态。
如果sign标记为1,则表示这是一行有效的数据;如果sign标记为-1,则表示这行数据需要被删除。
分区合并时,同一数据分区内,sign标记为1和-1的一组数据会被抵消删除。
这种1和-1相互抵消的操作,犹如将一张瓦楞纸折叠了一般。
ENGINE = CollapsingMergeTree(sign)
只有相同分区内的数据才有可能被折叠。
折叠规则
特点
折叠数据并不是实时触发的,和所有其他的MergeTree变种表引擎一样,这项特性也只有在分区合并的时候才会体现。
所以在分区合并之前,用户还是会看到旧的数据。解决这个问题的方式有两种。
用optimize TABLE table_name FINAL命令强制分区合并。
GROUP BY idHAVING SUM(sign) > 0
CollapsingMergeTree对于写入数据的顺序有着严格要求。
-- 在HDFS上创建用于存放文件的目录
hadoop fs -mkdir /clickhouse
-- 在HDFS上给ClickHouse用户授权
hadoop fs -chown -R clickhouse:clickhouse /clickhouse
-- hdfs_uri表示HDFS的文件存储路径,format表示文件格式
ENGINE = HDFS(hdfs_uri,format)CREATE TABLE hdfs_table1(
id UInt32,
code String,
name String
)ENGINE = HDFS('hdfs://node01:8020/clickhouse/hdfs_table1','CSV');
INSERT INTO hdfs_table1 SELECT number,concat('code',toString(number)),concat('n',toString(number)) FROM numbers(5)
-- 其他方式,只能读取
ENGINE = HDFS('hdfs://hdp1.nauu.com:8020/clickhouse/hdfs_table2/*','CSV')
ENGINE = HDFS('hdfs://hdp1.nauu.com:8020/clickhouse/hdfs_table2/organization_{1..3}.csv','CSV')
ENGINE = HDFS('hdfs://hdp1.nauu.com:8020/clickhouse/hdfs_table2/organization_?.csv','CSV')
MySQL表引擎可以与MySQL数据库中的数据表建立映射,并通过SQL向其发起远程查询,包括 SELECT和INSERT
ENGINE = MySQL(
'host:port', 'database',
'table', 'user',
'password'[replace_query, 'on_duplicate_clause'] )
CREATE TABLE mysql_dept(
deptno UInt32,
dname String,
loc String
)ENGINE = MySQL('192.168.88.101:3306', 'scott', 'dept', 'root','123456');
SELECT * FROM mysql_dept INSERT INTO TABLE mysql_dept VALUES (50,'干饭部','207')
-- 目前MySQL表引擎不支持任何UPDATE和DELETE操作
ENGINE = Kafka()
SETTINGS
kafka_broker_list = 'host:port,... ',
kafka_topic_list = 'topic1,topic2,...',
kafka_group_name = 'group_name',
kafka_format = 'data_format'[,]
[kafka_row_delimiter = 'delimiter_symbol']
[kafka_schema = '']
[kafka_num_consumers = N]
[kafka_skip_broken_messages = N]
[kafka_commit_every_batch = N]
必填参数:
再次执行SELECT查询会发现kafka_test数据表空空如也,这是因为Kafka表引擎在执行查询之后就会删除表内的数据。
-- 自动创建
CREATE TABLE file_table ( name String, value UInt32) ENGINE = File("CSV")INSERT INTO file_table VALUES ('one', 1), ('two', 2), ('three', 3)
-- 手动创建
//创建表目录
mkdir /chbase/data/default/file_table1
//创建数据文件#
mv /chbase/data/default/file_table/data.CSV/chbase/data/default/file_table1
ATTACH TABLE file_table1(
name String,
value UInt32
)ENGINE =
File(CSV)INSERT INTO file_table1 VALUES ('four', 4), ('five', 5)
Memory表引擎直接将数据保存在内存中,数据既不会被压缩也不会被格式转换,数据在内存中保存的形态与查询时看到的如出一辙。
当ClickHouse服务重启的时候,Memory表内的数据会全部丢失。
当数据被写入之后,磁盘上不会创建任何数据文件。 操作方式。
CREATE TABLE memory_1 (id UInt64)ENGINE = Memory()
CREATE TABLE set_1 (
id UInt8
)ENGINE = Set()
INSERT INTO TABLE set_1 SELECT number FROM numbers(10)
SELECT arrayJoin([1, 2, 3]) AS a WHERE a IN set_1
ENGINE = Join(join_strictness, join_type, key1[, key2, ...])
CREATE TABLE tinylog_1 (
id UInt64,
code UInt64
)ENGINE = TinyLog()
INSERT INTO TABLE tinylog_1 SELECT number,number+1 FROM numbers(100)
CREATE TABLE spripelog_1 (
id UInt64,
price Float32
)ENGINE = StripeLog()
INSERT INTO TABLE spripelog_1 SELECT number,number+100 FROMnumbers(1000)
CREATE TABLE log_1 (
id UInt64,
code UInt64
)ENGINE = Log()
INSERT INTO TABLE log_1 SELECT number,number+1 FROM numbers(200)
ENGINE = Merge(database, table_name)
-- database表示数据库名称;
-- table_name表示数据表的名称,它支持使用正则表达式
[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]
支持CTE(Common Table Expression,公共表表达式),以增强查询语句的表达。
在改用CTE的形式后,可以极大地提高语句的可读性和可维护性。
定义变量
这些变量能够在后续的查询子句中被直接访问。
WITH 10 AS start
SELECT number FROM system.numbers
WHERE number > start
LIMIT 5
调用函数
访问SELECT子句中的列字段,并调用函数做进一步的加工处理。
WITH SUM(data_uncompressed_bytes) AS bytes
SELECT database , formatReadableSize(bytes) AS format FROM
system.columns
GROUP BY database
ORDER BY bytes DESC
定义子查询
-- 子查询
WITH (
SELECT SUM(data_uncompressed_bytes) FROM system.columns
) AS total_bytes
SELECT database , (SUM(data_uncompressed_bytes) / total_bytes) * 100
AS database_disk_usage
FROM system.columns
GROUP BY database
ORDER BY database_disk_usage DESC
子查询中重复(嵌套)使用WITH
嵌套使用WITH子句
WITH (
round(database_disk_usage)
) AS database_disk_usage_v1
SELECT database,database_disk_usage, database_disk_usage_v1
FROM (
-- 嵌套
WITH (
SELECT SUM(data_uncompressed_bytes) FROM system.columns
) AS total_bytes
SELECT database , (SUM(data_uncompressed_bytes) / total_bytes) * 100 AS database_disk_usage FROM system.colum
GROUP BY database
ORDER BY database_disk_usage DESC
)
FROM子句表示从何处读取数据,目前支持如下3种形式
SELECT WatchID FROM hits_v1
SELECT MAX_WatchID FROM (SELECT MAX(WatchID) AS MAX_WatchID FROM hits_v1)
SELECT number FROM numbers(5)
在ClickHouse中,并没有数据库中常见的DUAL虚拟表,取而代之的是system.one。
SELECT 1
SELECT 1 FROM system.one
在FROM子句后,可以使用Final修饰符。
-- Sample Key声明的表达式必须也包含在主键的声明中
-- Sample Key必须是Int类型,如若不是,ClickHouse在进行CREATE TABLE操作时也不会报
错
CREATE TABLE hits_v1 (
CounterID UInt64,
EventDate DATE,
UserID UInt64
) ENGINE = MergeTree()
PARTITION BY toYYYYMM(EventDate)
ORDER BY (CounterID, intHash32(UserID))
SAMPLE BY intHash32(UserID)
SAMPLE子句目前支持如下3种用法
SAMPLE factor
SAMPLE factor表示按因子系数采样,其中factor表示采样因子,它的取值支持0~1之间 的小数。
如果factor设置为0或者1,则效果等同于不进行数据采样。
SELECT count() * 10 FROM hits_v1 SAMPLE 0.1
SELECT CounterID, _sample_factor FROM hits_v1 SAMPLE 0.1 LIMIT 2
SAMPLE rows
SAMPLE rows表示按样本数量采样,其中rows表示至少采样多少行数据,它的取值必须 是大于1的整数。
如果rows的取值大于表内数据的总行数,则效果等于rows=1
SELECT count() FROM hits_v1 SAMPLE 10000
SELECT CounterID,_sample_factor FROM hits_v1 SAMPLE 100000 LIMIT 1
SAMPLE factor OFFSET n
SAMPLE factor OFFSET n表示按因子系数和偏移量采样,其中factor表示采样因子,n 表示偏移多少数据后才开始采样,它们两个的取值都是0~1之间的小数。
-- 最终的查询会从数据的0.5处开始,按0.4的系数采样数据
SELECT CounterID FROM hits_v1 SAMPLE 0.4 OFFSET 0.5
-- 最终的查询会从数据的二分之一处开始,按0.1的系数采样数据
SELECT CounterID,_sample_factor FROM hits_v1 SAMPLE 1/10 OFFSET 1/2
如果在计算OFFSET偏移量后,按照SAMPLE比例采样出现了溢出,则数据会被自动截断
CREATE TABLE query_v1(
title String,
value Array(Int8)
) ENGINE = Log
INSERT INTO query_v1 VALUES ('food', [1,2,3]), ('fruit', [3,4]), ('meat', []);
SELECT title,value FROM query_v1;
目前支持ALL、ANY和ASOF三种类型。如果不主动声明,则默认是ALL。
连接类型
查询优化
WHERE子句基于条件表达式来实现数据过滤。如果过滤条件恰好是主键字段,则能够进一步借助索引加速查询;
PREWHERE目前只能用于MergeTree系列的表引擎,它可以看作对WHERE的一种优化,其作用与WHERE相同,均是用来过滤数据。
ClickHouse实现了自动优化的功能,会在条件合适的情况下将WHERE替换为PREWHERE。
如果想开启这项特性,需要将optimize_move_to_prewhere设置为1
GROUP BY又称聚合查询
聚合查询目前还能配合WITH ROLLUP、WITHCUBE和WITH TOTALS三种修饰符获取额外的汇总信
息。
ORDER BY子句通过声明排序键来指定查询数据返回时的顺序。
ORDER BY在使用时可以定义多个排序键,每个排序键后需紧跟ASC(升序)或DESC(降序)来确定排列顺序。如若不写,则默认为ASC(升序)。
空值处理
NULLS LAST
数据的排列顺序为其他值(value)→NaN→NULL。
NULLS FIRST
数据的排列顺序为NULL→NaN→其他值(value)
ORDER BY v1 DESC NULLS FIRST
LIMIT BY子句和大家常见的LIMIT所有不同,它运行于ORDER BY之后和LIMIT之前,能够按照指定分组,最多返回前n行数据(如果数据少于n行,则按实际数量返回)
常用于TOP N的查询场景。LIMIT BY的常规语法如下:
LIMIT n BY express
LIMIT子句用于返回指定的前n行数据,常用于分页场景
LIMIT n
SELECT number FROM system.numbers LIMIT 10
LIMIT n OFFSET m
SELECT number FROM system.numbers LIMIT 10 OFFSET 5
LIMIT m,n
SELECT number FROM system.numbers LIMIT 5 ,10