ClickHouse DDL

1. 數(shù)據(jù)庫

數(shù)據(jù)庫起到了命名空間的作用,可以有效規(guī)避命名沖突的問題,也為后續(xù)的數(shù)據(jù)隔離提供了支撐。任何一張數(shù)據(jù)表,都必須歸屬在某個(gè)數(shù)據(jù)庫之下。創(chuàng)建數(shù)據(jù)庫的完整語法如下所示:

CREATE DATABASE IF NOT EXISTS db_name [ENGINE = engine];

數(shù)據(jù)庫目前一共支持5種引擎,如下所示。

  • Ordinary:默認(rèn)引擎,在絕大多數(shù)情況下我們都會(huì)使用默認(rèn)引擎,使用時(shí)無須刻意聲明。在此數(shù)據(jù)庫下可以使用任意類型的表引擎。
  • Dictionary:字典引擎,此類數(shù)據(jù)庫會(huì)自動(dòng)為所有數(shù)據(jù)字典創(chuàng)建它們的數(shù)據(jù)表。
  • Memory:內(nèi)存引擎,用于存放臨時(shí)數(shù)據(jù)。此類數(shù)據(jù)庫下的數(shù)據(jù)表只會(huì)停留在內(nèi)存中,不會(huì)涉及任何磁盤操作,當(dāng)服務(wù)重啟后數(shù)據(jù)會(huì)被清除。
  • Lazy:日志引擎,此類數(shù)據(jù)庫下只能使用Log系列的表引擎。
  • MySQL:MySQL引擎,此類數(shù)據(jù)庫下會(huì)自動(dòng)拉取遠(yuǎn)端MySQL中的數(shù)據(jù),并為它們創(chuàng)建MySQL表引擎的數(shù)據(jù)表。

在絕大多數(shù)情況下都只需使用默認(rèn)的數(shù)據(jù)庫引擎。例如執(zhí)行下面的語句,即能夠創(chuàng)建屬于我們的第一個(gè)數(shù)據(jù)庫:

CREATE DATABASE DB_TEST;

默認(rèn)數(shù)據(jù)庫的實(shí)質(zhì)是物理磁盤上的一個(gè)文件目錄,所以在語句執(zhí)行之后,ClickHouse便會(huì)在安裝路徑下創(chuàng)建DB_TEST數(shù)據(jù)庫的文件目錄:

# ls -l /var/lib/clickhouse/data/
total 12
drwxr-x--- 2 clickhouse clickhouse 4096 Aug 16 16:44 DB_TEST
drwxr-x--- 2 clickhouse clickhouse 4096 Aug 16 16:45 default
drwxr-x--- 2 clickhouse clickhouse 4096 Aug 16 13:38 system

查看數(shù)據(jù)庫:

show databases;
┌─name────┐
│ DB_TEST │
│ default │
│ system  │
└─────────┘

切換數(shù)據(jù)庫并查看該數(shù)據(jù)庫下的表:

use DB_TEST;
show tables;

刪除數(shù)據(jù)庫:

DROP DATABASE [IF EXISTS] db_name;

2. 數(shù)據(jù)表

常規(guī)方法創(chuàng)建表:

CREATE TABLE [IF NOT EXISTS] [db_name.]table_name (
    name1 [type] [DEFAULT|MATERIALIZED|ALIAS expr],
    name2 [type] [DEFAULT|MATERIALIZED|ALIAS expr],
    ...
) ENGINE = engine;
  • 如果不指定數(shù)據(jù)庫名,則默認(rèn)把表創(chuàng)建在default數(shù)據(jù)庫下。
  • ENGINE參數(shù),它被用于指定數(shù)據(jù)表的引擎。表引擎決定了數(shù)據(jù)表的特性,也決定了數(shù)據(jù)將會(huì)被如何存儲(chǔ)及加載。例如示例中使用的Memory表引擎,是ClickHouse最簡單的表引擎,數(shù)據(jù)只會(huì)被保存在內(nèi)存中,在服務(wù)重啟時(shí)數(shù)據(jù)會(huì)丟失。

