clickhouse入门教程_clickhouse语法

clickhouse入门教程_clickhouse语法ClickHouse入门 1. 简介 ClickHouse 是俄罗斯的 Yandex 于 2016 年开源的列式存储数据库(DBMS),使用 C++ 语言编写,主要用于在线分析处理查询(OLAP),能

ClickHouse入门

ClickHouse入门

目录
  • ClickHouse入门
    • 1. 简介
      • 1.1 列式存储
      • 1.2 DBMS 的功能
      • 1.3 多样化引擎
      • 1.4 高吞吐写入能力
      • 1.5 数据分区与线程级并行
      • 1.6 性能对比
    • 2. 安装
      • 2.1 准备工作
        • 2.1.1 关闭防火墙
        • 2.1.2 修改打开文件数量限制
      • 2.2 单机安装
        • 2.2.1 Ubuntu
        • 2.2.2 CentOS
      • 2.3 配置文件
        • 2.3.1 服务端配置
        • 2.3.2 修改允许外部主机访问
      • 2.4 启动连接
    • 3. 数据类型
      • 3.1 整型
      • 3.2 浮点型
      • 3.3 布尔型
      • 3.4 Decimal 型
      • 3.5 字符串
      • 3.6 枚举类型
      • 3.7 时间类型
      • 3.8 数组
    • 4. 表引擎
      • 4.1 TinyLog
      • 4.2 Memory
      • 4.3 MergeTree
        • 4.3.1 PARTITION BY 分区(可选)
        • 4.3.2 PRIMARY KEY 主键(可选)
        • 4.3.3 ORDER BY (必填)
        • 4.3.4 二级索引
        • 4.3.5 数据 TTL
          • 1. 列级别 TTL
          • 2. 表级别 TTL
      • 4.4 ReplacingMergeTree
      • 4.5 SummingMergeTree
    • 5. SQL 操作
      • 5.1 Insert
      • 5.2 Update 和 Delete
      • 5.3 Select
      • 5.4 Alter
      • 5.5 导出数据
    • 6. 副本
      • 6.1 副本写入流程
      • 6.2 配置步骤
      • 6.3 测试
    • 7. 分片集群
        • 7.1 集群写入流程
        • 7.2 集群读取流程

1. 简介

ClickHouse 是俄罗斯的 Yandex 于 2016 年开源的列式存储数据库(DBMS),使用 C++ 语言编写,主要用于在线分析处理查询(OLAP),能够使用 SQL 查询实时生成分析数据报告。

1.1 列式存储

以下面的表为例:

Id Name Age
1 张三 18
2 李四 22
3 王五 34
  • 采用行式存储时,数据在磁盘上的组织结构为:

    1 张三 18 2 李四 22 3 王五 34

    优点时向查询某个人的所有属性时,可以通过一次磁盘查找加顺序读取就可以。但是当想查所有人的年龄时,需要不停的查找,或者全表扫描才行,遍历的很多数据都是不需要的。

  • 采用列式存储时,数据在磁盘上的组织结构为:

    1 2 3 张三 李四 王五 18 22 34

    这时想查询所有人的年龄只需把年龄那一列拿出来就可以了。

  • 列式存储的优点:

    • 对于列的聚合,计数,求和等统计操作原因优于行式存储。
    • 由于某一列的数据类型都是相同的,针对于数据存储更容易进行数据压缩,每一列选择更优的数据压缩算法,大大提高了数据的压缩比重。
    • 由于数据压缩比较好,一方面节省了磁盘空间,另一方面对于 cache 也有了更大的发挥空间。

1.2 DBMS 的功能

几乎覆盖了标准 SQL 的大部分语法,包括 DDL 和 DML,以及配套的各种函数,用户管理及权限管理,数据的备份与恢复。

1.3 多样化引擎

ClickHouse 和 MySQL 类似,把表级的存储引擎插件化,根据表的不同需求可以设定不同的存储引擎。目前包括合并树(Merge Tree)、日志、接口和其它四大类 20 多种引擎。

1.4 高吞吐写入能力

ClickHouse 采用类 LMS Tree(Log Structured Merge Tree)的结构,数据写入后定期在后台 Compaction。通过类 LMS Tree 的结构,ClickHouse 在数据导入时全部都是顺序 append 写,写入后数据段不可更改,在后台 compaction 时也是多个段 merge sort 后顺序写回磁盘。顺序写的特性,充分利用了磁盘的吞吐能力,即便在 HDD 上也有着优异的写入性能。

官方公开 benchmark 测试显示能够达到 50MB-200MB/s 的写入吞吐能力,按照每行 100 Byte 估算,大约相当于 50W-200W条/s 的写入速度。

1.5 数据分区与线程级并行

ClickHouse 将数据划分为多个 Partition, 每个 Partition 再进一步划分为多个 Index Granularity(索引粒度),然后通过多个 CPU 核心分别处理其中的一部分来实现并行数据处理。在这种设计下,单条 Query 就能利用整机所有 CPU。极致的并行处理能力,极大的降低了查询延迟。

所以,ClickHouse 即使对于大量数据的查询也能够化整为零平行处理。但是有一个弊端就是对于单条查询使用多 CPU,就不利用同时并发多条查询。所以对于高 QPS(Queries-per-second) 的查询业务,ClickHouse 并不是强项。

1.6 性能对比

  • 单表查询
  • 关联查询

结论:ClickHouse 像很多 OLAP 数据库一样,单表查询速度优于关联查询,而且 ClickHouse 的两者差距更为明显。

2. 安装

2.1 准备工作

2.1.1 关闭防火墙

# CentOS
$ sudo systemctl stop firewalld
$ sudo systemctl status firewalld
● firewalld.service
   Loaded: masked (/dev/null; bad)
   Active: inactive (dead)

# Ubuntu
$ sudo ufw disable
$ sudo ufw status
Status: inactive

2.1.2 修改打开文件数量限制

编辑 /etc/security/limits.conf文件(可能同时需要修改/etc/security/limits.d文件夹下的配置文件),加入以下内容:

$ ulimit -a
-t: cpu time (seconds)              unlimited
-f: file size (blocks)              unlimited
-d: data seg size (kbytes)          unlimited
-s: stack size (kbytes)             8192
-c: core file size (blocks)         0
-m: resident set size (kbytes)      unlimited
-u: processes                       131072
-n: file descriptors                65536
-l: locked-in-memory size (kbytes)  65536
-v: address space (kbytes)          unlimited
-x: file locks                      unlimited
-i: pending signals                 63858
-q: bytes in POSIX msg queues       819200
-e: max nice                        0
-r: max rt priority                 0
-N 15:                              unlimited

$ sudo vim /etc/security/limits.conf
# 用户@用户组 | 软限制soft 或 硬限制hard | 限制项 | 限制大小
# 打开文件数量限制
* soft nofile 65536
* hard nofile 65536
# 用户进程数量限制
* soft nproc 131072
* hard nproc 131072

注:需要重启或重新登录后才能生效(测试 SSH 重新登录不生效)。

