2022/03 作者:ihunter 0 次 0
一、SQL基本语法
1.1、CREATE
1.1.1、CREATE DATABASE
数据库起到了命名空间的作用,可以有效规避命名冲突的问题,也为后续的数据隔离提供了支撑。任何一张数据表,都必须归属在某个数据库之下。
创建数据库的完整语法如下所示:
CREATE DATABASE [IF NOT EXISTS] db_name [ENGINE = engine];
如果查询中存在 IF NOT EXISTS,则当数据库已经存在时,该查询不会返回任何错误。
create database if not exists db_name;
切换数据库使用:
use db_name;
[ENGINE = engine] 表示数据库所使用的引擎类型(是的,你没看错,数据库也支持设置引擎)
数据库目前一共支持 5 种引擎,如下所示:
Ordinary: 默认引擎,在绝大多数情况下我们都会使用默认引擎,使用时无须刻意声明。在此数据库下可以 使用任意类型的表引擎。
Dictionary: 字典引擎,此类数据库会自动为所有数据字典创建它们的数据表。
Memory: 内存引擎,用于存放临时数据。此类数据库下的数据表只会停留在内存中,不会涉及任何磁盘操 作,当服务重启后数据会被清除。
Lazy: 日志引擎,此类数据库下只能使用Log系列的表引擎。
MySQL: MySQL引擎,此类数据库下会自动拉取远端MySQL中的数据,并为它们创建MySQL表引擎的数据表
在绝大多数情况下都只需使用默认的数据库引擎,默认数据库的实质是物理磁盘上的一个文件目录,所以在语句执行之后,ClickHouse便会在安装路径下创建DB_TEST 数据库的文件目录
cd /var/lib/clickhouse/data ll
库名:db_name, 这个库的数据存储目录:/var/lib/clickhouse/data/db_name 表名:test, 这个表的数据存储目录:/var/lib/clickhouse/data/db_name/test/ 分区:20210425,这个分区的存储目录:/var/lib/clickhouse/data/db_name/test/20210425
与此同时,在metadata路径下也会一同创建用于恢复数据库的 DB_TEST.sql 文件:
/var/lib/clickhouse/metadata ll
使用 SHOW DATABASES 查询,即能够返回 ClickHouse 当前的数据库列表,使用 USE 查询可以实现在多个数据库之间进行切换,而通过 SHOW TABLES 查询可以查看当前数据库的数据表列表。删除一个数据库,则需要用到DROP(别瞎搞~)。
drop database [if exists] db_name
1.1.2. CREATE TABLE
对于创建表,语法如下:
CREATE TABLE [IF NOT EXISTS] [db.]table_name [ON CLUSTER cluster] ( name1 [type1] [DEFAULT|MATERIALIZED|ALIAS expr1], name2 [type2] [DEFAULT|MATERIALIZED|ALIAS expr2], ... ) ENGINE = engine
有三种方式创建表:
1、直接创建
create table table_name( id UInt16, name String) engine=TinyLog;
2、创建一个与其他表有相同结构的表,属于表复制(复制表)
CREATE TABLE [IF NOT EXISTS] [db.]table_name AS [db2.]name2 [ENGINE = engine]
可以对其指定不同的表引擎声明。如果没有表引擎声明,则创建的表将与 db2.name2 使用相同的表引擎。
create table table_name1 as table_name2 engine=Memory; desc table_name2;
3、使用指定的引擎创建一个与 SELECT 子句的结果具有相同结构的表,并使用 SELECT 子句的结果填充它。
语法:在Hive中,也支持这种语法,简称为 CTAS: create table .... as select ...
CREATE TABLE [IF NOT EXISTS] [db.]table_name ENGINE = engine AS SELECT ...
1.1.3. 表字段
参数说明:表字段支持三种默认值表达式的定义方法,分别是DEFAULT、MATERIALIZED和ALIAS。
无论使用哪种形式,表字段一旦被定义了默认值,它便不再强制要求定义数据类型,因为 ClickHouse 会 根据默认值进行类型推断。如果同时对表字段定义了数据类型和默认值表达式,则以明确定义的数据类型为主。
默认值表达式的三种定义方法之间也存在着不同之处,可以从如下三个方面进行比较。
数据写入:在数据写入时,只有DEFAULT类型的字段可以出现在 INSERT 语句中。而 MATERIALIZED 和ALIAS 都不能被显式赋值,它们只能依靠计算取值。例如试图为 MATERIALIZED 类 型的字段写入数据,将会得到如下的错误
DB::Exception: Cannot insert column URL,because it is MATERIALIZED column..
数据查询:在数据查询时,只有 DEFAULT 类型的字段可以通过 SELECT * 返回。而 MATERIALIZED 和ALIAS 类型的字段不会出现在 SELECT * 查询的返回结果集中。
数据存储:在数据存储时,只有 DEFAULT 和 MATERIALIZED 类型的字段才支持持久化。如果使用的表引擎支持物理存储(例如 TinyLog 表引擎),那么这些列字段将会拥有物理存储。而 ALIAS 类型 的字段不支持久化,它的取值总是需要依靠计算产生,数据不会落到磁盘。
可以使用 ALTER 语句修改默认值,例如:
ALTER TABLE [db_name.]table MODIFY COLUMN col_name DEFAULT value;
修改动作并不会影响数据表内先前已经存在的数据。但是默认值的修改有诸多限制,例如在合并树表引擎中,它的主键字段是无法被修改的;而某些表引擎则完全不支持修改(例如 TinyLog)。
1.1.4. 临时表
ClickHouse 也有临时表的概念,创建临时表的方法是在普通表的基础之上添加 TEMPORARY 关键字, 它的完整语法如下所示:
CREATE TEMPORARY TABLE [IF NOT EXISTS] table_name ( name1 [type][DEFAULT|MATERIALIZED|ALIAS expr], name2 [type][DEFAULT|MATERIALIZED|ALIAS expr] )
相比普通表而言,临时表有如下几点特殊之处:
它的生命周期是会话绑定的,所以它只支持Memory表引擎,如果会话结束,数据表就会被销毁;
临时表不属于任何数据库,所以在它的建表语句中,既没有数据库参数也没有表引擎参数。
临时表的优先级是大于普通表的。当两张数据表名称相同的时候,会优先读取临时表的数据。
在 ClickHouse 的日常使用中,通常不会刻意使用临时表。它更多被运用在 ClickHouse 的内部,是数据在集群间传播的载体。
1.1.5. 分区表
Hive可以创建分区表,分桶表。
分区表:把数据按照人为的方式进行分类管理,每个类别就是一个文件夹
分桶表:按照指定的程序逻辑来决定哪些数据被分到哪些桶。每个桶是一个文件。
数据分区(partition)和数据分片(shard)是完全不同的两个概念。数据分区是针对本地数据而言 的,是数据的一种纵向切分。而数据分片是数据的一种横向切分。
数据分区对于一款 OLAP 数据库而言意义非凡:借助数据分区,在后续的查询过程中能够跳过不必要的数据目录,从而提升查询的性能。合理地利用分区特性,还可以变相实现数据的更新操作,因为数据分区支持删除、替换和重置操作。假设数据表按照月份分区,那么数据就可以按月份的粒度被替换更新。分区虽好,但不是所有的表引擎都可以使用这项特性,目前只有合并树(MergeTree)家族系列的表引擎才支持数据分区。
接下来通过一个简单的例子演示分区表的使用方法。
首先由 PARTITION BY 指定分区键,例如下面的数据表 nx_table_partition1 使用了日期字段作为分区键,并将其格式化为年月的形式:
create table table_test_partition( id String, url String, eventTime DateTime ) engine = MergeTree()partition by toYYYYMM(eventTime)order by id;
通过 system.parts 系统表,查询数据表的分区状态:
select table, partition, path from system.parts where table ='table_test_partition';
可以看到,table_test_partition 按年月划分后,目前拥有的数据分区,且每个分区都对应一个独立的文件目录,用于保存各自部分的数据。
合理设计分区键非常重要,通常会按照数据表的查询场景进行针对性设计。例如在刚才的示例中数据表 按年月分区,如果后续的查询按照分区键过滤,例如:
select * from table_test_partition where eventTime = '2021-04-25';
那么在后续的查询过程中,可以利用分区索引跳过5月份和6月份的分区目录,只加载5月份的数据,从 而带来查询的性能提升。当然,使用不合理的分区键也会适得其反,分区键不应该使用粒度过细的数据 字段。例如,按照小时分区,将会带来分区数量的急剧增长,从而导致性能下降。
1.2. ClickHouse视图
Hive中的视图: 只有逻辑视图
ClickHouse的视图: 物化视图的概念 ClickHouse的物化视图,就类似于触发器
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 [MATERIALIZED] VIEW [IF NOT EXISTS] [db.]table_name [To [db.]name][ENGINE=engine] [POPULATE] AS SELECT ...
物化视图创建好之后,如果源表被写入新数据,那么物化视图也会同步更新。
POPULATE 修饰符决定了物化视图的初始化策略:如果使用了 POPULATE 修饰符,那么在创建视图的过程中,会连带将源表中已存在的数据一并导入,如同执行了 SELECT INTO 一般;反之,如果不使用 POPULATE 修饰符,那么 物化视图在创建之后是没有数据的,它只会同步在此之后被写入源表的数据。物化视图目前并不支持同步删除,如果在源表中删除了数据,物化视图的数据仍会保留。
物化视图本质是一张特殊的数据表,例如使用 SHOW TABLE 查看数据表的列表:
show tables;
由上可以发现,物化视图也在其中,它们是使用了 .inner 特殊前缀的数据表,所以删除视图的方法是直接使用
DROP TABLE
1.3. ALTER
目前只有 MergeTree、Merge 和 Distributed 这三类表引擎支持 ALTER 查询,基本语法:
ALTER TABLE [db].name [ON CLUSTER cluster] ADD|DROP|MODIFY COLUMN ...
参数解析:
ADD COLUMN – 向表中添加新列 DROP COLUMN – 在表中删除列 MODIFY COLUMN – 更改列的类型
查询列的信息:
desc table_name;
用法如下:
添加字段:
对于数据表中已经存在的旧数据而言,新追加的字段会使用默认值补全。
alter table [table_name] [add] [column] age UInt8; 或者alter table [table_name] [add] [column] age default 18; 或者alter table [table_name] [add] [column] age UInt8 after gender;
更改 age 列的类型:
修改某个字段的数据类型,实质上会调用相应的toType转型方法。如果当前的类型与期望的类型不能兼容,则修改操作将会失败
alter table [table_name] [modify] [column] age UInt16;
删除age列:
alter table [table_name] [drop] [column] age;
修改备注:
alter table [table_name] [comment] column age '年龄';
移动数据表:
需要注意的是,数据表的移动只能在单个节点的范围内。换言之,数据表移动的目标数据库和原始数据库必须处在同一个服务节点内,而不能是集群中的远程节点。
rename table dylan.test_table1 to dylan.test_table2;
1.4. DESCRIBE TABLE
desc [table_name];
1.5. Partition
创建一个分区表:
--创建新分区表 create table dylan_table_partition( id String, uname String, create_time Date ) engine = MergeTree() partition by toYYYYMM(create_time) order by id;
查询分区信息
ClickHouse 内置了许多 system 系统表,用于查询自身的状态信息。其中 parts 系统表专门用于查询数据表的分区信息
select partition_id, name, table, database, path from system.parts where table ='dylan_table_partition';
删除分区
alter table dylan_table_partition drop partition '20210425';
复制分区
alter table dylan_table_partition1 replace partition '20210425' from dylan_table_partition;
重置分区
如果数据表某一列的数据有误,需要将其重置为初始值,此时可以使用下面的语句实现:
ALTER TABLE dylan_table_partition1 CLEAR COLUMN column_name IN PARTITION partition_expr;
卸载和装载分区
表分区可以通过 DETACH 语句卸载,分区被卸载后,它的物理数据并没有删除,而是被转移到了当前 数据表目录的 detached 子目录下。而装载分区则是反向操作,它能够将 detached 子目录下的某个分 区重新装载回去。卸载与装载这一对伴生的操作,常用于分区数据的迁移和备份场景。
一旦分区被移动到了 detached 子目录,就代表它已经脱离了 ClickHouse 的管理,ClickHouse 并不会主动清理这些文件。这些分区文件会一直存在,除非我们主动删除或者使用 ATTACH 语句重新装载它们。
Hive中有类似的概念: offline
--卸载 ALTER TABLE dylan_test_partition DETACH PARTITION partition_expr --装载 ALTER TABLE dylan_test_partition ATTACH PARTITION partition_expr
1.6. CHECK TABLE
检查表中的数据是否损坏,他会返回两种结果:
该命令只支持 Log,TinyLog 和 StripeLog 引擎。 check table dylan_test_partition; 0 – 数据已损坏 1 – 数据完整
1.7. 分布式DDL执行
HDFS:datanode单独启动堆外提供服务么? 不可以
ClickHouse: 每个节点事实上,本质上,依然是独立的。如果你真的需要在所有的clickhouse节点上,去创建一张表:你需要把创建表的命令,在每个clickhouse server中执行一次。
ClickHouse 支持集群模式,一个集群拥有 1 到多个节点。CREATE、ALTER、DROP、RENMAE 及 TRUNCATE 这些 DDL 语句,都支持分布式执行。这意味着,如果在集群中任意一个节点上执行 DDL 语 句,那么集群中的每个节点都会以相同的顺序执行相同的语句。这项特性意义非凡,它就如同批处理命 令一样,省去了需要依次去单个节点执行 DDL 的烦恼。
将一条普通的 DDL 语句转换成分布式执行十分简单,只需加上 ON CLUSTERcluster_name 声明即可。例如,执行下面的语句后将会对 dp_cluster 集群内的所有节点广播这条 DDL 语句:
-- 分布式建库 create database if not exists dylan on CLUSTER dp_cluster; -- 分布式建表CREATE TABLE dylan_test_partition ON CLUSTER dp_cluster( id String, uname String, create_time Date )ENGINE=MergeTree() PARTITION BY toYYYYMM(create_time) ORDER BY id;
2. ClickHouse 导入导出
2.1. INSERT INTO
创建一张表:
drop table if exists dylan_test; create table dylan_test( id UInt16, name String ) engine=TinyLog; drop table if exists dylan_test1; create table dylan_test1( id UInt16, name String ) engine = TinyLog;
向表中添加数据,基本格式如下:
INSERT INTO [db.]table [(c1, c2, c3)] VALUES (v11, v12, v13), (v21, v22, v23),...
在使用 VALUES 格式的语法写入数据时,支持加入表达式或函数,例如:
insert into dylan_test values(1, toString(1+1));
还可以使用 select 来写入数据:
INSERT INTO [db.]table [(c1, c2, c3)] SELECT ...
ClickHouse 不支持的修改数据的查询:UPDATE, DELETE, REPLACE, MERGE, UPSERT, INSERT UPDATE.
插入 JSON 格式的数据:
INSERT INTO dylan_test1 FORMAT JSONEachRow{"id":"1", "name":"java"};
使用指定格式导出:
clickhouse-client -h bigdata04 --port 9977 --database="dylan" --query="select * from dylan.dylan_test1 FORMAT CSV" > dylan_test1.csv;
清空 dylan_test1 表:
truncate dylan_test1;
使用指定格式导入:
clickhouse-client -h bigdata04 --port 9977 --database="dylan" --query="insert into dylan.dylan_test1 FORMAT CSV" < ./dylan_test1.csv
2.2. update 和 delete
ClickHouse 提供了 DELETE 和 UPDATE 的能力,这类操作被称为 Mutation 查询,它可以看作ALTER 语句的变种。虽然Mutation能最终实现修改和删除,但不能完全以通常意义上的 UPDATE 和 DELETE 来理解,我们必须清醒地认识到它的不同:
总结:
1、Mutation操作适用于批量数据的修改和删除
2、不支持事务:一旦语句被提交执行就会立刻对现有的数据产生影响,无法回滚。
3、Mutation 操作执行是一个异步的过程,语句提交会立即返回,但是不代表具体逻辑已经执行完毕,具体的执行记录需要在system.mutations 系统表查询。
delete 的完整语法:
ALTER TABLE [db_name.]table_name DELETE WHERE filter_expr
查看数据目录:
[root@bigdata04 data]# pwd /var/lib/clickhouse/data/dylan/data [root@bigdata04 data]# ll total 24 drwxr-x--- 2 root root 4096 Aug 8 15:34 202104_1_1_0 drwxr-x--- 2 root root 4096 Aug 8 15:35 202104_1_1_0_2 drwxr-x--- 2 root root 4096 Aug 8 15:35 202104_1_1_0_3 drwxr-x--- 2 root root 6 Aug 8 15:34 detached -rw-r----- 1 root root 1 Aug 8 15:34 format_version.txt -rw-r----- 1 root root 109 Aug 8 15:35 mutation_2.txt -rw-r----- 1 root root 96 Aug 8 15:35 mutation_3.txt
可以发现在执行了update,delete操作之后数据目录会生成文件mutation_2.txt,mutation_3.txt。此 外还有在同名的目录下在末尾增加了_2 ,_3的后缀。可以看到 mutation_2.txt 和 mutation_3.txt 是日 志文件,完整的记录了update 和 delete 操作语句和时间。
mutation_id:生成对应的日志文件用于记录相关的信息。数据删除的过程是以数据表的每个分区目录为单位,将所有目录重写为新的目录,在目录的命名规则是在原有的名称上加上 block_numbers.number。数据的在重写的过程中会将所需要删除的数据去掉。旧的数据并不会立即删除,而是被标记为非激活状态(active = 0),等到MergeTree 引擎的下一次合并动作触发的时候,这些非活动目录才会被真正的从物理上删除。
UPDATE 语句不能修改分区键和主键。
上篇:
使用Canal实现ClickHouse实时同步MySQL数据
下篇:
MySQL按月自动创建分区表(千万级大表优化)