復(fù)制其他表的表結(jié)構(gòu):

CREATE TABLE [IF NOT EXISTS] [db_name1.]table_name AS [db_name2.]table_name2 [ENGINE = engine];
  • 新創(chuàng)建表的引擎可以與被復(fù)制表的引擎不一樣

通過SELECT子句的形式創(chuàng)建表:不僅會(huì)根據(jù)SELECT子句建立相應(yīng)的表結(jié)構(gòu),同時(shí)還會(huì)將SELECT子句查詢的數(shù)據(jù)順帶寫入

CREATE TABLE [IF NOT EXISTS] [db_name.]table_name ENGINE = engine AS SELECT ...

查看表結(jié)構(gòu):desc table;

刪除表:DROP TABLE [IF EXISTS] [db_name.]table_name;

3. 默認(rèn)值表達(dá)式

表字段支持三種默認(rèn)值表達(dá)式的定義方法,分別是DEFAULT、MATERIALIZED和ALIAS。無論使用哪種形式,表字段一旦被定義了默認(rèn)值,它便不再強(qiáng)制要求定義數(shù)據(jù)類型,因?yàn)镃lickHouse會(huì)根據(jù)默認(rèn)值進(jìn)行類型推斷。如果同時(shí)對(duì)表字段定義了數(shù)據(jù)類型和默認(rèn)值表達(dá)式,則以明確定義的數(shù)據(jù)類型為主,例如下面的例子:

CREATE TABLE dfv_v1 ( 
    id String,
    c1 DEFAULT 1000,
    c2 String DEFAULT c1
) ENGINE = TinyLog;
INSERT INTO dfv_v1(id) VALUES ('A000');
SELECT c1,c2,toTypeName(c1),toTypeName(c2) from dfv_v1;
┌───c1─┬─c2───┬─toTypeName(c1)─┬─toTypeName(c2)─┐
│ 1000 │ 1000 │ UInt16         │ String         │
└──────┴──────┴────────────────┴────────────────┘

由查詢結(jié)果可以驗(yàn)證,默認(rèn)值的優(yōu)先級(jí)符合我們的預(yù)期,其中c1字段根據(jù)默認(rèn)值被推斷為UInt16;而c2字段由于同時(shí)定義了數(shù)據(jù)類型和默認(rèn)值,所以它最終的數(shù)據(jù)類型來自明確定義的String。

默認(rèn)值表達(dá)式的三種定義方法之間也存在著不同之處,可以從如下三個(gè)方面進(jìn)行比較。
(1)數(shù)據(jù)寫入:在數(shù)據(jù)寫入時(shí),只有DEFAULT類型的字段可以出現(xiàn)在INSERT語句中。而MATERIALIZED和ALIAS都不能被顯式賦值,它們只能依靠計(jì)算取值。例如試圖為MATERIALIZED類型的字段寫入數(shù)據(jù),將會(huì)得到如下的錯(cuò)誤。

DB::Exception: Cannot insert column URL, because it is MATERIALIZED column..

(2)數(shù)據(jù)查詢:在數(shù)據(jù)查詢時(shí),只有DEFAULT類型的字段可以通過SELECT *返回。而MATERIALIZED和ALIAS類型的字段不會(huì)出現(xiàn)在SELECT *查詢的返回結(jié)果集中。
(3)數(shù)據(jù)存儲(chǔ):在數(shù)據(jù)存儲(chǔ)時(shí),只有DEFAULT和MATERIALIZED類型的字段才支持持久化。如果使用的表引擎支持物理存儲(chǔ)(例如TinyLog表引擎),那么這些列字段將會(huì)擁有物理存儲(chǔ)。而ALIAS類型的字段不支持持久化,它的取值總是需要依靠計(jì)算產(chǎn)生,數(shù)據(jù)不會(huì)落到磁盤。

