ClickHouse 是俄罗斯的 Yandex 于 2016 年开源的列式存储数据库(DBMS),使用 C++ 语言编写,主要用于在线分析处理查询(OLAP),能够使用 SQL 查询实时生成分析数据报告。
以下面的表为例:
| Id | Name | Age |
|---|---|---|
| 1 | 张三 | 18 |
| 2 | 李四 | 22 |
| 3 | 王五 | 34 |
采用行式存储时,数据在磁盘上的组织结构为:
| 1 | 张三 | 18 | 2 | 李四 | 22 | 3 | 王五 | 34 |
|---|
优点时向查询某个人的所有属性时,可以通过一次磁盘查找加顺序读取就可以。但是当想查所有人的年龄时,需要不停的查找,或者全表扫描才行,遍历的很多数据都是不需要的。
采用列式存储时,数据在磁盘上的组织结构为:
| 1 | 2 | 3 | 张三 | 李四 | 王五 | 18 | 22 | 34 |
|---|
这时想查询所有人的年龄只需把年龄那一列拿出来就可以了。
列式存储的优点:
几乎覆盖了标准 SQL 的大部分语法,包括 DDL 和 DML,以及配套的各种函数,用户管理及权限管理,数据的备份与恢复。
ClickHouse 和 MySQL 类似,把表级的存储引擎插件化,根据表的不同需求可以设定不同的存储引擎。目前包括合并树(Merge Tree)、日志、接口和其它四大类 20 多种引擎。
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 的写入速度。
ClickHouse 将数据划分为多个 Partition, 每个 Partition 再进一步划分为多个 Index Granularity(索引粒度),然后通过多个 CPU 核心分别处理其中的一部分来实现并行数据处理。在这种设计下,单条 Query 就能利用整机所有 CPU。极致的并行处理能力,极大的降低了查询延迟。
所以,ClickHouse 即使对于大量数据的查询也能够化整为零平行处理。但是有一个弊端就是对于单条查询使用多 CPU,就不利用同时并发多条查询。所以对于高 QPS(Queries-per-second) 的查询业务,ClickHouse 并不是强项。
结论:ClickHouse 像很多 OLAP 数据库一样,单表查询速度优于关联查询,而且 ClickHouse 的两者差距更为明显。
# 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
编辑 /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 重新登录不生效)。
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/
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
配置文件目录为/etc/clickhouse-server,目录结构如下:
编辑/etc/clickhouse-server/config.xml,取消<!-- <listen_host>::</listen_host> -->的注释。
# 启动 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
参考官方文档:https://clickhouse.tech/docs/zh/sql-reference/data-types/
固定长度的整型,包括有符号整型或无符号整型。
整型范围(-2n-1 ~ 2n-1-1):
Int8 - [-128 : 127]
Int16 - [-32768 : 32767]
Int32 - [-2147483648 - 2147483647]
Int63 - [-9223372036854775808 - 9223372036854775807]
无符号整型范围(0 ~ 2n-1):
使用场景:个数、数量、也可以存储 ID。
建议尽可能以整数形式存储数据。例如,将固定精度的数字转换为整数值,如时间用毫秒为单位表示,因为浮点型进行计算时可能引起四舍五入的误差。
:) 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.
使用场景:一般数据值比较小,不涉及大量的统计计算,精度要求不高的时候。比如保存商品的重量。
没有单独的类型来存储布尔值。可以使用 UInt8 类型,取值限制为 0 或 1。
有符号的浮点数,可在加、减和乘法运算过程中保持精度。对于除法,最低有效数字会被丢弃(不舍入)。
说明:s 标识小数位。
使用场景:一般金额、汇率、利率等字段为了保证小数点精度,都使用 Decimal 进行存储。
String
字符串可以是任意长度的,它可以包含任意的字节集,包含空字节。
FixedString(N)
固定长度 N 的字符串,N 必须是严格的正自然数。当服务端读取长度小于 N 的字符串的时候,通过在字符串末尾添加空字节来达到 N 字节长度;当服务端读取长度大于 N 的字符串的时候,将返回错误消息。
与 String 相比,极少会使用 FixedString,因为使用起来不是很方便。
使用场景:名称、文字描述、字符型编码。固定长度的可以保存一些定长的内容,比如一些编码、性别等,但是考虑到一定的变化风险,带来收益不够明显,所以定长字符串使用意义有限。
包括 Enum8 和 Enum16 类型。Enum 保存 ‘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;
使用场景:对一些状态、类型等字段算是一种空间优化,也算是一种数据约束。但是实际使用中往往会因为一些数据内容的变化增加一定的维护成本,甚至是数据丢失问题,所以谨慎使用。
日期类型,用两个字节存储,表示从 1970-01-01(无符号)到当前的日期值。
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.
表引擎是 ClickHouse 的一大特色。可以说,表引擎决定了如何存储表的数据。包括:
表引擎的使用方式就是必须显式的在创建表时定义该表使用的引擎,以及引擎使用的相关参数。
注意:引擎的名称大小写敏感。
以列文件的形式保存在磁盘上,不支持索引,没有并发控制。一般保存少量数据的小表,
生产环境上作用有限。可用于平时练习测试使用。
CREATE TABLE t_tinylog
(
id String,
name String
) ENGINE = TinyLog;
内存引擎,数据以未压缩的原始形式直接保存在内存当中,服务器重启数据就会消失。读写操作不会相互阻塞,不支持索引。简单查询下有非常非常高的性能表现(超过 10G/S)。
一般用的地方不多,除了用来测试,就是在需要非常高性能,同时数据量又不太大(上限大概 1 亿行)的场景。
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 的很多概念。
作用:降低扫描的范围,优化查询速度。
不分区:只会使用一个分区。
分区目录: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.
ClickHouse 中的主键,和其它数据库不太一样,它只提供了一级索引,但是却不是唯一约束。这就意味着是可以存在相同 primary key 的数据的。
主键的设定主要依据是查询语句中的 where 条件。
根据条件通过对主键进行某种形式的二分查找,能够定位到对应的 index granularity,避免了全表扫描。
index_granularity:直译为索引粒度,指在稀疏索引中两个相邻索引对应数据的间隔。ClickHouse 中的 MergeTree 引擎默认是 8192。官方不建议修改该值,除非该列存在大量重复值,比如在一个分区中几万行才有一个不同数据。
稀疏索引:按指定间隔记录索引值,优点是可以用很少的索引数据,定位更多的数据,代价就是只能定位到索引粒度的第一行,然后再进行行扫描。
order by 设定了分区内的数据按照哪些字段进行有序保存。
order by 是 MergeTree 中唯一一个必填项,甚至比 primary key 还重要,因为当用户不设置主键的时候,很多处理会依照 order by 的字段进行处理(比如去重和汇总)。
要求:主键必须是 order by 字段的前缀字段。比如 order by 字段是 (id, sku_id),那么主键必须是 id 或者 (id, sku_id)。
目前 ClickHouse 的官网上二级索引的功能在 v20.1.2.4 之前是被标注为实验性的,在该版本之后默认是开启的。
老版本使用二级索引前需要增加设置
是否允许使用实验性的二级索引(v20.1.2.4 开始,该参数已经被删除,默认开启)
set allow_experimental_data_skipping_indices=1
创建测试表
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是设定二级索引对于一级索引粒度的粒度。
插入数据
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');
测试效果
$ 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
TTL 即 Time To Live,MergeTree 提供了可以管理数据或者列的生命周期的功能。
-- 建立表,设置 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.
修改表,设置数据在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 。
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() 填入的参数为版本字段,重复数据保留版本字段值最大的;如果不填版本字段,默认按照插入顺序保留最后一条。
结论:
对于不查询明细,只关心以维度进行汇总聚合结果的场景。如果只使用普通的 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.
结论:
建议:
设计聚合表时,唯一键值、流水号可以去掉,所有字段全部是维度、度量或者时间戳。
问题:
能够直接通过以下 SQL 得到汇总值?
SELECT total_amount
FROM t_order_smt
WHERE id = 101;
答案是不可,因为可能会包含一些还没来得及聚合的临时分区。
如果要获取汇总值,还是需要使用 sum 进行聚合,这样效率会有一定的提高,但本身 ClickHouse 是列式存储的,效率提升有限,不会特别明显。
SELECT sum(total_amount)
FROM t_order_smt
WHERE id = 101;
基本与标准 SQL(MySQL) 一致。
标准
INSERT INTO [table_name] values (...), (...);
表到表插入
INSERT INTO [table_name] select a, b, c from [table_name_2];
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 语句分两步执行,同步执行的部分其实只是进行新增数据新增分区和把旧分区打上逻辑上的失效标记。直到触发分区合并的时候,才会删除旧数据释放磁盘空间,一般不会开放这样的功能给用户,由管理员完成。
ClickHouse 查询基本与标准 SQL 差别不大。
支持子查询;
支持 CTE(Common Table Expression 公用表表达式 with 字句);
支持 JOIN,但是 JOIN 操作无法使用缓存,所以即使是两次相同的 JOIN 语句,ClickHouse 也会是为两条新 SQL;
窗口函数(实验功能);
不支持自定义函数;
GROUP BY 操作增加了 with rollup\with cube\with totals 用来计算小计和总数;
纬度为 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.
同 MySQL 的修改字段基本一致。
新增字段
alter table tableName add column new colname String after col1;
修改字段类型
alter table tableName modify column newcolname String;
删除字段
alter table tableName drop column newcolname
$ clickhouse-client --query "select * from t_order_mt where create_time='2021-10-01 12:00:00'" --format CSVWithNames > ~/rs.csv
副本的作用主要是保证数据的高可用性,即使一台 ClickHouse 节点宕机,那么也可以从其它服务器获得相同的数据。