2.2 单机安装

2.2.1 Ubuntu

sudo apt-get install apt-transport-https ca-certificates dirmngr
sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv E0C56BD4

echo "deb https://repo.clickhouse.tech/deb/stable/ main/" | sudo tee 
    /etc/apt/sources.list.d/clickhouse.list
sudo apt-get update

sudo apt-get install -y clickhouse-server clickhouse-client

sudo service clickhouse-server start
clickhouse-client

清华镜像源加速:修改/etc/apt/sources.list.d/clickhouse.list文件,内容为

deb https://mirrors.tuna.tsinghua.edu.cn/clickhouse/deb/stable/ main/

2.2.2 CentOS

sudo yum install yum-utils
sudo rpm --import https://repo.clickhouse.tech/CLICKHOUSE-KEY.GPG
sudo yum-config-manager --add-repo https://repo.clickhouse.tech/rpm/clickhouse.repo
sudo yum install clickhouse-server clickhouse-client

sudo /etc/init.d/clickhouse-server start
clickhouse-client

清华镜像源加速:修改/etc/yum.repos.d/clickhouse.repo文件,内容为

[repo.yandex.ru_clickhouse_rpm_stable_x86_64]
name=clickhouse stable
baseurl=https://mirrors.tuna.tsinghua.edu.cn/clickhouse/rpm/stable/x86_64
enabled=1

2.3 配置文件

2.3.1 服务端配置

配置文件目录为/etc/clickhouse-server,目录结构如下:

  • config.d
  • config.xml:服务端配置
  • users.d
  • users.xml:权限密码,参数配置(CPU,内存等)

2.3.2 修改允许外部主机访问

编辑/etc/clickhouse-server/config.xml,取消<!-- <listen_host>::</listen_host> -->的注释。

2.4 启动连接

# 启动 ClickHouse 服务端
$ sudo systemctl start clickhouse-server
# 或
$ sudo clickhouse start

# 查看服务端状态
$ sudo systemctl status clickhouse-server
# 或
$ sudo clickhouse status

# 开启开机自启动
$ sudo systemctl enable clickhouse-server
# 关闭开机自启动
$ sudo systemctl disable clickhouse-server

# 客户端连接,-m 参数设置语句允许换行
$ clickhouse-client -m -h localhost -p 9000

3. 数据类型

参考官方文档:https://clickhouse.tech/docs/zh/sql-reference/data-types/

3.1 整型

固定长度的整型,包括有符号整型或无符号整型。

整型范围(-2n-1 ~ 2n-1-1):

  • Int8 – [-128 : 127]

  • Int16 – [-32768 : 32767]

  • Int32 – [-2147483648 – 2147483647]

  • Int63 – [-9223372036854775808 – 9223372036854775807]

无符号整型范围(0 ~ 2n-1):

  • UInt8 – [0 : 255]
  • UInt16 – [0 : 65535]
  • UInt32 – [0 : 4294967295]
  • UInt64 – [0 : 18446744073709551615]

使用场景:个数、数量、也可以存储 ID。

3.2 浮点型

  • Float32 – float
  • Float64 – double

建议尽可能以整数形式存储数据。例如,将固定精度的数字转换为整数值,如时间用毫秒为单位表示,因为浮点型进行计算时可能引起四舍五入的误差。

:) select 1.0 - 0.9;

SELECT 1. - 0.9

Query id: 29890dfc-cc42-4fd8-8bd4-edf9aca7136f

┌──────minus(1., 0.9)─┐
│ 0.09999999999999998 │
└─────────────────────┘

1 rows in set. Elapsed: 0.002 sec.

使用场景:一般数据值比较小,不涉及大量的统计计算,精度要求不高的时候。比如保存商品的重量。

3.3 布尔型

没有单独的类型来存储布尔值。可以使用 UInt8 类型,取值限制为 0 或 1。

3.4 Decimal 型

有符号的浮点数,可在加、减和乘法运算过程中保持精度。对于除法,最低有效数字会被丢弃(不舍入)。

  • Decimal32(s),相当于 Decimal(9-s,s),有效位数 1~9
  • Decimal64(s),相当于 Decimal(18-s,s),有效位数1~18
  • Decimal128(s),相当于 Decimal(38-s,s),有效位数1~38

说明:s 标识小数位。

使用场景:一般金额、汇率、利率等字段为了保证小数点精度,都使用 Decimal 进行存储。

3.5 字符串

  • String

    字符串可以是任意长度的,它可以包含任意的字节集,包含空字节。

  • FixedString(N)

    固定长度 N 的字符串,N 必须是严格的正自然数。当服务端读取长度小于 N 的字符串的时候,通过在字符串末尾添加空字节来达到 N 字节长度;当服务端读取长度大于 N 的字符串的时候,将返回错误消息。

    与 String 相比,极少会使用 FixedString,因为使用起来不是很方便。

使用场景:名称、文字描述、字符型编码。固定长度的可以保存一些定长的内容,比如一些编码、性别等,但是考虑到一定的变化风险,带来收益不够明显,所以定长字符串使用意义有限。

3.6 枚举类型

包括 Enum8 和 Enum16 类型。Enum 保存 ‘String’ = Integer 的对应关系。

  • Enum8 用 ‘String’ = Integer 对表示
  • Enum16 用 ‘String’ = Integer 对表示
-- 创建一个带有 Enum8("hello" = 1, "world" = 2) 类型的表
CREATE TABLE t_enum
(
    x Enum8("hello" = 1, "world" = 2)
) ENGINE = TinyLog;

-- 插入数据
INSERT INTO t_enum
VALUES ("hello"),
       ("world"),
       ("hello");

-- 尝试插入其它值
INSERT INTO t_enum
VALUES ("haha");

-- 查看枚举对应数值
SELECT cast(x, "Int8")
FROM t_enum;

使用场景:对一些状态、类型等字段算是一种空间优化,也算是一种数据约束。但是实际使用中往往会因为一些数据内容的变化增加一定的维护成本,甚至是数据丢失问题,所以谨慎使用。

3.7 时间类型

  • Date 接受年-月-日的字符串,比如:‘2021-08-01’
  • DateTime 接受年-月-日 时:分:秒的字符串,比如:‘2021-08-01 00:25:12’
  • DateTime64 接受年-月-日 时:分:秒.亚秒的字符串,比如:‘2021-08-01 00:25:12.66’

日期类型,用两个字节存储,表示从 1970-01-01(无符号)到当前的日期值。

3.8 数组

Array(T):由 T 类型元素组成的数组。

T 可以是任意类型,包含数组类型。但不推荐使用多维数组,ClickHouse 对多维数组的支持有限。例如,不能在 Merge Tree 表中存储多维数组。

:) select array(1, 2) as x, toTypeName(x);

SELECT
    [1, 2] AS x,
    toTypeName(x)

Query id: cfd2208b-bdbd-4809-a9d5-2ba3441b24c6

┌─x─────┬─toTypeName(array(1, 2))─┐
│ [1,2] │ Array(UInt8)            │
└───────┴─────────────────────────┘