可以使用ALTER語句修改默認(rèn)值,例如:

ALTER TABLE [db_name.]table MODIFY COLUMN col_name DEFAULT value;

修改動(dòng)作并不會(huì)影響數(shù)據(jù)表內(nèi)先前已經(jīng)存在的數(shù)據(jù)。但是默認(rèn)值的修改有諸多限制,例如在合并樹表引擎中,它的主鍵字段是無法被修改的;而某些表引擎則完全不支持修改(例如TinyLog)。

4. 臨時(shí)表

ClickHouse也有臨時(shí)表的概念,創(chuàng)建臨時(shí)表的方法是在普通表的基礎(chǔ)之上添加TEMPORARY關(guān)鍵字,它的完整語法如下所示:

CREATE TEMPORARY TABLE [IF NOT EXISTS] table_name (
    name1 [type] [DEFAULT|MATERIALIZED|ALIAS expr],
    name2 [type] [DEFAULT|MATERIALIZED|ALIAS expr],
    ...
);

相比普通表而言,臨時(shí)表有如下兩點(diǎn)特殊之處:

  • 它的生命周期是會(huì)話綁定的,所以它只支持Memory表引擎,如果會(huì)話結(jié)束,數(shù)據(jù)表就會(huì)被銷毀;
  • 臨時(shí)表不屬于任何數(shù)據(jù)庫,所以在它的建表語句中,既沒有數(shù)據(jù)庫參數(shù)也沒有表引擎參數(shù)。

臨時(shí)表的優(yōu)先級(jí)是大于普通表的。當(dāng)兩張數(shù)據(jù)表名稱相同的時(shí)候,會(huì)優(yōu)先讀取臨時(shí)表的數(shù)據(jù)。
在ClickHouse的日常使用中,通常不會(huì)刻意使用臨時(shí)表。它更多被運(yùn)用在ClickHouse的內(nèi)部,是數(shù)據(jù)在集群間傳播的載體。

5. 分區(qū)表

數(shù)據(jù)分區(qū)(partition)和數(shù)據(jù)分片(shard)是完全不同的兩個(gè)概念。數(shù)據(jù)分區(qū)是針對(duì)本地?cái)?shù)據(jù)而言的,是數(shù)據(jù)的一種縱向切分。而數(shù)據(jù)分片是數(shù)據(jù)的一種橫向切分。數(shù)據(jù)分區(qū)對(duì)于一款OLAP數(shù)據(jù)庫而言意義非凡:借助數(shù)據(jù)分區(qū),在后續(xù)的查詢過程中能夠跳過不必要的數(shù)據(jù)目錄,從而提升查詢的性能。合理地利用分區(qū)特性,還可以變相實(shí)現(xiàn)數(shù)據(jù)的更新操作,因?yàn)閿?shù)據(jù)分區(qū)支持刪除、替換和重置操作。假設(shè)數(shù)據(jù)表按照月份分區(qū),那么數(shù)據(jù)就可以按月份的粒度被替換更新。
分區(qū)雖好,但不是所有的表引擎都可以使用這項(xiàng)特性,目前只有合并樹(MergeTree)家族系列的表引擎才支持?jǐn)?shù)據(jù)分區(qū)。接下來通過一個(gè)簡單的例子演示分區(qū)表的使用方法。首先由PARTITION BY指定分區(qū)鍵,例如下面的數(shù)據(jù)表partition_v1使用了日期字段作為分區(qū)鍵,并將其格式化為年月的形式:

CREATE TABLE partition_v1 ( 
    ID String,
    URL String,
    EventTime Date
) ENGINE =  MergeTree()
PARTITION BY toYYYYMM(EventTime) 
ORDER BY ID;

INSERT INTO partition_v1 VALUES 
('A000','www.nauu.com','2019-05-01'),
('A001','www.brunce.com','2019-06-02');

