文档
官网地址:Fast Open-Source OLAP DBMS - ClickHouse
文档地址:ClickHouse中文文档 | ClickHouse中文文档 (roadgulf.cn)
可视化工具:DBeaver
优点
1.灵活的MPP架构(大模型并行处理),支持现行扩展,简单方便,高可靠性;
2.多服务器分布式处理数据,完备的DBMS系统(数据库管理系统);
3.底层数据列示存储,支持压缩,优化数据存储,优化索引数据,优化底层存储;
4.支持数据统计分析,支持类SQL查询,异地复制部署,分布式运算,出色的函数支持;
缺点
- 不支持事物,不支持真正的删除/更新(批量);
2.不支持高并发,官方建议QPS为100,可通过修改配置文件增加连接数;
3.不支持二级索引;
4.不擅长多表join大宽表;
5.元数据管理需要人为干涉;
6.尽量做1000条以上批量的写入,避免逐行insert或小批量的insert、update、delete
应用场景
1.绝大多数请求都是用于读访问的,要求实时返回结果;
2.数据需要以大批次(大于1000)进行更新,而不是单行更新,或者没有更新操作
3.数据只是添加到数据库,没有必要修改;
- 读数据时,会从数据库中读取大量的行,但只用到一小部分列;
5.表很"宽",即表中包含大量的列;
6.查询频率相对较低(通常没台服务器每秒查询数百次或更少);
7.对于简单查询,允许大约50毫秒的延迟;
8.列的值是比较小的数值或字符串(例如:每个URL只有60个字节);
9.在处理单个查询时需要高吞吐量(每台服务器每秒高达数十亿行);
10.不需要事物;
11.数据一致性较低(原子性、持久性、一致性、隔离性)
12.每次查询中只会查询一个大表,除了一个大表,其余都是小表;
13.查询结果显著小于数据源,即数据有过滤或聚合,返回结果不超过单个服务器内存小;
核心概念
1)数据分片:将数据进行横向切分,每个分片对应了Clickhouse的1个服务节点,还提供了本地表(Local Table)与分布式表(Distributed Table)的概念,本地表等同于一份数据的切片,而分布式表本身不存储数据,它是本地表的访问代理,类似分库中间件,借助分布式表,能够访问多个数据切片,从而实现分布式查询。
2)列式存储:a.同一列中的数据属于同一类型,列存的压缩比更高,节省了大量存储空间;b.压缩比高意味着更小的data size,从磁盘中读数据时间更短;c.可以根据根据不同列的类型选择最合适的压缩算法;
3)向量化:Clickhouse不仅将数据按列存储,而且按列计算,将多次for循环计算变成一次计算;
4)表;
5)分区:ClickHouse支持PARTITION BY字句,在建表时可以按照表达式进行数据分区操作,比如:toYYYYMM()按月分区,toMonday()按周分区,对num类型的列分区,数据以分区的形式统一管理和维护一批数据;
6)副本:通过复制集,保障数据的可靠性,也通过多副本的方式,增加了CK查询的并发能力,a.分布式DDL执行、ReplicatedMergeTree表主备节点之间的状态同步; c.并发访问数据;d.索引的使用;e.是否可以执行多线程请求;f.数据是否存储副本;g.并发操作insert into tb_x select * from tb_x 表引擎决定数据在文件系统中的存储方式,官方推荐的存储引擎是MergeTree系列,数据副本用ReplicatedMergeTree系列,读取集群数据需要使用分布式表引擎Distribute;
7)block:ClickHouse能处理的最小单位是block,block是一群行的集合,默认最大为8192行。因为每一列单独存储,因此每个数据文件相比于行式存储更有规律,通过对block采用LZ4压缩算法,整体压缩比大致可以8:1。ClickHouse通过出色的压缩比与block结构实现了批处理功能;
8)LSM:LSM的原理:把一颗大树拆分成N棵小树,数据先写入内存中,随着小树越来越大,内存的小树会flush到磁盘中。磁盘中的树定期做合并操作,合并成一棵大树。ClickHouse通过LSM实现数据的预排序,从而减少磁盘的读取量;ClickHouse的写入步骤可以总结为以下几点:a.每一批次数据写入,先记录日志,保证高可用机制;b.记录日志之后存入内存排序,后将有序结果写入磁盘,记录合并次数Level=0; c.定期将磁盘上Level=0或1的文件合并,并标记删除,后续物理删除;
9)索引:ClickHouse的采用一级索引(稀疏索引)+二级索引(跳数索引)来实现索引数据定位与查询。一级索引记录每个block块的第一个,每次基于索引字段查询只需要确定查询第几个block块即可,避免一个查询遍历所有数据,一级索引占用存储较小,可常驻内存,加速查询。二级索引由数据的聚合信息构建而成,根据索引类型的不同,其聚合信息的内容也不同,跳数索引的目的与一级索引一样,也是帮助查询时减少数据扫描的范围,原则都是"排除法",即尽可能的排除那些一定不满足条件的索引粒度。
分布式
分布式表的特点:1.表存储多个副本且有并发操作;2表特别大有多个切片组成,并且每个切片也可以存储多个副本;3.本地表是承接数据的载体,可以使用非Distributed引擎,一个本地表对应了一个数据分片;4.分布式表只能用Distributed引擎,与本地表形成一对多的映射关系,通过分布式表代理操作多张本地表;
sql
<!--第一步 增加配置文件-->
<remote_servers>
<ycloud_clickhouse_cluster> <!--集群名称-->
<shard> <!--集群的第一个分片-->
<internal_replication>true</internal_replication>
<replica> <!--该分片的第一个副本-->
<host>192.168.1.1</host>
<port>9920</port>
<user>default</user>
<password>ycloud_123</password>
</replica>
</shard>
<shard> <!--集群的第二个分片-->
<internal_replication>true</internal_replication>
<replica> <!--该分片的第一个副本-->
<host>192.168.1.2</host>
<port>9920</port>
<user>default</user>
<password>ycloud_123</password>
</replica>
</shard>
<shard> <!--集群的第三个分片-->
<internal_replication>true</internal_replication>
<replica> <!--该分片的第一个副本-->
<host>192.168.1.3</host>
<port>9920</port>
<user>default</user>
<password>ycloud_123</password>
</replica>
</shard>
</ycloud_clickhouse_cluster>
</remote_servers>
<!--第二步 创建本地表-->
----创建本地表
create table tb_demo on cluster 集群名(
id Int8
name STring
)engine=MergeTree()
order by id;
<!--第三步 创建分布式表-->
---创建分布式表与本地表映射
create table demo_all on cluster 集群名 ENGINE=Distributed('集群名','test','tb_demo',id) as tb_demo;
ENGINE=Distributed(cluster,database,table,[,sharding_key])
cluster:集群名
database和table:数据库名和表名,分布式表使用这组配置操作本地表
sharding_key:分片键(选填参数),在数据写入过程中,分布式表会依据分片键的规则,将数据分布到各个host节点的本地表
drop table if exists 表名 on cluster 集群名; ----删除集群中的表结构
alter table 表名 cluster 集群名 add column age Int8; ----修改集群中的表结构(添加字段)
文件目录结构
bash
程序在安装过程中会自动构建整套目录结构
1)/etc/clickhouse-ser 服务端配置文件目录
config.xml 全局配置
users.xml 用户配置
2)/var/lib/clickhouse 默认的数据存储目录(通常修改默认目录,将数据保存到大容量挂载的路径)
data 数据存储目录(数据库和表)
metadata 元数据信息
3)/var/log/clickhouse-server 默认保存日志目录
4)/usr/bin 默认添加进系统环境变量中
find ./ -name clickhouse
clickhouse 主程序可执行文件
clickhouse-client 一个指向clickhouse可执行文件的软连接,供客户端使用
clickhouse-server 一个指向clickhouse可执行文件的软连接,供服务端使用
clickhouse-compressor 内置提供的压缩工具,可用于数据的正压反解
引擎
**数据库引擎:**在建表时可以引入外部数据库,比如:MySQL引擎允许与远程MySQL服务器交互,支持INSERT和SELECT,不支持RENAME操作。PostgreSQL引擎类似,可与远程PostgreSQL服务进行读写操作。SQLite引擎用于连接SQLite数据库。实验性引擎如MaterializeMySQL和MaterializedPostgreSQL用于实现实时数据同步。
表引擎:
MergeTree系列
1)MergeTree(合并树):MergeTree表引擎主要用于海量数据分析,支持数据分区、存储有序、主键索引、稀疏索引、数据TTL(过期时间)等;问题:该表的相同主键无法去重;
2)ReplacingMergeTree(去重合并树):为了解决MergeTree相同主键无法去重的问题,clickhouse用ReplacingMergeTree做去重,问题:a.在没有彻底optimize之前,可能无法达到主键去重的效果,比如部分数据已经被去重,仍旧有主键重复b.在分布式场景下,相同primary key的数据可能被sharding到不同节点上,不同shard间可能无法去重;c.optimize是后台动作,无法预测具体执行时间点;c.手动执行optimize在海量数据场景下要消耗大量时间,无法满足业务即时查询的需求;
3)SummingMergeTree(聚合合并树):通过SummingMergeTree来支持对主键列进行预先聚合。在后台Compaction时,会将主键相同的多行进行sum求和;问题:ClickHouse只在后台Compaction时才会进行数据的预先聚合,而compaction的执行时机无法预测,所以可能存在部分数据已经被预先聚合、部分数据尚未被聚合的情况。因此,在执行聚合计算时,SQL中仍需要使用GROUP BY子句;在预先聚合时,ClickHouse会对主键列之外的其他所有列进行预聚合。如果这些列是可聚合的(比如数值类型),则直接sum;如果不可聚合(比如String类型),则随机选择一个值。通常建议SummingMergeTree与MergeTree配合使用;
4)AggregatingMergeTree(聚合合并树):也是预先聚合引擎的一种,用于提升聚合计算的性能,与SummingMergeTree的区别在于:SummingMergeTree对非主键列进行sum聚合,而AggregatingMergeTree则可以指定各种聚合函数;
5)CollapsingMergeTree(折叠合并树):该引擎要求在建表语句中指定一个标记列Sign,后台Compaction时会将主键相同、Sign相反的行进行折叠,也即删除。Sign的值分为两类:Sign=1状态行,Sign=-1取消行;
6)VersionedCollapsingMergeTree(版本折叠合并树):该引擎在建表语句中新增了一列Version,用于在乱序情况下记录状态行与取消行的对应关系,主键相同,且Version相同、Sign相反的行,在Compaction时会被删除;
Log系列
1)Log(日志):Log引擎会将每一列都存在一个文件中,对于每一次的INSERT操作,都会对应一个数据块。存储结构包含三部分:列.bin:数据文件按列单独存储;__marks.mrk:数据标记,统一保存了数据在各个.bin文件中的位置信息;sizes.json:记录了.bin和__marks.mrk大小的信息
2)StripeLog():将所有数据都存储在了一个文件中,对于每次的INSERT操作,ClickHouse会将数据块追加到表文件的末尾StripeLog引擎同样不支持ALTER UPDATE 和ALTER DELETE 操作;
3)TinyLog():该引擎适用于一次写入,多次读取的场景。对于处理小批数据的中间表可以使用该引擎;
**Memory(内存):**内存引擎,数据以未压缩的原始形式直接保存在内存中,服务器重启数据就会消失。读写操作不会相互阻塞,不支持索引;
视图
**普通试图(normal view):**不存储任何数据,只是一个子查询。当从视图读取数据时,实际是查询创建视图的子查询语句(创建视图的查询被用作from子句中的子查询);
**物化试图(materialized view):**物化视图实际存储了一份数据。用户查询的时候和表没有区别,更像是一张时刻在预计算的表。在创建物化视图的时候也需要定义存储引擎;
**实时视图(live view):**存储CREATE语句中SELECT查询的结果,并在查询结果更改时进行更新。
窗口视图(window view):
命令
bash
sudo /etc/init.d/clickhouse-server start --启动服务(方式一)
service clickhouse-server start --启动服务(方式二)
clickhouse-client -u(用户名,默认值default) -m(交互式客户端中可以执行多行函数) -h(服务端的host名称) -port(端口号,默认9000) -password(密码) -q(非交互式下的查询语句) -d(要操作的数据库,默认default) -f(使用指定的格式输出结果) -t(非交互模式下打印查询执行时间) -stacktrace(如果出现异常,打印堆栈信息) -config-file(配置文件的名称)
/*******************导入数据**********************/
cat file.txt | clickhouse-client -q 'insert into 库名.表名 format CSV' --将txt中的数据插入到表中(方式一)
clickhouse-client -q 'insert into 库名.表名 format CSV' < file.txt --将txt中的数据插入到表中(方式二)
clickhouse-client --format_csv_delimiter='_' -q 'insert into 库名.表名 format CSV' < file.txt --将txt中的数据插入到表中(方式三) 指定txt中字段之间的分隔符--format_csv_delimiter='_'
SQL语句
sql
show databasee; --查看数据库
create database 数据库名; --创建数据库
use 数据名; --进入数据库
select currentDatabase(); --查看当期使用的数据库(currentDatabase()是函数)
show tables; --查看表
show create table 表名; --查看建表语句
desc 表名; --查看表结构
drop table 表名; --删除表
select generateUUIDv4(),'变量名'; --生成随机字符串
select ['zss','dds','ww'] as names,toTypeName(names); //查看数据类型(方式一)
select array(1,2,3,4) as nums ,toTypeName(names); //查看数据类型(方式二)
--建表语句
create table 表名(
id1 UUID comment '随机字符串', --随机字符串
id2 UInt16 comment '没有标志位的int类型',
nane String, --姓名
cdate01 Date, --日期(格式为:2024-01-01)
cdate02 Date32, --日期(格式为:2024-01-01)
cdate02 DateTime, --秒(格式为:2024-01-01 11:12:23)
cdate02 DateTime64, --毫秒(格式为:2024-01-01 11:12:23.000)
gender Fixedstring(2) --性别
comm Float64 --工资
money Decimal(7,2) --金钱(Decimal比Float64精度高)
fs Array(String) comment '用户列表' --用户列表,数组类型(存字符串)
color Enum('RED'=1,'BLUE'=2), --颜色(枚举类型,只能插入括号中的内容,限制值的作用)
student Nested( --学生 Nested类型,对数组元素进行控制
sid UInt32,
sname String,
sage String
),
aa Tuple(s String,i Int64), --元组类型
ip IPv4, --ip类型(用于校验IP地址格式)
mm Map(String,UInt8) --map类型
)ENGINE=引擎名; --必须要指定存储引擎,引擎决定了数据存储位置、存储特点、使用方式等;
partition by toYYYYMMDD(create_time) --分区(按照创建时间)
primary key (id) --主键(不是唯一索引)
order by (id,sku_id) --按照字段排序(不指定主键,排序字段默认是主键)
/******************SQL语法***************************/
optimize table 表名 final; --合并压缩
insert into 表名 values(generateUUIDv4()); --插入UUID类型的格式(方式一)
insert into 表名 select generateUUIDv4(),'变量名'; --插入UUID类型的格式(方式二 )
insert into 表名 values('RED'),('BLUE'); --插入Enum('RED'=1,'BLUE'=2 )枚举类型(方式一)
insert into 表名 values(1),(2); --插入Enum('RED'=1,'BLUE'=2 )枚举类型(方式二)
insert into 表名 values([1,2,3],['zhd','zdf','fddf']); --插入Nested类型的方式
insert into 表名 values(('yy',12)),(('ss',22)) --插入Tuple类型的方式
insert into 表名 values('192.168.19.10'); --插入IP的方式
insert into 表名 values(map('ddd',123)); --插入map的方式(方式一)
insert into 表名 values({'fsd': 233}); --插入map的方式(方式二)
insert into 表名 values(array('lay','zytf')); --插入数组(字符串)类型
select fs.size0 from 表名; --用sql方式 查询数组长度
select length(fs) from 表名; --用函数方式 查询数组长度
select fs[1] from 表名; --用函数方式 根据下标值查数组数据(方式一)
select arrayElement[fs,1] from 表名; --用函数方式 根据下标值查数组数据(方式二)
select arrayMap(e->upper(e),fs) 表名; --用函数方式 将所有字符串转成大写
select aa.s,aa.i from 表名; --查询元组数据方式
select mm['ddd'] from 表名; --查询map的方式 根据key取值(方式一)
select arrayElement[mm,'ddd'] from 表名; --用函数方式 根key查map的值(方式二)
select (1.2+3) :: Int8; --类型强制转换
with 100 as number select money + number from 表名; --with定义变量在和表中的字段操作
with toYYYYMM(create_time) as c_t select c_t from 表名; --with定义变量通过函数修改字段的格式
with splitByString(',',name) as str_arr select str_arr from 表名; --将字符串转成数组
--array join将数组炸开(按行显示)
select
id,
name --炸开后的别名
from 表名
array join names as name; --names是表中的字段【Array(String)数组类型】
inner join --内连接
left outer join --左外连接
right outer join --右外连接
full outer join --全连接
cross join --交叉连接
--内关联 all(关联精度)全关联
select *
from 表名1
all join
表名2 on 表名1.id=表名2.id
--内关联 any(关联精度)任意,关联上就行
select *
from 表名1
any join
表名2 on 表名1.id=表名2.id --any是关联精度(任意,关联上就行)
--内关联 asof是一种模糊连接,允许在连接键之后追加定义一个模糊连接的匹配条件
select *
from 表名1
asof join
表名2 on (表名1.id=表名2.id) and (表名1.2 > 表名2.2)
/*********************创建试图***********************/
create view xx_view as select id,name,age from 表名; --创建普通试图(不会落盘)
create materialized view xx_view engine = 表引擎 populate as select * from 表名; --创建物化试图(落盘)【materialized(物化试图)、populate(数据同步) 】
/********************操作表结构************************/
alter table 表名 add colume name2 String; --添加字段
alter table 表名 drop colume name2 String; --删除字段
alter table 表名 modify colume age String default ''; --修改字段类型(将age修改为字符串)
alter table 表名 comment colume name2 '用户名'; --给字段添加注释
alter table 表名 drop where id =3; --根据ID删除数据
alter table 表名 update name2='java' where id=3 --根据id修改name的值
rename table tb_test to t1; --修改表名
rename table tb_test to t1,ttt1 to t2; --修改多张表名
rename table t2 to test1.t; --移动表到另一个库中
/**********************分区操作************************/
select name,table,partition from system.parts where table ='表名'; --查看分区信息
alter table 表名 drop partition '20240203'; --删除分区
alter table t2 REPLACE PARTITION '20240302' FROM t1 --根据分区将t1表的数据复制到t2
alter table 表名 clear colume name in partition '20240203'; --根据分区清空name字段数据
alter table 表名 detach partition '20240203'; --卸载分区(底层只是移动文件)
alter table 表名 attach partition '20240203'; --装载分区(底层只是移动文件)