1 rows in set. Elapsed: 0.002 sec.

4. 表引擎

表引擎是 ClickHouse 的一大特色。可以说,表引擎决定了如何存储表的数据。包括:

  • 数据的存储方式和位置,写到哪里以及从哪里读取数。
  • 支持哪些查询以及如何支持。
  • 并发数据访问。
  • 索引的使用(如果存在)。
  • 是否可以执行多线程请求。
  • 数据复制参数。

表引擎的使用方式就是必须显式的在创建表时定义该表使用的引擎,以及引擎使用的相关参数。

注意:引擎的名称大小写敏感。

4.1 TinyLog

列文件的形式保存在磁盘上,不支持索引没有并发控制。一般保存少量数据的小表,

生产环境上作用有限。可用于平时练习测试使用。

CREATE TABLE t_tinylog
(
    id   String,
    name String
) ENGINE = TinyLog;

4.2 Memory

内存引擎,数据以未压缩的原始形式直接保存在内存当中,服务器重启数据就会消失。读写操作不会相互阻塞不支持索引。简单查询下有非常非常高的性能表现(超过 10G/S)

一般用的地方不多,除了用来测试,就是在需要非常高性能,同时数据量又不太大(上限大概 1 亿行)的场景。

4.3 MergeTree

ClickHouse 中最强大的表引擎当属 MergeTree(合并树)引擎及该系列(*MergeTree)中的其它引擎,支持索引和分区,地位可以相当于 InnoDB 之于 MySQL。而且基于 MergeTree,还衍生出了很多子引擎,也是非常有特色的引擎。

-- 创建表
CREATE TABLE t_order_mt
(
    id           UInt32,
    sku_id       String,
    total_amount Decimal(16, 2),
    create_time  DateTime
) ENGINE = MergeTree
      PARTITION BY toYYYYMMDD(create_time)
      PRIMARY KEY (id)
      ORDER BY (id, sku_id);
 
-- 插入数据
INSERT INTO t_order_mt
VALUES (101, "sku_001", 1000.00, "2021-08-01 12:00:00"),
       (102, "sku_002", 2000.00, "2021-08-01 11:00:00"),
       (102, "sku_004", 2500.00, "2021-08-01 12:00:00"),
       (102, "sku_002", 2000.00, "2021-08-01 13:00:00"),
       (102, "sku_002", 12000.00, "2021-08-01 13:00:00"),
       (102, "sku_002", 600.00, "2021-08-02 12:00:00");
       
-- 查看数据,按分区显示
:) select * from t_order_mt;

SELECT *
FROM t_order_mt

Query id: 49467be5-462d-43a1-8293-6a8eea414c13

┌──id─┬─sku_id──┬─total_amount─┬─────────create_time─┐
│ 102 │ sku_002 │       600.00 │ 2021-08-02 12:00:00 │
└─────┴─────────┴──────────────┴─────────────────────┘
┌──id─┬─sku_id──┬─total_amount─┬─────────create_time─┐
│ 101 │ sku_001 │      1000.00 │ 2021-08-01 12:00:00 │
│ 102 │ sku_002 │      2000.00 │ 2021-08-01 11:00:00 │
│ 102 │ sku_002 │      2000.00 │ 2021-08-01 13:00:00 │
│ 102 │ sku_002 │     12000.00 │ 2021-08-01 13:00:00 │
│ 102 │ sku_004 │      2500.00 │ 2021-08-01 12:00:00 │
└─────┴─────────┴──────────────┴─────────────────────┘

6 rows in set. Elapsed: 0.003 sec.

注意:ClickHouse 的主键并没有自动添加唯一约束,可以重复。

MergeTree 其实还有很多参数(绝大多数用默认值即可),但以上三个参数是比较重要的,也涉及了关于 MergeTree 的很多概念。

4.3.1 PARTITION BY 分区(可选)

  • 作用:降低扫描的范围,优化查询速度。

  • 不分区:只会使用一个分区。

  • 分区目录:MergeTree 是以列文件 + 索引文件 + 表定义文件组成的,但是如果设定了分区那么这些文件就会保存到不同的分区目录中。

  • 并行:分区后,面对涉及跨分区的查询统计,ClickHouse 会以分区为单位进行处理。

  • 数据写入与分区合并:任何一个批次的数据写入都会产生一个临时分区,不会纳入任何一个已有的分区。写入后的某个时刻(大概 10-15 分钟后),ClickHouse 会自动执行合并操作(等不及也可以手动通过 optimize 执行),把临时分区的数据合并到已有分区中。

    -- 优化 xxx 表
    optimize table xxx final;
    
    -- 优化 xxx 表的 xxx 分区
    optimize table xxx partition xxx final;
    

    例如:

    -- 再次插入上面的数据
    INSERT INTO t_order_mt
    VALUES (101, "sku_001", 1000.00, "2021-08-01 12:00:00"),
           (102, "sku_002", 2000.00, "2021-08-01 11:00:00"),
           (102, "sku_004", 2500.00, "2021-08-01 12:00:00"),
           (102, "sku_002", 2000.00, "2021-08-01 13:00:00"),
           (102, "sku_002", 12000.00, "2021-08-01 13:00:00"),
           (102, "sku_002", 600.00, "2021-08-02 12:00:00");
    
    -- 查看数据,多出两个临时分区
    :) select * from t_order_mt;
    
    SELECT *
    FROM t_order_mt
    
    Query id: 54943ffa-7d59-4b0b-8a7f-500f323c10a7
    
    ┌──id─┬─sku_id──┬─total_amount─┬─────────create_time─┐
    │ 102 │ sku_002 │       600.00 │ 2021-08-02 12:00:00 │
    └─────┴─────────┴──────────────┴─────────────────────┘
    ┌──id─┬─sku_id──┬─total_amount─┬─────────create_time─┐
    │ 102 │ sku_002 │       600.00 │ 2021-08-02 12:00:00 │
    └─────┴─────────┴──────────────┴─────────────────────┘
    ┌──id─┬─sku_id──┬─total_amount─┬─────────create_time─┐
    │ 101 │ sku_001 │      1000.00 │ 2021-08-01 12:00:00 │
    │ 102 │ sku_002 │      2000.00 │ 2021-08-01 11:00:00 │
    │ 102 │ sku_002 │      2000.00 │ 2021-08-01 13:00:00 │
    │ 102 │ sku_002 │     12000.00 │ 2021-08-01 13:00:00 │
    │ 102 │ sku_004 │      2500.00 │ 2021-08-01 12:00:00 │
    └─────┴─────────┴──────────────┴─────────────────────┘
    ┌──id─┬─sku_id──┬─total_amount─┬─────────create_time─┐
    │ 101 │ sku_001 │      1000.00 │ 2021-08-01 12:00:00 │
    │ 102 │ sku_002 │      2000.00 │ 2021-08-01 11:00:00 │
    │ 102 │ sku_002 │      2000.00 │ 2021-08-01 13:00:00 │
    │ 102 │ sku_002 │     12000.00 │ 2021-08-01 13:00:00 │
    │ 102 │ sku_004 │      2500.00 │ 2021-08-01 12:00:00 │
    └─────┴─────────┴──────────────┴─────────────────────┘
    
    12 rows in set. Elapsed: 0.004 sec.
    
    -- 手动执行优化命令
    :) OPTIMIZE TABLE  t_order_mt FINAL;
    
    -- 再次查看数据
    :) select * from t_order_mt;
    
    SELECT *
    FROM t_order_mt
    
    Query id: a4a9e510-b4a9-419b-8596-f9fe9016be1b
    
    ┌──id─┬─sku_id──┬─total_amount─┬─────────create_time─┐
    │ 102 │ sku_002 │       600.00 │ 2021-08-02 12:00:00 │
    │ 102 │ sku_002 │       600.00 │ 2021-08-02 12:00:00 │
    └─────┴─────────┴──────────────┴─────────────────────┘
    ┌──id─┬─sku_id──┬─total_amount─┬─────────create_time─┐
    │ 101 │ sku_001 │      1000.00 │ 2021-08-01 12:00:00 │
    │ 101 │ sku_001 │      1000.00 │ 2021-08-01 12:00:00 │
    │ 102 │ sku_002 │      2000.00 │ 2021-08-01 11:00:00 │
    │ 102 │ sku_002 │      2000.00 │ 2021-08-01 13:00:00 │
    │ 102 │ sku_002 │     12000.00 │ 2021-08-01 13:00:00 │
    │ 102 │ sku_002 │      2000.00 │ 2021-08-01 11:00:00 │
    │ 102 │ sku_002 │      2000.00 │ 2021-08-01 13:00:00 │
    │ 102 │ sku_002 │     12000.00 │ 2021-08-01 13:00:00 │
    │ 102 │ sku_004 │      2500.00 │ 2021-08-01 12:00:00 │
    │ 102 │ sku_004 │      2500.00 │ 2021-08-01 12:00:00 │
    └─────┴─────────┴──────────────┴─────────────────────┘
    
    12 rows in set. Elapsed: 0.004 sec.
    