SELECT table,partition,path from system.parts WHERE table = 'partition_v1';
┌─table────────┬─partition┬─path────────────────────────────────────────────────────────┐
│ partition_v1 │ 201905   │ /var/lib/clickhouse/data/DB_TEST/partition_v1/201905_1_1_0/ │
│ partition_v1 │ 201906   │ /var/lib/clickhouse/data/DB_TEST/partition_v1/201906_2_2_0/ │
└──────────────┴──────────┴─────────────────────────────────────────────────────────────┘

可以看到,partition_v1按年月劃分后,目前擁有兩個(gè)數(shù)據(jù)分區(qū),且每個(gè)分區(qū)都對(duì)應(yīng)一個(gè)獨(dú)立的文件目錄,用于保存各自部分的數(shù)據(jù)。合理設(shè)計(jì)分區(qū)鍵非常重要,通常會(huì)按照數(shù)據(jù)表的查詢場景進(jìn)行針對(duì)性設(shè)計(jì)。例如在剛才的示例中數(shù)據(jù)表按年月分區(qū),如果后續(xù)的查詢按照分區(qū)鍵過濾,例如:

SELECT * FROM  partition_v1 WHERE EventTime ='2019-05-01';

那么在后續(xù)的查詢過程中,可以利用分區(qū)索引跳過6月份的分區(qū)目錄,只加載5月份的數(shù)據(jù),從而帶來查詢的性能提升。

當(dāng)然,使用不合理的分區(qū)鍵也會(huì)適得其反,分區(qū)鍵不應(yīng)該使用粒度過細(xì)的數(shù)據(jù)字段。例如,按照小時(shí)分區(qū),將會(huì)帶來分區(qū)數(shù)量的急劇增長,從而導(dǎo)致性能下降。

6. 視圖

ClickHouse擁有普通和物化兩種視圖,其中物化視圖擁有獨(dú)立的存儲(chǔ),而普通視圖只是一層簡單的查詢代理。創(chuàng)建普通視圖的完整語法如下所示:

CREATE VIEW [IF NOT EXISTS] [db_name.]view_name AS SELECT ...

普通視圖不會(huì)存儲(chǔ)任何數(shù)據(jù),它只是一層單純的SELECT查詢映射,起著簡化查詢、明晰語義的作用,對(duì)查詢性能不會(huì)有任何增強(qiáng)。假設(shè)有一張普通視圖view_tb_v1,它是基于數(shù)據(jù)表tb_v1創(chuàng)建的,那么下面的兩條SELECT查詢是完全等價(jià)的:

-- 普通表
SELECT * FROM tb_v1;
-- tb_v1的視圖
SELECT * FROM view_tb_v1;

物化視圖支持表引擎,數(shù)據(jù)保存形式由它的表引擎決定,創(chuàng)建物化視圖的完整語法如下所示:

CREATE MATERIALIZED VIEW [IF NOT EXISTS] [db.]table_name [TO[db.]name] [ENGINE = engine] [POPULATE] AS SELECT ...

物化視圖創(chuàng)建好之后,如果源表被寫入新數(shù)據(jù),那么物化視圖也會(huì)同步更新。

POPULATE修飾符決定了物化視圖的初始化策略:

  • 如果使用了POPULATE修飾符,那么在創(chuàng)建視圖的過程中,會(huì)連帶將源表中已存在的數(shù)據(jù)一并導(dǎo)入,如同執(zhí)行了SELECT INTO一般;
  • 反之,如果不使用POPULATE修飾符,那么物化視圖在創(chuàng)建之后是沒有數(shù)據(jù)的,它只會(huì)同步在此之后被寫入源表的數(shù)據(jù)。
  • 當(dāng)使用了 TO [db].[table],會(huì)將數(shù)據(jù)保存到指定的表中,所以就不能使用POPULATE語法了,如果沒有使用TO [db].[table]語法,就必須設(shè)置存儲(chǔ)數(shù)據(jù)的表(這里指新創(chuàng)建的視圖)的ENGINE