启动 zookeeper 集群;
新建/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>
修改配置文件所属用户和用户组为 clickhouse;
$ sudo chown clickhouse:clickhouse /etc/clickhouse-server/config.d/metrika.xml
修改/etc/clickhouse-server/config.xml配置文件,增加 zookeeper 外部配置文件的引用;
<!-- 仅新版本需要 -->
<zookeeper incl="zookeeper-servers" optional="true"/>
<include_from>/etc/clickhouse-server/config.d/metrika.xml</include_from>
同步配置到副本服务器节点;
重启 clickhouse;
$ sudo systemctl restart clickhouse-server
注意:副本只能同步数据,不能同步表结构,所以需要在每台机器上手动建表。
-- 节点一
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 地址,需要保持一致;参数二为副本名称,必须不一样。
副本虽然能够提高数据的可用性,降低丢失风险,但是每台服务器实际上必须容纳全量数据,对数据的横向扩容没有解决。
要解决数据水平切分的问题,需要引入分片的概念。通过分片把一份完整的数据进行切分,不同的分片分布到不同的节点上,再通过 Distributed 表引擎把数据拼接起来一同使用。
Distributed 表引擎本身不存储数据,类似于 MyCat 之于 MySQL,成为一种中间件,通过分布式逻辑表来写入、分发、路由多台节点不同分片的分布式数据。
注意:ClickHouse 的集群是表级别的,实际企业中,大部分做了高可用,但是没有用分片,避免降低查询性能以及操作集群的复杂性。
3 分片 2 副本共 6 个节点:

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