4.3.2 PRIMARY KEY 主键(可选)

ClickHouse 中的主键,和其它数据库不太一样,它只提供了一级索引,但是却不是唯一约束。这就意味着是可以存在相同 primary key 的数据的。

主键的设定主要依据是查询语句中的 where 条件。

根据条件通过对主键进行某种形式的二分查找,能够定位到对应的 index granularity,避免了全表扫描。

index_granularity:直译为索引粒度,指在稀疏索引中两个相邻索引对应数据的间隔。ClickHouse 中的 MergeTree 引擎默认是 8192。官方不建议修改该值,除非该列存在大量重复值,比如在一个分区中几万行才有一个不同数据。

稀疏索引:按指定间隔记录索引值,优点是可以用很少的索引数据,定位更多的数据,代价就是只能定位到索引粒度的第一行,然后再进行行扫描。

4.3.3 ORDER BY (必填)

order by 设定了分区内的数据按照哪些字段进行有序保存。

order by 是 MergeTree 中唯一一个必填项,甚至比 primary key 还重要,因为当用户不设置主键的时候,很多处理会依照 order by 的字段进行处理(比如去重和汇总)。

要求:主键必须是 order by 字段的前缀字段。比如 order by 字段是 (id, sku_id),那么主键必须是 id 或者 (id, sku_id)。

4.3.4 二级索引

目前 ClickHouse 的官网上二级索引的功能在 v20.1.2.4 之前是被标注为实验性的,在该版本之后默认是开启的。

  1. 老版本使用二级索引前需要增加设置

    是否允许使用实验性的二级索引(v20.1.2.4 开始,该参数已经被删除,默认开启)

    set allow_experimental_data_skipping_indices=1
    
  2. 创建测试表

    CREATE TABLE t_order_mt2
    (
        id           UInt32,
        sku_id       String,
        total_amount Decimal(16, 2),
        create_time  DateTime,
        INDEX a total_amount TYPE minmax GRANULARITY 5
    ) ENGINE = MergeTree
          PARTITION BY toYYYYMMDD(create_time)
          PRIMARY KEY (id)
          ORDER BY (id, sku_id);
    

    说明:其中GRANULARITY N是设定二级索引对于一级索引粒度的粒度。

  3. 插入数据

    INSERT INTO t_order_mt2
    VALUES (101, "sku_001", 1000.00, "2021-08-01 12:00:00"),
           (102, "sku_002", 2000.00, "2021-08-01 11:00:00"),
           (102, "sku_004", 2500.00, "2021-08-01 12:00:00"),
           (102, "sku_002", 2000.00, "2021-08-01 13:00:00"),
           (102, "sku_002", 12000.00, "2021-08-01 13:00:00"),
           (102, "sku_002", 600.00, "2021-08-02 12:00:00");
    
  4. 测试效果

    $ clickhouse-client --send_logs_level=trace <<< "select * from t_order_mt2 where total_amount > toDecimal32(900, 2)";
    [Ubuntu18-006] 2021.08.06 11:34:17.603474 [ 29915 ] {4cc867dd-fd0d-4b7e-8ec3-f404e70b190d} <Debug> executeQuery: (from [::ffff:127.0.0.1]:34884, using production parser) select * from t_order_mt2 where total_amount > toDecimal32(900, 2)
    [Ubuntu18-006] 2021.08.06 11:34:17.603873 [ 29915 ] {4cc867dd-fd0d-4b7e-8ec3-f404e70b190d} <Debug> InterpreterSelectQuery: MergeTreeWhereOptimizer: condition "total_amount > toDecimal32(900, 2)" moved to PREWHERE
    [Ubuntu18-006] 2021.08.06 11:34:17.604018 [ 29915 ] {4cc867dd-fd0d-4b7e-8ec3-f404e70b190d} <Trace> ContextAccess (default): Access granted: SELECT(id, sku_id, total_amount, create_time) ON default.t_order_mt2
    [Ubuntu18-006] 2021.08.06 11:34:17.604077 [ 29915 ] {4cc867dd-fd0d-4b7e-8ec3-f404e70b190d} <Trace> InterpreterSelectQuery: FetchColumns -> Complete
    [Ubuntu18-006] 2021.08.06 11:34:17.604248 [ 29915 ] {4cc867dd-fd0d-4b7e-8ec3-f404e70b190d} <Debug> default.t_order_mt2 (482d26fd-99ef-4e94-882d-26fd99efbe94) (SelectExecutor): Key condition: unknown
    [Ubuntu18-006] 2021.08.06 11:34:17.604324 [ 29915 ] {4cc867dd-fd0d-4b7e-8ec3-f404e70b190d} <Debug> default.t_order_mt2 (482d26fd-99ef-4e94-882d-26fd99efbe94) (SelectExecutor): MinMax index condition: unknown
    [Ubuntu18-006] 2021.08.06 11:34:17.607684 [ 29915 ] {4cc867dd-fd0d-4b7e-8ec3-f404e70b190d} <Debug> default.t_order_mt2 (482d26fd-99ef-4e94-882d-26fd99efbe94) (SelectExecutor): Index `a` has dropped 1/2 granules.
    [Ubuntu18-006] 2021.08.06 11:34:17.607712 [ 29915 ] {4cc867dd-fd0d-4b7e-8ec3-f404e70b190d} <Debug> default.t_order_mt2 (482d26fd-99ef-4e94-882d-26fd99efbe94) (SelectExecutor): Selected 2/2 parts by partition key, 1 parts by primary key, 2/2 marks by primary key, 1 marks to read from 1 ranges
    [Ubuntu18-006] 2021.08.06 11:34:17.607770 [ 29915 ] {4cc867dd-fd0d-4b7e-8ec3-f404e70b190d} <Debug> MergeTreeSelectProcessor: Reading 1 ranges from part 20210801_1_1_0, approx. 5 rows starting from 0
    [Ubuntu18-006] 2021.08.06 11:34:17.608306 [ 29915 ] {4cc867dd-fd0d-4b7e-8ec3-f404e70b190d} <Information> executeQuery: Read 5 rows, 160.00 B in 0.004763978 sec., 1049 rows/sec., 32.80 KiB/sec.
    [Ubuntu18-006] 2021.08.06 11:34:17.608325 [ 29915 ] {4cc867dd-fd0d-4b7e-8ec3-f404e70b190d} <Debug> MemoryTracker: Peak memory usage (for query): 0.00 B.
    101     sku_001 1000.00 2021-08-01 12:00:00
    102     sku_002 2000.00 2021-08-01 11:00:00
    102     sku_002 2000.00 2021-08-01 13:00:00
    102     sku_002 12000.00        2021-08-01 13:00:00
    102     sku_004 2500.00 2021-08-01 12:00:00
    