物化視圖目前并不支持同步刪除,如果在源表中刪除了數(shù)據(jù),物化視圖的數(shù)據(jù)仍會(huì)保留。物化視圖本質(zhì)是一張?zhí)厥獾臄?shù)據(jù)表,使用SHOW TABLES可以看到物化視圖的表名,表名為.inner.[物化視圖的名稱],刪除視圖的語法是:DROP TABLE view_name;

7. 數(shù)據(jù)表的修改操作

目前只有MergeTree、Merge和Distributed這三類表引擎支持ALTER語法。

  • 追加字段
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;

對(duì)于數(shù)據(jù)表中已經(jīng)存在的舊數(shù)據(jù)而言,新追加的字段會(huì)使用默認(rèn)值補(bǔ)全。

  • 修改數(shù)據(jù)類型
ALTER TABLE tb_name MODIFY COLUMN [IF EXISTS] name [type] [default_expr];

修改某個(gè)字段的數(shù)據(jù)類型,實(shí)質(zhì)上會(huì)調(diào)用相應(yīng)的toType轉(zhuǎn)型方法。如果當(dāng)前的類型與期望的類型不能兼容,則修改操作將會(huì)失敗。例如,將String類型的IP字段修改為IPv4類型是可行的,而嘗試將String類型轉(zhuǎn)為UInt類型就會(huì)出現(xiàn)錯(cuò)誤。

  • 修改備注

追加備注的語法如下所示:

ALTER TABLE tb_name COMMENT COLUMN [IF EXISTS] name 'some comment';
  • 刪除字段
ALTER TABLE tb_name DROP COLUMN [IF EXISTS] name;

列字段在被刪除之后,它的數(shù)據(jù)也會(huì)被連帶刪除。

  • 移動(dòng)數(shù)據(jù)表
RENAME TABLE [db_name11.]tb_name11 TO [db_name12.]tb_name12,[db_name21.]tb_name21 TO [db_name22.]tb_name22, ...

RENAME可以修改數(shù)據(jù)表的名稱,如果將原始數(shù)據(jù)庫與目標(biāo)數(shù)據(jù)庫設(shè)為不同的名稱,那么就可以實(shí)現(xiàn)數(shù)據(jù)表在兩個(gè)數(shù)據(jù)庫之間移動(dòng)的效果。而原始數(shù)據(jù)庫與目標(biāo)數(shù)據(jù)庫的名稱相同的話,就是修改表名稱的操作。

需要注意的是,數(shù)據(jù)表的移動(dòng)只能在單個(gè)節(jié)點(diǎn)的范圍內(nèi)。換言之,數(shù)據(jù)表移動(dòng)的目標(biāo)數(shù)據(jù)庫和原始數(shù)據(jù)庫必須處在同一個(gè)服務(wù)節(jié)點(diǎn)內(nèi),而不能是集群中的遠(yuǎn)程節(jié)點(diǎn)。

  • 清空數(shù)據(jù)表
TRUNCATE TABLE [IF EXISTS] [db_name.]tb_name;

8. 分區(qū)基本操作

目前只有MergeTree系列的表引擎支持?jǐn)?shù)據(jù)分區(qū)。

  • 查詢分區(qū)信息

ClickHouse內(nèi)置了許多system系統(tǒng)表,用于查詢自身的狀態(tài)信息。其中parts系統(tǒng)表專門用于查詢數(shù)據(jù)表的分區(qū)信息。例如執(zhí)行下面的語句,就能夠得到數(shù)據(jù)表partition_v2的分區(qū)狀況:

