大家好,我是小于哥,今天分享一篇关于一款MPP查询分析型数据库——ClickHouse。它是一个开源的,面向列的分析数据库,由Yandex为OLAP和大数据用例创建。ClickHouse对实时查询处理的支持使其适用于需要亚秒级分析结果的应用程序。ClickHouse的查询语言是SQL的一种方言,它支持强大的声明性查询功能,同时为最终用户提供熟悉度和较小的学习曲线。
它可以替代Elasticserach做深度聚合需求,性能表现不错,在数据量千万级亿级表现很好,且资源消耗相比之前降低不少,同样的服务器资源可以承担更多的业务需求。
今天就分享一篇关于ClickHouse原理及使用的帖子,很值得大家学习!
一、ClickHouse 概述
1.1 什么是ClickHouse
ClickHouse 是俄罗斯的 Yandex 于2016年开源的列式存储数据库(DBMS),主要用于在线分析处理查询(OLAP),能够使用SQL 查询实时生成分析数据报告。
1.2 什么是列式存储
以下面的表为例:
IdNameAge1张三182李四223王五34
采用行式存储时,数据在磁盘上的组织结构为:
好处是想查某个人所有的属性时,可以通过一次磁盘查找加顺序读取就可以。但是当想查所有人的年龄时,需要不停的查找,或者全表扫描才行,遍历的很多数据都是不需要的。
而采用列式存储时,数据在磁盘上的组织结构为:
这时想查所有人的年龄只需把年龄那一列拿出来就可以了。
1.3 安装前的准备
1.3.1 CentOS 取消打开文件数限制
在/etc/security/limits.conf、 /etc/security/limits.d/90-nproc.conf 这2个文件的末尾加入一下内容:
vi /etc/security/limits.conf
在文件末尾添加:
* soft nofile 65536
* hard nofile 65536
* soft nproc 131072
* hard nproc 131072
vi /etc/security/limits.d/90-nproc.conf
在文件末尾添加:
* soft nofile 65536
* hard nofile 65536
* soft nproc 131072
* hard nproc 131072
重启服务器之后生效,用ulimit -n 或者 ulimit -a 查看设置结果
ulimit -n
1.3.2 CentOS 取消SELINUX
SELINUX 是 CentOS 类似于windows 下的安全360
修改 /etc/selinux/config 中的 SELINUX=disabled 后重启
vi /etc/selinux/config
SELINUX=disabled
1.3.3 关闭防火墙
service iptables stop
service ip6tables stop
1.3.4 安装依赖
yum install -y libtool
yum install -y *unixODBC*
yum search libicu
yum install libicu.x86_64
二、安装
2.1 网址
官网:https://clickhouse.yandex/
下载地址:http://repo.red-soft.biz/repos/clickhouse/stable/el7/
2.2 单机模式
2.2.1 上传5个文件到 mkdir /usr/local/clickhouse
clickhouse-client-1.1.54236-4.el7.x86_64.rpm 09-Jun-2017 11:02 3.0K
clickhouse-compressor-1.1.54236-4.el7.x86_64.rpm 09-Jun-2017 11:02 880K
clickhouse-debuginfo-1.1.54236-4.el7.x86_64.rpm 09-Jun-2017 11:02 8.6M
clickhouse-server-1.1.54236-4.el7.x86_64.rpm 09-Jun-2017 11:02 32M
clickhouse-server-common-1.1.54236-4.el7.x86_64.rpm 09-Jun-2017 11:02 8.0K
2.2.2 分别安装这5个rpm 文件
rpm -ivh /usr/local/clickhouse/*.rpm
2.2.3 启动 ClickServer
前台启动:
clickhouse-server --config-file=/etc/clickhouse-server/config.xml
查看启动后的进程:ps -aux | grep click
后台启动:
nohup clickhouse-server --config-file=/etc/clickhouse-server/config.xml >null 2>&1 &
2.2.4 使用 client 连接server
clickhouse-client
2.3 分布式集群安装
2.3.1 在node-02、node-03 上面执行之前的所有步骤
2.3.2 三台机器修改配置文件 vi /etc/clickhouse-server/config.xml
<listen_host>0.0.0.0</listen_host>
<!-- <listen_host>::1</listen_host> -->
<!-- <listen_host>127.0.0.1</listen_host> -->
将刚刚修改好的配置分发到三台机器上去: ync.sh /etc/clickhouse-server/config.xml
2.3.3 在三台机器的etc 目录下新建 metrika.xml 文件
vi /etc/metrika.xml
添加如下内容:
<yandex>
<clickhouse_remote_servers>
<perftest_3shards_1replicas>
<shard>
<internal_replication>true</internal_replication>
<replica>
<host>node-01</host>
<port>9000</port>
</replica>
</shard>
<shard>
<replica>
<internal_replication>true</internal_replication>
<host>node-02</host>
<port>9000</port>
</replica>
</shard>
<shard>
<internal_replication>true</internal_replication>
<replica>
<host>node-03</host>
<port>9000</port>
</replica>
</shard>
</perftest_3shards_1replicas>
</clickhouse_remote_servers>
<!--zookeeper相关配置-->
<zookeeper-servers>
<node index="1">
<host>node-01</host>
<port>2181</port>
</node>
<node index="2">
<host>node-02</host>
<port>2181</port>
</node>
<node index="3">
<host>node-03</host>
<port>2181</port>
</node>
</zookeeper-servers>
<macros>
<replica>node-01</replica>
</macros>
<networks>
<ip>::/0</ip>
</networks>
<clickhouse_compression>
<case>
<min_part_size>10000000000</min_part_size>
<min_part_size_ratio>0.01</min_part_size_ratio>
<method>lz4</method>
</case>
</clickhouse_compression>
</yandex>
注意:下面标红的地方需要根据机器不同去修改,node-02就是修改成node-02, node-03就是修改成node-03
node-01
3.3.4 三台机器启 ClickServer
首先在三台机器开启Zookeeper
前台启动:
clickhouse-server --config-file=/etc/clickhouse-server/config.xml
查看启动后的进程:ps -aux | grep click
后台启动:
nohup clickhouse-server --config-file=/etc/clickhouse-server/config.xml >null 2>&1 &
集群搭建成功,进入clickhouse-client 客户端:
这里补充ClickHouse与Elasticsearch一样,都采用列式存储结构,都支持副本分片,不同的是ClickHouse底层有一些独特的实现,如下:
MergeTree 合并树表引擎,提供了数据分区、一级索引、二级索引。Vector Engine 向量引擎,数据不仅仅按列存储,同时还按向量(列的一部分)进行处理,这样可以更加高效地使用CPU。
关于Elasticsearch的索引比较好的一篇文章:
终端研发部:10分钟了解Elasticsearch实现文件的全文内容检索7 赞同 · 1 评论文章
三、数据类型
3.1 整型
固定长度的整型,包括有符号整型或无符号整型。Clickhouse 对大小写敏感。
整型范围(-2^(n-1) ~ 2^(n-1) - 1):
Int8
−128:127-128 : 127 Int16
−32768:32767-32768 : 32767 Int32
−2147483648:2147483647-2147483648 : 2147483647 Int64
−9223372036854775808:9223372036854775807-9223372036854775808 : 9223372036854775807
无符号整型范围(0 ~ 2^n -1)
UInt8
0:2550 : 255 UInt16
0:655350 : 65535 UInt32
0:42949672950 : 4294967295 UInt64
0:184467440737095516150 : 18446744073709551615
3.2 浮点型
Float32 - floatFloat64 - double
建议尽可能以整型形式存储数据。例如,将固定精度的数字转换为整数值,如果间用毫秒为单位表示,因为浮点型进行计算时可能引起四舍五入的误差。
与标准SQL 相比,ClickHouse 支持以下类别的浮点数:
Inf - 正无穷
-Inf - 负无穷
NaN - 非数字:
3.3 布尔型
没有单独的类型来存储布尔值。可以使用 UInt8 类型,取值限制为 0 或 1 。
3.4 字符串
1)String
字符串可以任意长度的。它可以包含任意的字节集,包含空字节。
2) FixedString(N)
固定长度N 的字符串,N 必须是严格的正自然数。当服务端读取长度小于N的字符串时候,通过在字符串末尾添加空字节来达到N 字节长度。当服务端读取长度大于 N 的字符串时候,将返回错误消息。
与String 相比,极少会使用FixedString,因为使用起来不是很方便。
3.5 枚举类型
ClickHouse 支持枚举类型,这是一种在定义常量时经常会使用的数据类型。ClickHouse 提供了Enum8 和Enum16 两种枚举类型,它们除了取值范围不同之外,别无二致。枚举固定使用(String:Int)Key/Value 键值对的形式定义数据,所以Enum8 和Enum16分别会对应(String:Int8)和(String:Int16)!
包括 Enum8 和 Enum16 类型。Enum 保存 string = integer 的对应关系。
Enum8 用 String=Int8 对描述。
Enum16 用 String=Int16 对描述。
用法演示:
创建一个带有一个枚举 Enum8(hello=1,world=2) 类型的列:
create table enum(enum Enum8(hello=0, enum=1))engine=TinyLog
示例2:
-- 枚举数据类型
CREATE TABLE test_enum(id Int8, color Enum8(red=1, green=2, blue=3)) engine=Memory;
insert into test_enum values(1,red),(2,red),(3,green);
-- 也可以使用这种方式进行插入数据:
insert into test_enum values(4,3);
insert into test_enum values(5,pink); -- 没有声明的值是不能插入
select id, toInt32(color) from test_enum;
-- 节省存储空间,提升处理效率;底层存储Int类型,占用空间最小
3.6 数组
Array(T):由T 类型元素组成的数组。
T 可以是任意类型,包含数组类型。但不推荐使用多维数组,ClickHouse 对多维数组的支持有限。例如,不能在MergeTree 表中存储多维数组。
可以使用 array 函数来创建数组:array(T)
也可以使用方括号:[]
-- 数组数据类型
array(T) 数组是强数据类型
e1,e2,d3...
toTypeName() -- 查看变量的数据类型
创建数组案例:
示例:
select array(1,2,3,4);
案例2:
CREATE TABLE test_array
(
name String,
hobby Array(String)
) ENGINE = Log
insert into test_array values
(张三,[读书,爬山,散步]),
(李四,[read,hiking,dance]),
(王五,array(吃,睡,喝,玩));
select * from test_array;
insert into test_array values (张三2,[读书,爬山,散步]);
insert into test_array values (李四2,[read,hiking,dance]);
insert into test_array values (王五2,array(吃,睡,喝,玩));
-- clickhouse 会利用多核处理器将数据分块存储、计算
select * from test_array;
-- 查询数组中的数据
select *, hobby[1] from test_array;
-- 遍历数组中每个元素,给每个元素加上abc
select arrayMap(e -> concat(e, abc), hobby) from test_array;
3.7 Tupe 元组
是一个特殊的数据类型,集合
可以存储任意的数据类型,在定义的时候声明数据类型和数据元素个数
元组类型由1~n 个元素组成,每个元素之间允许设置不同的数据类型,且彼此之间不要求兼容。无组同样支持类型推断,
其推断依据仍然以最小存储代码为原则。与数组类似,元组也可以使用两种方式定义
常规方式tuple(T):元组中可以存储多种数据类型,但是要注意数据类型的顺序
Tuple(String, UInt8, Date)
select tuple(1, abc, 12.33) as x, toTypeName(x);
┌─x───────────────┬─toTypeName(tuple(1, \abc\, 12.33))─┐
│ (1,abc,12.33) │ Tuple(UInt8, String, Float64) │
└─────────────────┴──────────────────────────────────────┘
select (1, 2, hello) as x, toTypeName(x);
┌─x─────────────┬─toTypeName(tuple(1, 2, \hello\))─┐
│ (1,2,hello) │ Tuple(UInt8, UInt8, String) │
└───────────────┴────────────────────────────────────┘
创建tuple表及查询示例:
-- 注意:建表的时候使用元组的需要制定元组的数据类型
create table test_tuple(
name String,
info Tuple(String, String, UInt8)
) engine=Memory;
insert into test_tuple values
(zss, (M, coder, 23)),
(lss, tuple(F, coder, 23));
select * from test_tuple;
┌─name─┬─info─────────────┐
│ zss │ (M,coder,23) │
│ lss │ (F,coder,23) │
└──────┴──────────────────┘
-- 通过下标查询tupe数据
select name, info.1, info.2, info.3 from test_tuple;
┌─name─┬─tupleElement(info, 1)─┬─tupleElement(info, 2)─┬─tupleElement(info, 3)─┐
│ zss │ M │ coder │ 23 │
│ lss │ F │ coder │ 23 │
└──────┴───────────────────────┴───────────────────────┴───────────────────────┘
3.8 Nested 嵌套表结构
Nested 是一种嵌套表结构。一张数据表,可以定义任意多个嵌套类型字段,但每个字段的嵌套层级只支持一级,
即嵌套表内不能继续使用嵌套类型。对于简单场景的层级关系或关联关系,使用嵌套类型也是一种不错的选择。
create table test_nested (
id Int8,
name String,
hobby Nested(
hid Int8,
h1 String,
h2 String
)
)engine=Memory;
查看表结构:
┌─name──────┬─type──────────┬─default_type─┬─default_expression─┐
│ id │ Int8 │ │ │
│ name │ String │ │ │
│ hobby.hid │ Array(Int8) │ │ │
│ hobby.h1 │ Array(String) │ │ │
│ hobby.h2 │ Array(String) │ │ │
└───────────┴───────────────┴──────────────┴────────────────────┘
嵌套类型本质是一种多维数组的结构。嵌套表中每个字段都是一个数组,并且行与行之间数组的长度无须对齐。
需要注意的是,在同一行数据内每个数组字段的长度必须相等。
-- 插入数据
insert into test_nested values (1, zss, [1,2,3],[吃,喝,睡],[eat,drink,sleep]);
insert into test_nested values (2, lss, [1,2,3],[吃,喝,睡],[eat,drink,sleep]);
-- 查询数据
select * from test_nested;
┌─id─┬─name─┬─hobby.hid─┬─hobby.h1──────┬─hobby.h2────────────────┐
│ 1 │ zss │ [1,2,3] │ [吃,喝,睡] │ [eat,drink,sleep] │
└────┴──────┴───────────┴───────────────┴─────────────────────────┘
┌─id─┬─name─┬─hobby.hid─┬─hobby.h1──────┬─hobby.h2────────────────┐
│ 2 │ lss │ [1,2,3] │ [吃,喝,睡] │ [eat,drink,sleep] │
└────┴──────┴───────────┴───────────────┴─────────────────────────┘
-- 复杂查询数据
select id, name, hobby.hid, hobby.h1, hobby.h1[1] from test_nested;
┌─id─┬─name─┬─hobby.hid─┬─hobby.h1──────┬─arrayElement(hobby.h1, 1)─┐
│ 1 │ zss │ [1,2,3] │ [吃,喝,睡] │ 吃 │
└────┴──────┴───────────┴───────────────┴───────────────────────────┘
┌─id─┬─name─┬─hobby.hid─┬─hobby.h1──────┬─arrayElement(hobby.h1, 1)─┐
│ 2 │ lss │ [1,2,3] │ [吃,喝,睡] │ 吃 │
└────┴──────┴───────────┴───────────────┴───────────────────────────┘
3.9 复杂数据类型
3.9.1 domain
-- Domain pojo beans:https://clickhouse.tech/docs/zh/sql-reference/data-types/domains/ipv4/域名类型分为IPv4和IPv6两类,本质上它们是对整型和字符串的进一步封装。IPv4类型是基于UInt32封装的
(1)出于便携性的考量,例如IPv4类型支持格式检查,格式错误的IP数据是无法被写入的,例如:INSERT INTO IPv4_test values(www.nauu.com, 192.0.0.1)
(2)出于性能的考量,同样以IPv4为例,IPv4使用UInt32存储,相比String 更加紧凑,占用的空间更小,查询性能更快。IPv6类型是基于FixedString(16) 封装的,它的使用方法与IPv4别无二致,在使用Domain类型的时候还有一点需要注意,虽然它从表象上看起来与String 一样,但Domain 类型并不是字符串,所以它不支持隐式的自动类型转换。如果需要返回IP的字符串形式,则需要显式调用IPv4NumToString 或 IPv6NumToString 函数进行转换。
create table test_domain1(
id Int8,
ip IPv4
)engine=Memory;
insert into test_domain1 values(1, 192.168.133.1);
insert into test_domain1 values(1, 192.168.133);
create table test_domain2(
id Int8,
ip String
)engine=Memory;
insert into test_domain2 values(1, 192.168.133.1);
insert into test_domain2 values(1, 192.168.133);
四、数据库
数据库起到了命名空间的作用,可以有效规避命名冲突的问题,也为后续的数据隔离提供了支撑。任何一张数据表,都必须归属在某个数据库之下。
在CK中数据库也有自己的引擎,数据库目前支持的数据库引擎有5种:
Ordinary:默认引擎,在绝大多数情况下我们都会使用默认引擎,使用时无须刻意声明。在此数据库下可以使用任意类型的表引擎Dictionary:字典引擎,此类数据库会自动为所有数据字典创建它们的数据表Memory:内存引擎,用于存放临时数据。此类数据库下的数据表只会停留在内存中,不会涉及任何磁盘操作,当服务重启后数据会被清除Lazy:日志引擎,此类数据库下只能使用Log系列的表引擎MySQL:MySQL引擎,此类数据库下会自动拉取远端MySQL中的数据,并为它们创建MySQL表引擎的数据表MaterializeMySQL:MySQL数据同步;将MySQL数据全量或增量方式同步到clickhouse中,解决mysql服务并发访问压力过大的问题
4.1 MySQL 数据库引擎
MySQL数据库引擎
先在mysql中先创建好数据库db_mysql_test
确认远程访问权限确认域名是否配置
create database school_score engine=MySQL(node-01:3306,school_score,root,aa_bb_CC1234);
这样就可以在clickhouse下查询mysql中的数据了
use school_score;
show tables;
在clickhouse 不能创建mysql引擎表。但可以在clickhouse插入数据到mysql;因为 clickhouse不支持delete删除数据、update更新操作;原因是clickhouse定位于olap分析,不支持事务
4.2 Log 家族【Log系列引擎】
Log家族,比较简单,数据少,测试使用,本地表,本地存储表数据。默认数据存储在:/var/lib/clickhouse/data/...
Log家族具有最小功能的轻量级引擎。当您需要快速写入许多小表(最多约100万行)并在以后整体读取它们时,该类型的引擎是最有效的
4.2.1 TinyLog引擎(数据不分块)
TinyLog引擎数据不分块,而Log会进行分块。所以效率上来说:TinyLog不如Log快,因为它是单线程的。
最简单的表引擎,用于将数据存储在磁盘上。每列都存储在单独的压缩文件中,写入时,数据将附加到文件末尾。
该引擎没有并发控制。
只支持并发读如果同时从表中读取和写入数据,则读取操作将抛出异常;如果同时写入多个查询中的表,则数据将被破坏。
-- 建表
create table tb_tinylog(id Int8, name String, age Int8, birthDay Date) engine=TinyLog;
-- 插入数据
insert into tb_tinylog values(1,马云,56, 1990-01-01),(2,马化腾,55, 1990-01-01),(3,马克思,123, 1990-01-01);
-- 查询数据
select * from tb_tinylog;
ll /var/lib/clickhouse/data/default/test_db
-rw-r--r-- 1 root root 29 Jan 10 15:17 age.bin
-rw-r--r-- 1 root root 32 Jan 10 15:17 birthDay.bin
-rw-r--r-- 1 root root 29 Jan 10 15:17 id.bin
-rw-r--r-- 1 root root 54 Jan 10 15:17 name.bin
-rw-r--r-- 1 root root 121 Jan 10 15:17 sizes.json
数据存储在机器的磁盘上,每列一个文件,插入数据向列文件的后面追加
再插入一条数据后,存储列数据的文件的大小增加了
age.bin 和 id.bin, name.bin 是压缩过的对应的列的数据,sizes.json 中记录了每个 *.bin 文件的大小:
cat sizes.json
[root@node-01 tb_tinylog]# cat sizes.json
{"yandex":{"age%2Ebin":{"size":"29"},"birthDay%2Ebin":{"size":"32"},"id%2Ebin":{"size":"29"},"name%2Ebin":{"size":"54"}}}[root@node-01 tb_tinylog]#
补充说明,如果表损坏,需要将表删除、表对应的元数据删除,然后重启clickhouse-server服务
[root@node-01 test_db]# rm -rf /var/lib/clickhouse/data/test_db/tb_tinylog
[root@node-01 test_db]# rm -rf /var/lib/clickhouse/metadata/test_db/tb_tinylog.sql
这种表引擎的典型用法是 write-once:
首先只写入一次数据,然后根据需要多次读取。此引擎适用于相对较小的表(建议最多1,000,000行)。如果有许多小表,则使用此表引擎是适合的,因为它比需要打开的文件更少。当拥有大量小表时,可能会导致性能低下。不支持索引。
4.2.2 StripLog (数据分块列在一起)
在你需要写入许多小数据量(小于一百万行)的表的场景下使用这个引擎。
-- 建表
create table stripe_log_table (
timestamp DateTime,
message_type String,
message String
) engine=StripeLog;
五、数据表
ClickHouse 数据表的定义语法,是在标准SQL的基础上建立的,所以熟悉数据库的读者们在看到接下来的语法时,应该会感到熟悉。ClickHouse 目前提供了三种最基本的建表方法!但是注意的是在CK中建表一定要指定表的引擎,那么表的引擎的详细文档在后面。
5.1 创建表
create table [if not exists] [db_name.]table_name (
name1 [type][DEFAULT|MATERLIALIZED|ALIAS expr],
name2 [type][DEFAULT|MATERLIALIZED|ALIAS expr],
省略...
) ENGINE=engine
第一种建表方式:
create table test_db.test1(
id Int16 DEFAULT 0 comment 用户的标识,
name String comment 用户姓名,
age UInt8 comment 用户年龄
) ENGINE=Log;
上述语句将会在default 默认的数据库下创建一张内存表。注意末尾的ENGINE 参数,它被用于指定数据表的引擎。表引擎决定了数据表的特性,也决定了数据将会被如何存储及加载。例如示例中使用的Memory表引擎,是ClickHouse最简单的表引擎,数据只会被保存在内存中,在服务重启时数据会丢失。
第二种方式建表:
这种方式其实就是复制已经存在的一张的表结构,可用于数据的备份,可用于多个数据库之间复制表结构
CREATE TABLE [IF NOT EXISTS] [db_name.]table_name AS [db_name2.]table_name2[ENGINE=engine];
示例:
create database newdb;
use newdb;
-- 将复制test_db 数据库中的test1 表的表结构
create table tb_test1 as test_db.test1;
desc tb_test1;
第三种方式建表:
通过SELECT 查询的方式创建表,同时也会导入查询的结果数据
-- 语法
CREATE TABLE [IF NOT EXISTS] [db_name.]table_name ENGINE=engine AS SELECT ...
示例
create table tb_log engine=Memory as select * from test_db.tb_tinylog;
5.2 删除表
DROP TABLE [IF EXISTS] [db_name.]table_name;
5.3 临时表
ClickHouse 也有临时表的概念,创建临时表的方法是在普通表的基础之上添加TEMPORARY 关键字,相比普通表而言,临时表也如下两点特殊之处:
它的生命周期是会话绑定的,所以它只支持Memeory 表引擎,如果会话结束,数据表就会被销毁;临时表不属于任何数据库所以在它的建表语句中,既没有数据库参数也没有表引擎参数
临时表的有优先级大于系统中的表,一般用于集群之间的数据传播的载体
临时表的创建语法如下:
CREATE TEMPORARY TABLE [IF NOT EXISTS] table_name (
name1 [type] [DEFAULT|MATERIALIZED|ALIAS expr],
name2 [type] [DEFAULT|MATERIALIZED|ALIAS expr],
...
)
-- 直接创建临时表,临时表不属于任何一个数据库,也不会持久保存,不用指定引擎
create temporary table tb_tmp(id Int8, name String); -- 临时表不需要指定表的引擎
-- 创建临时表并加载数据
create temporary table log as select * from test_log;
-- 将临时表的数据导入到当前数据库表中,实现不同数据库的数据迁移
create table tb_log engine=Log as select * from log;
我们可以理解成临时表会将当前数据库中已经存在的同名表覆盖隐藏,当出现操作的时候,如果有临时表,那么会操作临时表!
临时表:
不属于任何数据库会话断开以后表删除,不会持久化如果本地表和临时表冲突,临时表优先数据库之间的数据迁移
create table tb_name engine=Log as select * from db.tb;
create temporary table log as select * from test_db.test_log;
create table tb_log engine=Log as select * from log;
5.4 视图
5.4.1 普通视图
ClickHouse拥有普通和物化两种视图,其中物化视图拥有独立的存储,而普通视图只是一层简单的查询代理
CREATE VIEW [IF NOT EXISTS] [db_name.]view_name AS SELECT ...
普通视图不会存储任何数据,它只是一层单酏的SELECT 查询映射,起着简化查询、明晰语义的作用,对查询性能不会有任何增强。假设有一张普通视图 view_tb_v1,它是基于数据表 tb_v1创建的,那么下面的两格SELECT 查询是完全等价的。
SELECT * FROM tb_v1
-- tb_v1 的视图
SELECT * FROM view_tb_v1;
示例:
create table tb_teacher (
tid Int8,
name String,
age UInt8,
gender String,
sal Float64
)engine=Log;
insert into tb_teacher values
(1, wbb, 27, m, 20000),
(2, lbb, 25, f, 30000),
(3, mbb, 26, m, 40000),
(4, sbb, 37, f, 50000),
(5, lbb, 17, m, 60000);
create view teacher_view as select name, sal from tb_teacher;
-- 普通视图不存储数据,只是一个指向
select * from teacher_view;
5.4.2 物化视图
物化视图支持表引擎,相当于特殊的表,数据保存形式由它的表引擎决定,创建物化视图的完整语法如下所示:
create materialized view mv_log engine=Log populate as select * from log;
在磁盘上有表目录,目录中的文件和映射表的结构一样
-- 物化视图 特殊的表 结构 引擎 持久化
create materialized view m_teacher_view as engine=Log populate as select * from tb_teacher;
select * from m_teacher_view;
-- 向普通表tb_teacher插入数据
insert into tb_teacher values(6, xingge, 48, m, 1500);
-- 查询物化视图中的数据,同步数据
select * from m_teacher_view;
5.5 分区表
数据分区(partition)和数据分片(shard)是完全不同的两个概念。数据分区是针对本地数据而言的,是数据的一种纵向切分。而数据分片是数据的一种横向切分。数据分区对于一款OLAP数据库而言意义非凡:借助数据分共,在后续的查询过程中能够跳过不必要的数据目录,从而提升查询的性能。合理地利用分区特性,还可以变相实现数据的更新操作,因为数据分区支持删除、替换和重置操作。假设数据表按照月份分区,那么数据就可以按月份的粒度被替换更新。分区虽好,但不是所有的表引擎都可以使用这项特性,目前只有合并树(MergeTree)家族系列的表引擎才支持数据分区。接下来通过一个简单的例子演示分区表的使用方法。首先由PARTITION BY 指定分区键,并将其格式化为年月的形式:
create table tb_partition(
cid String,
ctime DateTime,
money Float64
)engine=MergeTree() -- MergeTree家族最简单的引擎
partition by toYYYYMM(ctime) -- 按照年月来分区
order by cid -- 指定排序字段; 当排序字段与主键字段是同一个字段时,就会将主键字段不指定
primary key cid;
-- 插入数据
insert into tb_partition values
(1, 2020-12-01 11:00:21, 100),
(2, 2020-12-02 11:12:21, 300),
(3, 2020-11-01 11:00:21, 200);
-- 查询tb_partition表的分区信息
select table, partition, engine, path from system .parts where table=tb_partition;
-- 再次插入数据,进入到/var/lib/clickhouse/data/test_db/tb_partition/ 会发现11、12月份的数据都各有2个块
insert into tb_partition values
(4, 2020-12-01 11:00:21, 100),
(5, 2020-12-02 11:12:21, 300),
(6, 2020-11-01 11:00:21, 200);
-- 合并数据
optimize table tb_partition; -- 一次合并两个分区
-- 进入到/var/lib/clickhouse/data/test_db/tb_partition/ 会发现11、12月份的数据都各有1个块
-- 之前老的分区,默认8分钟会被删除
链接:
https://blog.csdn.net/weixin_32265569/article/details/111822811作者:不埋雷的探长
PS:如果想学习技术,或者在学习技术的过程中有疑问,对编程方向的选择,可以来这里找小于哥,一个有思想有规划,被代码延误的心灵导师,可咨询offer的选择,职业规划,学习路线,技术开发中的问题
去咨询
不会有人刷到这还想白嫖吧?不会吧?点赞对真的我很重要!要不加个关注?
我是小于哥呀
@终端研发部
专注于职场经验和技术开发的分享,希望我的回答能对同学有一些帮助,笔芯~
》的全部内容,本文网址:https://www.7ca.cn/baike/59613.shtml,如对您有帮助可以分享给好友,谢谢。