4.3.5 数据 TTL

TTL 即 Time To Live,MergeTree 提供了可以管理数据或者列的生命周期的功能。

1. 列级别 TTL
-- 建立表,设置 total_amount 10 秒后过期
CREATE TABLE IF NOT EXISTS t_order_mt3
(
    id           UInt32,
    sku_id       String,
    total_amount Decimal(16, 2) TTL create_time + INTERVAL 10 SECOND,
    create_time  DateTime
) ENGINE = MergeTree
      PARTITION BY toYYYYMMDD(create_time)
      PRIMARY KEY (id)
      ORDER BY (id, sku_id);
      
-- 插入数据
INSERT INTO t_order_mt3
VALUES (106, "sku_001", 1000.00, now()),
       (107, "sku_002", 2000.00, now()),
       (108, "sku_003", 600.00, now());
       
-- 查看数据
:) select * from t_order_mt3;

SELECT *
FROM t_order_mt3

Query id: 4f00babb-7d60-4764-9fa3-2e6b75032959

┌──id─┬─sku_id──┬─total_amount─┬─────────create_time─┐
│ 106 │ sku_001 │      1000.00 │ 2021-08-06 13:48:06 │
│ 107 │ sku_002 │      2000.00 │ 2021-08-06 13:48:06 │
│ 108 │ sku_003 │       600.00 │ 2021-08-06 13:48:06 │
└─────┴─────────┴──────────────┴─────────────────────┘

3 rows in set. Elapsed: 0.002 sec.

-- 10 秒后执行 optimize 命令
:) optimize table t_order_mt3 final;

OPTIMIZE TABLE t_order_mt3 FINAL

Query id: 9a809a08-b25d-4602-9861-d4a1af4d4866

Ok.

0 rows in set. Elapsed: 0.001 sec.

-- 再次查看数据
:) select * from t_order_mt3;

SELECT *
FROM t_order_mt3

Query id: 3c7e091e-e8e0-49f7-8472-9603711f0cf6

┌──id─┬─sku_id──┬─total_amount─┬─────────create_time─┐
│ 106 │ sku_001 │         0.00 │ 2021-08-06 13:48:06 │
│ 107 │ sku_002 │         0.00 │ 2021-08-06 13:48:06 │
│ 108 │ sku_003 │         0.00 │ 2021-08-06 13:48:06 │
└─────┴─────────┴──────────────┴─────────────────────┘

3 rows in set. Elapsed: 0.002 sec.
2. 表级别 TTL

修改表,设置数据在create_time之后 10 秒丢失。

-- 修改表 TTL
ALTER TABLE t_order_mt3
    MODIFY TTL create_time + INTERVAL 10 SECOND;
    
-- 查看数据,已经被丢弃
:) select * from t_order_mt3;

SELECT *
FROM t_order_mt3

Query id: c4a60da0-7dfe-444b-a149-3c8ea2803692

Ok.

0 rows in set. Elapsed: 0.002 sec.

注意:涉及判断的字段必须是 Date 或者 Datetime 类型,推荐使用分区的日期字段,可用的时间周期包括 SECOND、MINUTE、HOUR、DAY、WEEK、MONTH、QUARTER、YEAR 。

4.4 ReplacingMergeTree

ReplacingMergeTree 是 MergeTree 的一个变种,它存储特性完全继承 MergeTree,只是多了一个去重的功能。尽管 MergeTree 可以设置主键,但是 primary key 并没有唯一约束的功能。如果想处理掉重复的数据,可以借助 ReplacingMergeTree 引擎。

  • 去重时机

    数据的去重只会在合并的过程中出现。合并会在未知的时间在后台进行,所以你无法预先做出计划。有一些数据可能仍未被处理。

  • 去重范围

    如果表经过了分区,去重只会在分区内部进行,不能执行跨分区的去重。

    所以 ReplacingMergeTree 能力有限,ReplacingMergeTree 适用于在后台清除重复的数据以节省空间,但是它不能保证没有重复的数据出现。

-- 创建表
CREATE TABLE IF NOT EXISTS t_order_rmt
(
    id           UInt32,
    sku_id       String,
    total_amount Decimal(16, 2),
    create_time  DateTime
) ENGINE = ReplacingMergeTree(create_time)
      PARTITION BY toYYYYMMDD(create_time)
      PRIMARY KEY (id)
      ORDER BY (id, sku_id);

-- 插入数据
INSERT INTO t_order_rmt
VALUES (101, "sku_001", 1000.00, "2021-08-01 12:00:00"),
       (102, "sku_002", 2000.00, "2021-08-01 11:00:00"),
       (102, "sku_004", 2500.00, "2021-08-01 12:00:00"),
       (102, "sku_002", 2000.00, "2021-08-01 13:00:00"),
       (102, "sku_002", 12000.00, "2021-08-01 13:00:00"),
       (102, "sku_002", 600.00, "2021-08-02 12:00:00");
INSERT INTO t_order_rmt
VALUES (101, "sku_001", 1000.00, "2021-08-01 12:00:00"),
       (102, "sku_002", 2000.00, "2021-08-01 11:00:00"),
       (102, "sku_004", 2500.00, "2021-08-01 12:00:00"),
       (102, "sku_002", 2000.00, "2021-08-01 13:00:00"),
       (102, "sku_002", 12000.00, "2021-08-01 13:00:00"),
       (102, "sku_002", 600.00, "2021-08-02 12:00:00");
       