SELECT partition_id,name,table,database FROM system.parts WHERE table = 'partition_v2';
┌─partition_id───┬─name───────────┬─table────────┬─database┐
│ 201905         │ 201905_1_1_0_6 │ partition_v2 │ default │
│ 201910         │ 201910_3_3_0_6 │ partition_v2 │ default │
│ 201911         │ 201911_4_4_0_6 │ partition_v2 │ default │
│ 201912         │ 201912_5_5_0_6 │ partition_v2 │ default │
└────────────────┴────────────────┴──────────────┴─────────┘
# 目前partition_v2表共擁有4個(gè)分區(qū),其中partition_id或者name等同于分區(qū)的主鍵,可以基于它們的取值確定一個(gè)具體的分區(qū)。
  • 刪除指定分區(qū)
ALTER TABLE tb_name DROP PARTITION partition_expr;

# 假如現(xiàn)在需要更新partition_v2數(shù)據(jù)表整個(gè)7月份的數(shù)據(jù),則可以先將7月份的分區(qū)刪除
ALTER TABLE partition_v2 DROP PARTITION 201907;
# 然后將整個(gè)7月份的新數(shù)據(jù)重新寫入,就可以達(dá)到更新的目的
INSERT INTO partition_v2 VALUES ('A004-update','www.bruce.com', '2019-07-02');...
  • 復(fù)制分區(qū)數(shù)據(jù)

ClickHouse支持將A表的分區(qū)數(shù)據(jù)復(fù)制到B表,這項(xiàng)特性可以用于快速數(shù)據(jù)寫入、多表間數(shù)據(jù)同步和備份等場景,它的完整語法如下:

ALTER TABLE B REPLACE PARTITION partition_expr FROM A;

并不是任意數(shù)據(jù)表之間都能夠相互復(fù)制,它們還需要滿足兩個(gè)前提條件:
(1)兩張表需要擁有相同的分區(qū)鍵;

(2)它們的表結(jié)構(gòu)完全相同。

# 假設(shè)數(shù)據(jù)表partition_v2與先前的partition_v1分區(qū)鍵和表結(jié)構(gòu)完全相同
# 那么應(yīng)先在partition_v1中寫入一批8月份的新數(shù)據(jù)
INSERT INTO partition_v1 VALUES ('A006-v1','www.v1.com','2019-08-05'),('A007-v1','www.v1.com','2019-08-20');
# 再執(zhí)行下面的語句
ALTER TABLE partition_v2 REPLACE PARTITION 201908 FROM partition_v1;
# 即能夠?qū)artition_v1的整個(gè)201908分區(qū)中的數(shù)據(jù)復(fù)制到partition_v2
  • 重置分區(qū)數(shù)據(jù)

如果數(shù)據(jù)表某一列的數(shù)據(jù)有誤,需要將其重置為初始值,此時(shí)可以使用下面的語句實(shí)現(xiàn):

ALTER TABLE tb_name CLEAR COLUMN column_name IN PARTITION partition_expr;

對(duì)于默認(rèn)值的含義,遵循如下原則:如果聲明了默認(rèn)值表達(dá)式,則以表達(dá)式為準(zhǔn);否則以相應(yīng)數(shù)據(jù)類型的默認(rèn)值為準(zhǔn)。

  • 卸載與裝載分區(qū)

表分區(qū)可以通過DETACH語句卸載,分區(qū)被卸載后,它的物理數(shù)據(jù)并沒有刪除,而是被轉(zhuǎn)移到了當(dāng)前數(shù)據(jù)表目錄的detached子目錄下。而裝載分區(qū)則是反向操作,它能夠?qū)etached子目錄下的某個(gè)分區(qū)重新裝載回去。卸載與裝載這一對(duì)伴生的操作,常用于分區(qū)數(shù)據(jù)的遷移和備份場景。

卸載某個(gè)分區(qū)的語法如下所示:

ALTER TABLE tb_name DETACH PARTITION partition_expr;

一旦分區(qū)被移動(dòng)到了detached子目錄,就代表它已經(jīng)脫離了ClickHouse的管理,ClickHouse并不會(huì)主動(dòng)清理這些文件。這些分區(qū)文件會(huì)一直存在,除非我們主動(dòng)刪除或者使用ATTACH語句重新裝載它們。