目录前言滤波电路科普主要分类实际情况单位的概念常用评价参数函数型滤波器简单分析滤波电路构成低通滤波器RC低通滤波器RL低通滤波器高通滤波器RC高通滤波器RL高通滤波器部分摘自《LC滤波器设计与制作》,侵权删。前言最近需要学习放大电路和滤波电路,但是由于只在之前做音乐频谱分析仪的时候简单了解过一点点运放,所以也是相当从零开始学习了。滤波电路科普主要分类滤波器:主要是从不同频率的成分中提取出特定频率的信号。有源滤波器:由RC元件与运算放大器组成的滤波器。可滤除某一次或多次谐波,最普通易于采用的无源滤波器结构是将电感与电容串联,可对主要次谐波(3、5、7)构成低阻抗旁路。无源滤波器:无源滤波器,又称
@作者:SYFStrive @博客首页:HomePage📜:微信小程序📌:个人社区(欢迎大佬们加入)👉:社区链接🔗📌:觉得文章不错可以点点关注👉:专栏连接🔗💃:感谢支持,学累了可以先看小段由小胖给大家带来的街舞👉微信小程序(🔥)目录自定义组件-behaviors 1、什么是behaviors 2、behaviors的工作方式 3、创建behavior 4、导入并使用behavior 5、behavior中所有可用的节点 6、同名字段的覆盖和组合规则总结最后自定义组件-behaviors 1、什么是behaviorsbehaviors是小程序中,用于实现
遍历文件夹我们通常是使用递归进行操作,这种方式比较简单,也比较容易理解。本文为大家介绍另一种不使用递归的方式,由于没有使用递归,只用到了循环和集合,所以效率更高一些!一、使用递归遍历文件夹整体思路1、使用File封装初始目录,2、打印这个目录3、获取这个目录下所有的子文件和子目录的数组。4、遍历这个数组,取出每个File对象4-1、如果File是否是一个文件,打印4-2、否则就是一个目录,递归调用代码实现publicclassSearchFile{publicstaticvoidmain(String[]args){//初始目录Filedir=newFile("d:/Dev");Datebeg
ES一、简介1、ElasticStackES技术栈:ElasticSearch:存数据+搜索;QL;Kibana:Web可视化平台,分析。LogStash:日志收集,Log4j:产生日志;log.info(xxx)。。。。使用场景:metrics:指标监控…2、基本概念Index(索引)动词:保存(插入)名词:类似MySQL数据库,给数据Type(类型)已废弃,以前类似MySQL的表现在用索引对数据分类Document(文档)真正要保存的一个JSON数据{name:"tcx"}二、入门实战{"name":"DESKTOP-1TSVGKG","cluster_name":"elasticsear
文章目录1.任务背景2.任务目标3.相关知识点4.任务实操4.1安装配置JDK4.2启动FISCOBCOS4.3下载解压WeBASE-Front4.4拷贝sdk证书文件4.5启动节点4.6访问节点4.7检查运行状态5.任务总结1.任务背景FISCOBCOS其实是有控制台管理工具,用来对区块链系统进行各种管理操作。但是对于初学者来说,还是可视化界面更友好,本节就来介绍WeBASE管理平台,这是一款微众银行开源的自研区块链中间件平台,可以降低区块链使用的门槛,大幅提高区块链应用的开发效率。微众银行是腾讯牵头设立的民营银行,在国内民营银行里还是比较出名的。微众银行参与FISCOBCOS生态建设,一定
TCL脚本语言简介•TCL(ToolCommandLanguage)是一种解释执行的脚本语言(ScriptingLanguage),它提供了通用的编程能力:支持变量、过程和控制结构;同时TCL还拥有一个功能强大的固有的核心命令集。TCL经常被用于快速原型开发,脚本编程,GUI和测试等方面。•实际上包含了两个部分:一个语言和一个库。首先,Tcl是一种简单的脚本语言,主要使用于发布命令给一些互交程序如文本编辑器、调试器和shell。由于TCL的解释器是用C\C++语言的过程库实现的,因此在某种意义上我们又可以把TCL看作C库,这个库中有丰富的用于扩展TCL命令的C\C++过程和函数,所以,Tcl是
文章目录一、项目场景二、基本模块原理与调试方法分析——信源部分:三、信号处理部分和显示部分:四、基本的通信链路搭建:四、特殊模块:interpretedMATLABfunction:五、总结和坑点提醒一、项目场景 最近一个任务是使用simulink搭建一个MIMO串扰消除的链路,并用实际收到的数据进行测试,在搭建的过程中也遇到了不少的问题(当然这比vivado里面的debug好不知道多少倍)。准备趁着这个机会,先以一个很基本的通信链路对simulink基础和相关的debug方法进行总结。 在本篇中,主要记录simulink的基本原理和基本的SISO通信传输链路(QPSK方式),计划在下篇记
目录一、ESP32简单介绍二、ESP32Wi-Fi模块介绍三、ESP32Wi-Fi编程模型四、ESP32Wi-Fi事件处理流程 五、ESP32Wi-Fi开发环境六、ESP32Wi-Fi具体代码七、ESP32Wi-Fi代码解读6.1主程序app_main7.2自定义代码wifi_init_sta()八、ESP32Wi-Fi连接验证8.1测试方法8.2服务器模拟工具sscom58.3测试代码8.4测试结果前言为了开发一款亚马逊物联网产品,开始入手ESP32模块。为了能够记录自己的学习过程,特记录如下操作过程。一、ESP32简单介绍ESP32是一套Wi-Fi(2.4GHz)和蓝牙(4.2)双模解决方
按照目前的情况,这个问题不适合我们的问答形式。我们希望答案得到事实、引用或专业知识的支持,但这个问题可能会引发辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visitthehelpcenter指导。关闭10年前。我使用PHP的时间太长了,对它感到厌倦了。我也想学习一门新语言。我一直在使用Ruby并且喜欢它。我必须在Rails和Sinatra之间做出选择,那么您会推荐哪一个?Sinatra真的不能用来构建复杂的应用程序,它只能用于简单的应用程序吗?
📝学技术、更要掌握学习的方法,一起学习,让进步发生👩🏻作者:一只IT攻城狮。💐学习建议:1、养成习惯,学习java的任何一个技术,都可以先去官网先看看,更准确、更专业。💐学习建议:2、然后记住每个技术最关键的特性(通常一句话或者几个字),从主线入手,由浅入深学习。❤️《SpringCloud入门实战系列》解锁SpringCloud主流组件入门应用及关键特性。带你了解SpringCloud主流组件,是如何一战解决微服务诸多难题的。项目demo:源码地址👉🏻SpringCloud入门实战系列不迷路👈🏻:SpringCloud入门实战(一)什么是SpringCloud?SpringCloud入门实战