-- 查看数据
:) select * from t_order_rmt;

SELECT *
FROM t_order_rmt

Query id: d3ff4146-724d-4827-8c4a-ee163c2bbfb0

┌──id─┬─sku_id──┬─total_amount─┬─────────create_time─┐
│ 102 │ sku_002 │       600.00 │ 2021-08-02 12:00:00 │
└─────┴─────────┴──────────────┴─────────────────────┘
┌──id─┬─sku_id──┬─total_amount─┬─────────create_time─┐
│ 101 │ sku_001 │      1000.00 │ 2021-08-01 12:00:00 │
│ 102 │ sku_002 │     12000.00 │ 2021-08-01 13:00:00 │
│ 102 │ sku_004 │      2500.00 │ 2021-08-01 12:00:00 │
└─────┴─────────┴──────────────┴─────────────────────┘
┌──id─┬─sku_id──┬─total_amount─┬─────────create_time─┐
│ 101 │ sku_001 │      1000.00 │ 2021-08-01 12:00:00 │
│ 102 │ sku_002 │     12000.00 │ 2021-08-01 13:00:00 │
│ 102 │ sku_004 │      2500.00 │ 2021-08-01 12:00:00 │
└─────┴─────────┴──────────────┴─────────────────────┘
┌──id─┬─sku_id──┬─total_amount─┬─────────create_time─┐
│ 102 │ sku_002 │       600.00 │ 2021-08-02 12:00:00 │
└─────┴─────────┴──────────────┴─────────────────────┘

8 rows in set. Elapsed: 0.009 sec.

-- 优化表合并分区,会进行去重
:) optimize table t_order_rmt final;

OPTIMIZE TABLE t_order_rmt FINAL

Query id: 48df496b-b2b4-4ffc-b808-5fecfb343402

Ok.

0 rows in set. Elapsed: 0.002 sec.

-- 再次查看数据,已经进行了合并去重
:) select * from t_order_rmt;

SELECT *
FROM t_order_rmt

Query id: dfad17ce-33b1-4098-be90-fa174abaef79

┌──id─┬─sku_id──┬─total_amount─┬─────────create_time─┐
│ 102 │ sku_002 │       600.00 │ 2021-08-02 12:00:00 │
└─────┴─────────┴──────────────┴─────────────────────┘
┌──id─┬─sku_id──┬─total_amount─┬─────────create_time─┐
│ 101 │ sku_001 │      1000.00 │ 2021-08-01 12:00:00 │
│ 102 │ sku_002 │     12000.00 │ 2021-08-01 13:00:00 │
│ 102 │ sku_004 │      2500.00 │ 2021-08-01 12:00:00 │
└─────┴─────────┴──────────────┴─────────────────────┘

4 rows in set. Elapsed: 0.002 sec.

注意:ClickHouse 新版本在插入进行分区时会进行一次去重,所以需要插入两批数据才能看到效果。

说明:ReplacingMergeTree() 填入的参数为版本字段,重复数据保留版本字段值最大的;如果不填版本字段,默认按照插入顺序保留最后一条。

结论:

  • 实际上是使用 order by 字段作为唯一键;
  • 去重不能跨分区;
  • 插入(新版本)或合并分区时才会进行去重;
  • 重复数据保留版本字段值最大的记录,如果版本字段相同则按插入顺序保留最后一条记录;

4.5 SummingMergeTree

对于不查询明细,只关心以维度进行汇总聚合结果的场景。如果只使用普通的 MergeTree 的话,无论是存储空间的开销,还是查询时临时聚合的开销都比较大。

ClickHouse 为了这种场景,提供了一种能够”预聚合“的引擎 SummingMergeTree 。

-- 创建表
CREATE TABLE IF NOT EXISTS t_order_smt
(
    id           UInt32,
    sku_id       String,
    total_amount Decimal(16, 2),
    create_time  DateTime
) ENGINE = SummingMergeTree(total_amount)
      PARTITION BY toYYYYMMDD(create_time)
      PRIMARY KEY (id)
      ORDER BY (id, sku_id);
      
-- 插入数据
INSERT INTO t_order_smt
VALUES (101, "sku_001", 1000.00, "2021-08-01 12:00:00"),
       (102, "sku_002", 2000.00, "2021-08-01 11:00:00"),
       (102, "sku_004", 2500.00, "2021-08-01 12:00:00"),
       (102, "sku_002", 2000.00, "2021-08-01 13:00:00"),
       (102, "sku_002", 12000.00, "2021-08-01 13:00:00"),
       (102, "sku_002", 600.00, "2021-08-02 12:00:00");

-- 查看数据,进行了预聚合
:) select * from t_order_smt;

SELECT *
FROM t_order_smt

Query id: 8cff82df-89b3-4149-8835-210266715922

┌──id─┬─sku_id──┬─total_amount─┬─────────create_time─┐
│ 102 │ sku_002 │       600.00 │ 2021-08-02 12:00:00 │
└─────┴─────────┴──────────────┴─────────────────────┘
┌──id─┬─sku_id──┬─total_amount─┬─────────create_time─┐
│ 101 │ sku_001 │      1000.00 │ 2021-08-01 12:00:00 │
│ 102 │ sku_002 │     16000.00 │ 2021-08-01 11:00:00 │
│ 102 │ sku_004 │      2500.00 │ 2021-08-01 12:00:00 │
└─────┴─────────┴──────────────┴─────────────────────┘

4 rows in set. Elapsed: 0.003 sec.

-- 继续插入
INSERT INTO t_order_smt
VALUES (101, "sku_001", 2000.00, "2021-08-01 13:00:00");

-- 查看数据
:) select * from t_order_smt;

SELECT *
FROM t_order_smt

Query id: a430dc59-953e-4a2f-b92a-7eac55156b89

┌──id─┬─sku_id──┬─total_amount─┬─────────create_time─┐
│ 101 │ sku_001 │      2000.00 │ 2021-08-01 13:00:00 │
└─────┴─────────┴──────────────┴─────────────────────┘
┌──id─┬─sku_id──┬─total_amount─┬─────────create_time─┐
│ 101 │ sku_001 │      1000.00 │ 2021-08-01 12:00:00 │
│ 102 │ sku_002 │     16000.00 │ 2021-08-01 11:00:00 │
│ 102 │ sku_004 │      2500.00 │ 2021-08-01 12:00:00 │
└─────┴─────────┴──────────────┴─────────────────────┘
┌──id─┬─sku_id──┬─total_amount─┬─────────create_time─┐
│ 102 │ sku_002 │       600.00 │ 2021-08-02 12:00:00 │
└─────┴─────────┴──────────────┴─────────────────────┘

5 rows in set. Elapsed: 0.002 sec.

-- 手动优化合并
:) optimize table t_order_smt final;

OPTIMIZE TABLE t_order_smt FINAL

Query id: 5661ed80-def3-4b50-84c3-cd6775209cee