裝載某個(gè)分區(qū)的完整語法如下所示:

ALTER TABLE tb_name ATTACH PARTITION partition_expr;

9. 分布式DDL

ClickHouse支持集群模式,一個(gè)集群擁有1到多個(gè)節(jié)點(diǎn)。CREATE、ALTER、DROP、RENMAE及TRUNCATE這些DDL語句,都支持分布式執(zhí)行。這意味著,如果在集群中任意一個(gè)節(jié)點(diǎn)上執(zhí)行DDL語句,那么集群中的每個(gè)節(jié)點(diǎn)都會(huì)以相同的順序執(zhí)行相同的語句。這項(xiàng)特性意義非凡,它就如同批處理命令一樣,省去了需要依次去單個(gè)節(jié)點(diǎn)執(zhí)行DDL的煩惱。

將一條普通的DDL語句轉(zhuǎn)換成分布式執(zhí)行十分簡單,只需加上ON CLUSTER cluster_name聲明即可。例如,執(zhí)行下面的語句后將會(huì)對(duì)ch_cluster集群內(nèi)的所有節(jié)點(diǎn)廣播這條DDL語句:

CREATE TABLE partition_v3 ON CLUSTER ch_cluster( 
    ID String,
    URL String,
    EventTime Date
) ENGINE =  MergeTree()
PARTITION BY toYYYYMM(EventTime)
ORDER BY ID;

10. 數(shù)據(jù)寫入

INSERT語句支持三種語法范式,三種范式各有不同,可以根據(jù)寫入的需求靈活運(yùn)用。

第一種是使用VALUES格式的常規(guī)語法:

INSERT INTO [db.]table [(c1, c2, c3…)] VALUES (v11, v12, v13…), (v21, v22, v23…), ...

其中,c1、c2、c3是列字段聲明,可省略。VALUES后緊跟的是由元組組成的待寫入數(shù)據(jù),通過下標(biāo)位與列字段聲明一一對(duì)應(yīng)。數(shù)據(jù)支持批量聲明寫入,多行數(shù)據(jù)之間使用逗號(hào)分隔

在使用VALUES格式的語法寫入數(shù)據(jù)時(shí),支持加入表達(dá)式或函數(shù),例如:

INSERT INTO partition_v2 VALUES ('A0014',toString(1+2),now());

第二種是使用指定格式的語法:

INSERT INTO [db.]table [(c1, c2, c3…)] FORMAT format_name data_set

以常用的CSV格式寫入為例:

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 ...

在通過SELECT子句寫入數(shù)據(jù)的時(shí)候,同樣也支持加入表達(dá)式或函數(shù),雖然VALUES和SELECT子句的形式都支持聲明表達(dá)式或函數(shù),但是表達(dá)式和函數(shù)會(huì)帶來額外的性能開銷,從而導(dǎo)致寫入性能的下降。所以如果追求極致的寫入性能,就應(yīng)該盡可能避免使用它們。

ClickHouse內(nèi)部所有的數(shù)據(jù)操作都是面向Block數(shù)據(jù)塊的,所以INSERT語句最終會(huì)將數(shù)據(jù)轉(zhuǎn)換為Block數(shù)據(jù)塊。也正因如此,INSERT語句在單個(gè)數(shù)據(jù)塊的寫入過程中是具有原子性的。在默認(rèn)的情況下,每個(gè)數(shù)據(jù)塊最多可以寫入1048576行數(shù)據(jù)(由max_insert_block_size參數(shù)控制)。也就是說,如果一條INSERT語句寫入的數(shù)據(jù)少于max_insert_block_size行,那么這批數(shù)據(jù)的寫入是具有原子性的,即要么全部成功,要么全部失敗。需要注意的是,只有在ClickHouse服務(wù)端處理數(shù)據(jù)的時(shí)候才具有這種原子寫入的特性,例如使用JDBC或者HTTP接口時(shí)。因?yàn)閙ax_insert_block_size參數(shù)在使用CLI命令行或者INSERT SELECT子句寫入時(shí)是不生效的。