Ok.

0 rows in set. Elapsed: 0.002 sec.

-- 再次查看数据
:) select * from t_order_smt;

SELECT *
FROM t_order_smt

Query id: 7ae5dbb5-9ac3-4135-afba-ade1596b495e

┌──id─┬─sku_id──┬─total_amount─┬─────────create_time─┐
│ 102 │ sku_002 │       600.00 │ 2021-08-02 12:00:00 │
└─────┴─────────┴──────────────┴─────────────────────┘
┌──id─┬─sku_id──┬─total_amount─┬─────────create_time─┐
│ 101 │ sku_001 │      3000.00 │ 2021-08-01 12:00:00 │
│ 102 │ sku_002 │     16000.00 │ 2021-08-01 11:00:00 │
│ 102 │ sku_004 │      2500.00 │ 2021-08-01 12:00:00 │
└─────┴─────────┴──────────────┴─────────────────────┘

4 rows in set. Elapsed: 0.002 sec.

结论:

  • 分区内进行聚合汇总,不在一个分区的数据不会被聚合;
  • 插入(新版本)或分区合并时进行聚合汇总;
  • 以 SummingMergeTree() 中指定的列作为汇总数据列,可以填写多列必须数字列;如果不填,以所有非维度列且为数字列的字段为汇总数据列;
  • 以 order by 的列为准,作为维度列;
  • 其它列按插入顺序保留第一条记录;

建议:

设计聚合表时,唯一键值、流水号可以去掉,所有字段全部是维度、度量或者时间戳。

问题:

能够直接通过以下 SQL 得到汇总值?

SELECT total_amount
FROM t_order_smt
WHERE id = 101;

答案是不可,因为可能会包含一些还没来得及聚合的临时分区。

如果要获取汇总值,还是需要使用 sum 进行聚合,这样效率会有一定的提高,但本身 ClickHouse 是列式存储的,效率提升有限,不会特别明显。

SELECT sum(total_amount)
FROM t_order_smt
WHERE id = 101;

5. SQL 操作

5.1 Insert

基本与标准 SQL(MySQL) 一致。

  • 标准

    INSERT INTO [table_name] values (...), (...);
    
  • 表到表插入

    INSERT INTO [table_name] select a, b, c from [table_name_2];
    

5.2 Update 和 Delete

ClickHouse 提供了 Update 和 Delete 的功能,这类操作被称为 Mutation 查询,它可以看作 Alter 的一种。

虽然可以实现修改和删除,但是和一般的 OLTP 数据库不一样,Mutation 语句是一种很“重”的操作,而且不支持事务。

“重”的原因主要是每次修改或者删除都会导致放弃目标数据的原有分区,重建新分区。所以尽量做批量的变更,不要进行频繁小数据的操作。

-- 删除操作
ALTER TABLE t_order_smt DELETE WHERE sku_id = "sku_001";

-- 修改操作
ALTER TABLE t_order_smt UPDATE total_amount = toDecimal32(2000.00, 2) WHERE id = 102;

由于操作比较“重”,所以 Mutation 语句分两步执行,同步执行的部分其实只是进行新增数据新增分区和把旧分区打上逻辑上的失效标记。直到触发分区合并的时候,才会删除旧数据释放磁盘空间,一般不会开放这样的功能给用户,由管理员完成。

5.3 Select

ClickHouse 查询基本与标准 SQL 差别不大。

  • 支持子查询;

  • 支持 CTE(Common Table Expression 公用表表达式 with 字句);

  • 支持 JOIN,但是 JOIN 操作无法使用缓存,所以即使是两次相同的 JOIN 语句,ClickHouse 也会是为两条新 SQL;

  • 窗口函数(实验功能);

  • 不支持自定义函数;

  • GROUP BY 操作增加了 with rollupwith cubewith totals 用来计算小计和总数;

    纬度为 a,b

    • rollup:上卷
      • group by
      • group by a
      • group by a, b
    • cube:多维分析
      • group by
      • group by a
      • group by b
      • group by a, b
    • totals:总计
      • group by
      • group by a, b
-- 清空数据
ALTER
TABLE
t_order_mt
DELETE
WHERE 1 = 1;

-- 插入数据
INSERT INTO t_order_mt
VALUES (101, "sku_001", 1000.00, "2021-10-01 12:00:00"),
       (101, "sku_002", 2000.00, "2021-10-01 12:00:00"),
       (103, "sku_004", 2500.00, "2021-10-01 12:00:00"),
       (104, "sku_002", 2000.00, "2021-10-01 12:00:00"),
       (105, "sku_003", 600.00, "2021-10-02 12:00:00"),
       (106, "sku_001", 1000.00, "2021-10-04 12:00:00"),
       (107, "sku_002", 2000.00, "2021-10-04 12:00:00"),
       (108, "sku_004", 2500.00, "2021-10-04 12:00:00"),
       (109, "sku_002", 2000.00, "2021-10-04 12:00:00"),
       (110, "sku_003", 600.00, "2021-10-01 12:00:00");
       
-- with rollup 上卷,从右至左去掉维度进行小计
:) SELECT id, sku_id, sum(total_amount)
:-] FROM t_order_mt
:-] GROUP BY id, sku_id
:-] WITH ROLLUP;

SELECT
    id,
    sku_id,
    sum(total_amount)
FROM t_order_mt
GROUP BY
    id,
    sku_id
    WITH ROLLUP

Query id: 463748fe-afc1-49d3-8cba-4f041a9f5506

┌──id─┬─sku_id──┬─sum(total_amount)─┐
│ 110 │ sku_003 │               600 │
│ 109 │ sku_002 │              2000 │
│ 107 │ sku_002 │              2000 │
│ 106 │ sku_001 │              1000 │
│ 104 │ sku_002 │              2000 │
│ 101 │ sku_002 │              2000 │
│ 103 │ sku_004 │              2500 │
│ 108 │ sku_004 │              2500 │
│ 105 │ sku_003 │               600 │
│ 101 │ sku_001 │              1000 │
└─────┴─────────┴───────────────────┘
┌──id─┬─sku_id─┬─sum(total_amount)─┐
│ 110 │        │               600 │
│ 106 │        │              1000 │
│ 105 │        │               600 │
│ 109 │        │              2000 │
│ 107 │        │              2000 │
│ 104 │        │              2000 │
│ 103 │        │              2500 │
│ 108 │        │              2500 │
│ 101 │        │              3000 │
└─────┴────────┴───────────────────┘
┌─id─┬─sku_id─┬─sum(total_amount)─┐
│  0 │        │             16200 │
└────┴────────┴───────────────────┘

20 rows in set. Elapsed: 0.006 sec.

-- with cube 从右至左去掉维度进行小计,再从左至右去掉维度进行小计
:) SELECT id, sku_id, sum(total_amount)
:-] FROM t_order_mt
:-] GROUP BY id, sku_id
:-] WITH CUBE;

SELECT
    id,
    sku_id,
    sum(total_amount)
FROM t_order_mt
GROUP BY
    id,
    sku_id
    WITH CUBE

Query id: aa14eb94-d618-4edc-9112-548d3471f28e

┌──id─┬─sku_id──┬─sum(total_amount)─┐
│ 110 │ sku_003 │               600 │
│ 109 │ sku_002 │              2000 │
│ 107 │ sku_002 │              2000 │
│ 106 │ sku_001 │              1000 │
│ 104 │ sku_002 │              2000 │
│ 101 │ sku_002 │              2000 │
│ 103 │ sku_004 │              2500 │
│ 108 │ sku_004 │              2500 │
│ 105 │ sku_003 │               600 │
│ 101 │ sku_001 │              1000 │
└─────┴─────────┴───────────────────┘
┌──id─┬─sku_id─┬─sum(total_amount)─┐
│ 110 │        │               600 │
│ 106 │        │              1000 │
│ 105 │        │               600 │
│ 109 │        │              2000 │
│ 107 │        │              2000 │
│ 104 │        │              2000 │
│ 103 │        │              2500 │
│ 108 │        │              2500 │
│ 101 │        │              3000 │
└─────┴────────┴───────────────────┘
┌─id─┬─sku_id──┬─sum(total_amount)─┐
│  0 │ sku_003 │              1200 │
│  0 │ sku_004 │              5000 │
│  0 │ sku_001 │              2000 │
│  0 │ sku_002 │              8000 │
└────┴─────────┴───────────────────┘
┌─id─┬─sku_id─┬─sum(total_amount)─┐
│  0 │        │             16200 │
└────┴────────┴───────────────────┘

24 rows in set. Elapsed: 0.006 sec.

-- with totals 总计
:) SELECT id, sku_id, sum(total_amount)
:-] FROM t_order_mt
:-] GROUP BY id, sku_id
:-] WITH TOTALS;

SELECT
    id,
    sku_id,
    sum(total_amount)
FROM t_order_mt
GROUP BY
    id,
    sku_id
    WITH TOTALS

Query id: 2d88fa13-a60d-4c66-a8ae-f5167a1a990d

┌──id─┬─sku_id──┬─sum(total_amount)─┐
│ 110 │ sku_003 │               600 │
│ 109 │ sku_002 │              2000 │
│ 107 │ sku_002 │              2000 │
│ 106 │ sku_001 │              1000 │
│ 104 │ sku_002 │              2000 │
│ 101 │ sku_002 │              2000 │
│ 103 │ sku_004 │              2500 │
│ 108 │ sku_004 │              2500 │
│ 105 │ sku_003 │               600 │
│ 101 │ sku_001 │              1000 │
└─────┴─────────┴───────────────────┘

Totals:
┌─id─┬─sku_id─┬─sum(total_amount)─┐
│  0 │        │             16200 │
└────┴────────┴───────────────────┘

10 rows in set. Elapsed: 0.004 sec.

5.4 Alter

同 MySQL 的修改字段基本一致。

  • 新增字段

    alter table tableName add column new colname String after col1;
    
  • 修改字段类型

    alter table tableName modify column newcolname String;
    
  • 删除字段

    alter table tableName drop column newcolname
    

5.5 导出数据

$ clickhouse-client --query "select * from t_order_mt where create_time="2021-10-01 12:00:00"" --format CSVWithNames > ~/rs.csv

更多支持格式参照:https://clickhouse.com/docs/en/interfaces/formats/

6. 副本

副本的作用主要是保证数据的高可用性,即使一台 ClickHouse 节点宕机,那么也可以从其它服务器获得相同的数据。

6.1 副本写入流程

clickhouse入门教程_clickhouse语法

6.2 配置步骤

  1. 启动 zookeeper 集群;

  2. 新建/etc/clickhouse-server/config.d/metrika.xml文件,添加以下内容;

    <?xml version="1.0"?>
    <yandex>
    	<zookeeper-servers>
            <node index="1">
                <host>example1</host>
                <port>2181</port>
            </node>
            <node index="2">
                <host>example2</host>
                <port>2181</port>
            </node>
            <node index="3">
                <host>example3</host>
                <port>2181</port>
            </node>
        </zookeeper-servers>
    </yandex>
    
  3. 修改配置文件所属用户和用户组为 clickhouse;

    $ sudo chown clickhouse:clickhouse /etc/clickhouse-server/config.d/metrika.xml
    
  4. 修改/etc/clickhouse-server/config.xml配置文件,增加 zookeeper 外部配置文件的引用;

    <!-- 仅新版本需要 -->
    <zookeeper incl="zookeeper-servers" optional="true"/>
    <include_from>/etc/clickhouse-server/config.d/metrika.xml</include_from>
    
  5. 同步配置到副本服务器节点;

  6. 重启 clickhouse;

    $ sudo systemctl restart clickhouse-server
    

注意:副本只能同步数据,不能同步表结构,所以需要在每台机器上手动建表。

6.3 测试

-- 节点一
CREATE TABLE t_order_rep2
(
    id           UInt32,
    sku_id       String,
    total_amount Decimal(16, 2),
    create_time  DateTime
) ENGINE = ReplicatedMergeTree("/clickhouse/table/01/t_order_rep", "rep1")
      PARTITION BY toYYYYMMDD(create_time)
      PRIMARY KEY (id)
      ORDER BY (id, sku_id);
      
-- 节点二
CREATE TABLE t_order_rep2
(
    id           UInt32,
    sku_id       String,
    total_amount Decimal(16, 2),
    create_time  DateTime
) ENGINE = ReplicatedMergeTree("/clickhouse/table/01/t_order_rep", "rep2")
      PARTITION BY toYYYYMMDD(create_time)
      PRIMARY KEY (id)
      ORDER BY (id, sku_id);

说明:ReplicatedMergeTree 参数一为 zookeeper 地址,需要保持一致;参数二为副本名称,必须不一样。

7. 分片集群

副本虽然能够提高数据的可用性,降低丢失风险,但是每台服务器实际上必须容纳全量数据,对数据的横向扩容没有解决。

要解决数据水平切分的问题,需要引入分片的概念。通过分片把一份完整的数据进行切分,不同的分片分布到不同的节点上,再通过 Distributed 表引擎把数据拼接起来一同使用。

Distributed 表引擎本身不存储数据,类似于 MyCat 之于 MySQL,成为一种中间件,通过分布式逻辑表来写入、分发、路由多台节点不同分片的分布式数据。

注意:ClickHouse 的集群是表级别的,实际企业中,大部分做了高可用,但是没有用分片,避免降低查询性能以及操作集群的复杂性。

7.1 集群写入流程

3 分片 2 副本共 6 个节点:

clickhouse入门教程_clickhouse语法

7.2 集群读取流程

3 分片 2 副本共 6 个节点:

clickhouse入门教程_clickhouse语法

原文地址:https://www.cnblogs.com/xiaoQQya/archive/2022/05/26/16313669.html

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
转载请注明出处: https://daima100.com/5191.html

(0)
上一篇 2023-05-19
下一篇 2023-05-19

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注