11. 數(shù)據(jù)的刪除與修改

ClickHouse提供了DELETE和UPDATE的能力,這類操作被稱為Mutation查詢,它可以看作ALTER語句的變種。雖然Mutation能最終實(shí)現(xiàn)修改和刪除,但不能完全以通常意義上的UPDATE和DELETE來理解,我們必須清醒地認(rèn)識(shí)到它的不同:

  • 首先,Mutation語句是一種"很重"的操作,更適用于批量數(shù)據(jù)的修改和刪除;
  • 其次,它不支持事務(wù),一旦語句被提交執(zhí)行,就會(huì)立刻對(duì)現(xiàn)有數(shù)據(jù)產(chǎn)生影響,無法回滾;
  • 最后,Mutation語句的執(zhí)行是一個(gè)異步的后臺(tái)過程,語句被提交之后就會(huì)立即返回。所以這并不代表具體邏輯已經(jīng)執(zhí)行完畢,它的具體執(zhí)行進(jìn)度需要通過system.mutations系統(tǒng)表查詢。

DELETE語句的完整語法如下所示:

ALTER TABLE [db_name.]table_name DELETE WHERE filter_expr

刪除過程:

  • 每一此刪除操作,都會(huì)生成一個(gè)mutation_id
SELECT database, table, mutation_id, block_numbers.number as num, is_done FROM system.mutations;

┌─database─┬─table────────┬─mutation_id────┬─num──┬─is_done─┐
│ default  │ partition_v2 │ mutation_6.txt │ [6]  │ 1       │
└──────────┴──────────────┴────────────────┴──────┴─────────┘

在數(shù)據(jù)表的根目錄下,會(huì)以mutation_id為名生成與之對(duì)應(yīng)的日志文件,其中完整地記錄了這次DELETE操作的執(zhí)行語句和時(shí)間,數(shù)據(jù)刪除的過程是以數(shù)據(jù)表的每個(gè)分區(qū)目錄為單位,將所有目錄重寫為新的目錄,新目錄的命名規(guī)則是在原有名稱上加上system.mutations.block_numbers.number的值,所以執(zhí)行刪除操作之前的分區(qū)目錄如下:

201905_1_1_0

刪除操作:

201905_1_1_0
# 生成了新的目錄
201905_1_1_0_6
# 日志文件
mutation_6.txt

數(shù)據(jù)會(huì)從201905_1_1_0目錄中重寫一份到201905_1_1_0_6目錄中,這個(gè)過程中會(huì)在201905_1_1_0_6目錄中將需要?jiǎng)h除的數(shù)據(jù)去掉。

舊的數(shù)據(jù)目錄并不會(huì)立即刪除,而是會(huì)被標(biāo)記成非激活狀態(tài)。等到MergeTree引擎的下一次合并動(dòng)作觸發(fā)時(shí),這些非激活目錄才會(huì)被真正從物理意義上刪除。

據(jù)修改除了需要指定具體的列字段之外,整個(gè)邏輯與數(shù)據(jù)刪除別無二致,它的完整語法如下所示:

ALTER TABLE [db_name.]table_name UPDATE column1 = expr1 [, ...] WHERE filter_expr;

UPDATE支持在一條語句中同時(shí)定義多個(gè)修改字段,分區(qū)鍵和主鍵不能作為修改字段。例如,執(zhí)行下面的語句即能夠根據(jù)WHERE條件同時(shí)修改partition_v2內(nèi)的URL和OS字段:

ALTER TABLE partition_v2 UPDATE URL = 'www.wayne.com',OS = 'mac' WHERE ID IN (SELECT ID FROM partition_v2 WHERE EventTime = '2019-06-01');
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容