数据分析ClickHouse学习笔记

一、ClickHouse基础

1.1 ClickHouse介绍

ClickHouse是一个用于联机分析(OLAP)的列式DBMS。

简单来说,相比MySQL等行式数据库,数据存储方式是:

Rowidis_deltitlesexcreateAt
#021a12024/2/18 5:19
#130b12024/2/18 8:10
#241c12024/2/18 7:38
#N

即是:处于同一行中的数据总是被物理的存储在一起。

在列式数据库系统中,数据按如下的顺序存储:

Row:#0#1#2#N
id234
is_del101
titleabc
sex111
createAt2024/2/18 5:192024/2/18 8:102024/2/18 7:38

即是:来自不同列的值被单独存储,来自同一列的数据被存储在一起。

这种数据存储方式适用不同的业务场景,数据访问的场景包括:

1.进行了何种查询、多久查询一次以及各类查询的比例;

2.每种类型的查询(行、列和字节)读取多少数据;

3.读取数据和更新之间的关系;

4.使用的数据集大小以及如何使用本地的数据集;

5.是否使用事务,以及它们是如何进行隔离的;

6.数据的复制机制与数据的完整性要求;

7.每种类型的查询要求的延迟与吞吐量

做个例子,以下表数据为例:

idnameage
1张三12
2李四13
3王五14

行式存储,数据在磁盘的组织结构:

在这里插入图片描述

列式存储,数据在磁盘的组织结构:

在这里插入图片描述

行式存储应用场景在查询某个人的所有属性,只需根据索引一次磁盘查找加顺序读取即可。但是当列变多,每次只查询一列或者某几列(统计所有人年龄平均值),如果还是行式存储数据,需要全表扫描,遍历很多数据都是不需要的。而列式存储想查询所有人年龄,只需把年龄一列拿出即可

1.2 OLAP和OLTP

OLTP(On-Line Transaction Processing):联机事务处理,典型代表是关系型数据库(如MySQL),它的数据存储在服务器本地的文件里

OLAP(On-Line Analytical Processing):联机分析处理,OLAP型数据库的典型代表是分布式文件系统(hive),它的数据存储在HDFS集群里

OLTPOLAP
业务目的处理业务,如订单,进销存业务支持决策
面向对象业务处理人分析决策人
主要工作负载增删改查询
主要衡量指标事务吞吐量查询响应速度(QPS)
数据库设计3NF/BCNF星型/雪花模型
1.3 OLAP场景的关键特征
  • 绝大多数是读请求
  • 数据以相当大的批次(> 1000行)更新,而不是单行更新;或者根本没有更新。
  • 已添加到数据库的数据不能修改。
  • 对于读取,从数据库中提取相当多的行,但只提取列的一小部分。
  • 宽表,即每个表包含着大量的列
  • 查询相对较少(通常每台服务器每秒查询数百次或更少)
  • 对于简单查询,允许延迟大约50毫秒
  • 列中的数据相对较小:数字和短字符串(例如,每个URL 60个字节)
  • 处理单个查询时需要高吞吐量(每台服务器每秒可达数十亿行)
  • 事务不是必须的
  • 对数据一致性要求低
  • 每个查询有一个大表。除了他以外,其他的都很小。
  • 查询结果明显小于源数据。换句话说,数据经过过滤或聚合,因此结果适合于单个服务器的RAM中

可以看出,OLAP场景与其他通常业务场景(例如,OLTP或K/V)有很大的不同, 因此想要使用OLTP或Key-Value数据库去高效的处理分析查询场景,并不是非常完美的适用方案。

1.4 列式数据库更适合OLAP场景的原因
输入/输出
  1. 针对分析类查询,通常只需要读取表的一小部分列。在列式数据库中你可以只读取你需要的数据。例如,如果只需要读取100列中的5列,这将帮助你最少减少20倍的I/O消耗。
  2. 由于数据总是打包成批量读取的,所以压缩是非常容易的。同时数据按列分别存储这也更容易压缩。这进一步降低了I/O的体积。
  3. 由于I/O的降低,可以让更多的数据被系统缓存。

例如,查询«统计每个广告平台的记录数量»需要读取«广告平台ID»这一列,它在未压缩的情况下需要1个字节进行存储。如果大部分流量不是来自广告平台,那么这一列至少可以以十倍的压缩率被压缩。当采用快速压缩算法,它的解压速度最少在十亿字节(未压缩数据)每秒。换句话说,这个查询可以在单个服务器上以每秒大约几十亿行的速度进行处理。这实际上是当前实现的速度。

CPU

由于执行一个查询需要处理大量的行,因此在整个向量上执行所有操作将比在每一行上执行所有操作更加高效,减少调用成本的查询引擎。

数据压缩

能存储大量数据的数据库都是采用列式存储的,它压缩比极高,如果压缩比低,是无法存储海量数据的,数据库的存储格式决定了存储的体积。

行式存储每一行的数据格式不同,有数字,文本,日期型等,很大的问题就是,不同的格式无法采用统一的压缩格式,而列式存储一列是一个数据块,是同一个数据格式,很好的弥补了压缩比低的缺点。

压缩比高好处是同样规模的数据,占用的空间小,可以节省磁盘,更重要的是可以节省内存,对于内存小的可以将更多的数据放到其中,如对于128G内存能够管理的列式存储的数据远远大于行式存储的数据,这样管理行式存储的数据需要分布式,代理中间件等,十分复杂,列式存储可能一台机器就足够了,节省资源。

列式储存的好处:

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

列式存储的劣势:

1.不支持事务。
2.对行级别的查询不方便,列式存储一般负责范围的查询。

1.5 读与写
写(MergeTree)

ClickHouse采用类LSM Tree的结构(HBase也使用的这种结构),数据写入后定期在后台合并。通过类LSM tree的结构,但没有内存表,没有预写日志,ClickHouse在数据导入时全部是顺序append写入磁盘,在后台周期性合并数据到主数据段。顺序写的特性,充分利用了磁盘的吞吐能力,即便在HDD上也有着优异的写入性能。

尤其是当在批量插入的场景下,MySQL是真正的将数据一条一条的写入到数据库中,磁盘一致地在移动,而ClickHouse会将大量的数据直接放到临时分区中,然后ClickHouse会自动调用进程去后台处理,compaction 时也是多个段 merge sort 后顺序写回磁盘。

读(MergeTree)

(1)稀疏索引
索引:用于查询,通过索引可以查找到数据的具体位置,关系型数据库中默认没有索引,但一般来说每张表都需要加一个主键,主键就是索引,在hive中,分区也相当于一个索引,这两种索引的不同在于粒度。

MySQL一般使用稠密索引,即尽可能的让每一条数据都有一个索引,对应公交车的站牌,可以精确定位到一个地点。

clickhouse使用稀疏索引,索引之间的颗粒度(默认8192行),对应火车站的车站,可以定位到一个范围,想找到具体的数据,还需要在这个范围内进行逐表扫描。

在这里插入图片描述

稀疏索引的好处是可以用很少的索引数据,定位更多的数据,范围查询过滤比较快,代价是只能定位到索引粒度第一行,然后再逐个扫描数据,不适合做点对点查询。对于稠密和稀疏两种设计思路没有好坏,只有取舍,而语句级多线程就是针对稀疏索引的缺点做的一些优化。

(2)语句级多线程
MySQL是多线程,但不是语句级的,其处理任意一条SQL,只使用一个线程,MySQL可以执行多条SQL,所以多线程这个功能也不会浪费,对多条SQL开多个线程,不同SQL之间互相独立。

ClickHouse不同,它会针对一条SQL启动多个线程共同完成,如上图中,会在10101 - 22222,32343 - 76543,76766 - 98345会启动三个线程同时进行查询,在ClickHouse中每一个间隔都称为颗粒,默认为8192行,即一个线程最多扫描8192行。

ClickHouse将数据划分为多个partition,每个partition再进一步划分为多个index granularity(索引颗粒),然后通过多个CPU核心分别处理其中的一部分来实现并行数据处理。

在这种设计下,单条Query就能利用整机所有CPU。极致的并行处理能力,极大的降低了查询延时。

MySQL一般接受简单、数据量小的查询(业务系统),导致QPS相对较高,每个查询的资源较少,只有一个线程对一个查询,但是可以支持更多查询。

ClickHouse适合复杂数据量大的查询(分析系统),QPS不高,每个查询投入的资源更多,多个线程对一个查询,但是支持的查询更少。

弊端:clickhouse即使对于大量数据的查询也能够化整为零平行处理。但是有一个弊端就是对于单条查询使用多cpu,就不利于同时并发多条查询。所以对于高qps的查询业务,clickhouse并不是强项。

1.6多样化引擎

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

Clickhouse 中最强大的表引擎是 MergeTree (合并树)引擎及该系列(MergeTree)中的其他引擎。 MergeTree 系列的引擎被设计用于插入极大量的数据到一张表当中。数据可以以数据片段的形式一个接着一个的快速写入,数据片段在后台按照一定的规则进行合并。相比在插入时不断修改(重写)已存储的数据,这种策略会高效很多。

合并树系列:MergeTree、ReplacingMergeTree(分区内主键去重)、CollapsingMergeTree(标记列 Sign)、VersionedCollapsingMergeTree(快速写入不断变化的对象状态)、SummingMergeTree(对主键列进行预先聚合)、AggregatingMergeTree(高级预先聚合)

对于MergeTree的一些理解:

1.存储的数据按主键排序。 这使得能够创建一个小型的稀疏索引来加快数据检索。
2.如果指定分区键的话,可以使用分区。在相同数据集和相同结果集的情况下ClickHouse中某些带分区的操作会比普通操  作更快。查询中指定分区键时ClickHouse会自动截取分区数据,有效增加查询性能。
3.支持数据副本。 ReplicatedMergeTree系列的表提供了数据副本功能
4.支持数据采样。需要时可以给表设置一个采样方法。

–MergeTree建表语句

CREATE TABLE [IF NOT EXISTS] [db_name.]table_name (
name1 [type] [DEFAULT|MATERIALIZED|ALIAS expr],
name2 [type] [DEFAUErEMAMLERLALLIZED|ALIAS expr],
省略...
) ENGINE = MergeTree()
[PARTITION BY expr]
[ORDER BY expr]
[PRIMARY KEY expr]
[SAMPLE BY expr]
[SETTINGS name=value, 省略...]

order by 设定分区内的数据按照哪些字段顺序进行有序保存。 order by 是 MergeTree 中唯一一个必填项,甚至比 primary key 还重要,因为当不设置主键的情况,很多处理会依照 order by 的字段进行处理(比如去重和汇总)。 要求:主键必须是 order by 字段的前缀字段。 比如: order by 字段是 (id, u_id) ,那主键必须是 id 或者(id, u_id)

1.7 基于ClickHouse构建标签画像示例

1.标签表结构演进

表结构:

tag_iduser_id
tagnum1101
tagnum1102
tagnum2101
tagnum3101
#####DDL
CREATE TABLE ......ORDER BY (tag_id,user_id)
engine=MergeTree()

###用户圈选,交集
SELECT user_id FROM xxx WHERE tag_id=1 AND
user_id IN (select user_id from xxx
where tag_id =2
)
where tagID= 1

###用户画像,基于人群进行画像分析
SELECT tag_id ,count(1) FROM xxx AND
uid IN (select user_id from xxx
where tag_id =2
)and tag_id in (4,5,6,7)
GROUP BY tag_id

​ 优势:

  • 表结构简单,标签扩展简单

  • 同一个uid 数据只在shard,增加并行查询效率

    劣势:

  • 标签和用户量增加,数据行数膨胀迅速,查询效率衰减明显

  • 多条件嵌套查询SQL 复杂

2.标签表结构演进

user_idtag1tag2tagN
101F1020
101M2030
102M1030
#####DDL:
CREATE TABLE( ......
user_id UInt64,
tag1 SimpleAggregateFunction(anyLast, Nullable(Enum(‘女’ = F, '男’ = M)
.........
)
ORDER BY uid ENGINE= AggregatingMergeTree()

###用户圈选,交集
SELECT uid FROM table_users WHERE anyLastMerge(tag1)=‘F ’ AND
anyLast(tag2)= 10

### 用户标签详情
SELECT uid FROM table_users WHERE anyLastMerge(tagID)=‘F ’
AND anylastMerge(tag2)= 10

###用户画像,基于已有人群进行用户画像分析
SELECT tag1 ,count(1) FROM table_users AND
user_id IN (select user from xxx
where tag2 =10
)GROUP BY tag

优缺点:

  • 数据行数减少,SQL 语句简单
  • SimpleAggregateFunction (anylast,…)支持标签值更新
  • 交,并,差,SQL表达复杂(在线分析效率低)

3.标签表结构演进

tagtag_valueuser_ids(bitmap)
tag1F(101,102,103,)
tag1M(104,105,)
tag320(101,)

在这里插入图片描述

数据存储与同步

###离线任务计算标签结果:
create table label_local(
tag String ,
tag_value String ,
user_id UInt64
)
ENGINE = MerTree()
partition by tag
order by tag,tag_value

### bitmap标签表
create table label_bitmap_local
(
tag String comment '标签名称',
tag_value String comment '标签值',
user_ids AggregatingMergeTree()
)
partition by tag
order by (tag,tag_value)
settings index_granularity = 128;

###数据导入
insert into table  label_bitmap_local
select 
tag,
tag_value,
groupBitmapState(user_id)
from label_local
group by tag,tag_value

使用bitmap做人群(群体)圈选

with
(
    select  groupbitmapMergeState(user_ids)  //获取满足条件数据包
    from label_bitmap_local
    where tag='性别' and tag_value = '女'
)as user_group_1,
(
    select  groupbitmapMergeState(user_ids) 
    from label_bitmap_local
    where tag='年龄段' and tag_value = '18-30'
) as user_group_2 
select  bitmapToArray(bitmapAnd(user_group_1,user_group_2)) //求交集,转数组
### Result = (性别=女生) ∩(年龄段=18-30)= 年轻女性

群体分析

###人群预估,群体基数
select bitmapCardinality(bitmapAnd(user_group_1,user_group_2)) 

###查询个人是否存在群体中
select bitmapContains(bitmapAnd(user_group_1,user_group_2),user_id)

### 查询群体是否存在公共元素
select bitmapHasAny(user_group_1,user_group_2) 

### 群体是否包含另一群体所有
select bitmapHasAll(user_group_1,user_group_2)

###圈定群体(年轻女性)其他情况分布(消费水平)
with
(
select bitmapAnd(user_group_1,user_group_2) 
) as filter_users

select  bitmapCardinality(bitmapAnd(filter_users, group_by_users)) AS count,    
tag,  
tag_value 
from (
        SELECT    groupBitmapMergeState(user_ids) AS group_by_users,    tag,    tag_value
        FROM lable_bitmap_local
        WHERE tag = "消费水平"
        GROUP BY (tag, tag_value)
        );
  //年轻女性群体消费能力分布
1.8 缺点、局限

不支持事务,不支持真正的删除/更新;

数据一致性差,或者只能保证最终数据一致性

适用宽边多维度分析查询,多表join性能差 可以使用物化视图进行优化

不支持高并发,官方建议qps为100 Clickhouse快是由于采用了并行处理机制,即便一个查询,也会用服务器一半的CPU去执行,因此ClickHouse不能支持高并发的使用场景,默认单查询使用CPU核数为服务器核数的一半

1.9优化

1.建议每次写入多于1000行的批量写入,或每秒不超过一个写入请求

2.同一个分区的 两个part的数据量都很大,然后后台自动的merge合并成一个的时候,会消耗大量的cpu 控制merge的线程,后台默认的线程数是16 降低线程数的配置; 实时的数据尽量在ssd上.

3.选择更加合适的引擎

4.建表优化:尽量不使用 Nullable 类型,避免数据出现大量空值字段,空值会被单独存储,字段可以设置默认值;

5.分区粒度:分区粒度根据业务场景特性来设置,不宜过粗也不宜过细。我们的数据一般都是按照时间来严格划分,所以都是按天、按月来划分分区。如果索引粒度过细按分钟、按小时等划分会产生大量的分区目录,更不能直接 PARTITION BY create_time ,会导致分区数量惊人的多,几乎每条数据都有一个分区会严重的影响性能。

1.9.1慢查询优化

ClickHouse 在 20.6 版本后已经提供查看查询计划的原生 EXPLAIN,但是提供的信息对慢 SQL 优化提供的帮助不是很大,推荐使用查看服务日志这种方式进行分析,这种方式需要使用 clickhouse-client 进行执行 SQL 语句。

举个栗子,通过query_log_all 定位到一个慢 SQL:

select ifNull(sum(price), 0) as total_price
from wms.wms_order_sku_local final    
prewhere tranTime = '2024-02-27 08:00:00' and orderType = '10'
where is_del = '0'  and is_valid = '0'

使用 clickhouse-client,send_logs_level 参数指定日志级别为 trace。

clickhouse-client -h 地址 --port 端口 --user 用户名 --password 密码 --send_logs_level=trace

在 client 中执行上述慢 SQL,服务端打印核心日志如下:

 1.(SelectExecutor): Key condition: unknown              没有使用主键索引:导致全表扫描
 
 2.(SelectExecutor): MinMax index condition: unknown     没有使用分区索引:导致全表扫描
 
 3.MemoryTracker: Peak memory usage (for query): 60.37 MiB.   查询语句消耗的内存最大为 60.37MB
 
 4.(SelectExecutor): Selected 36 parts by date, 9390 marks by primary key   共扫描 36 个 parts,9390 个 MarkRange,查询system.parts系统分区信息表发现当前表共拥有36个活跃分区,相当于全表扫描。

所以需要再查询条件上添加主键字段或者分区索引来进行优化。比如将tranTime为分区键,在添加这个条件后可以看到没有使用主键索引,但使用分区索引,扫描分片数为 6,MarkRange 186,共扫描 1409001 行数据,使用内存 40.76MB,扫描数据大小等大幅度降低节省大量服务器资源,并且提升了查询速度,0.267s 降低到 0.18s。

1.9.2条件聚合函数降低扫描数据行数

假设要统计某天的” 入库件量”,” 有效出库单量”,” 复核件量”三个指标。

-- 入库件量:select sum(qty) from table_1 final prewhere type = 'inbound' and dt = '2024-01-01';
-- 有效出库单量:select count(distinct orderNo) final from table_1 prewhere type = 'outbound' and dt = '2024-01-01' where and status = '1' ;
-- 复核件量:select sum(qty) from table_1 final prewhere type = 'check' and dt = '2024-01-01';

查询三个指标需要上述三个 SQL 语句查询 table_1 来完成,但dt 是一致的,区别在于 type 和 status 两个条件。假设 dt = ‘2024-01-1’ 每次查询需要扫描 100W 行数据,那么一次接口请求将会扫描 300W 行数据。通过条件聚合函数优化后将三次查询改成一次,那扫描行数将降低为 100W 行,可极大的节省集群计算资源。

select sumIf(qty, type = 'inbound'), 
-- 入库件量countIf(distinct orderNo, type = 'outbound' and status = '1'),
-- 有效出库单量sumIf(qty, type = 'check') 
-- 复核件量prewhere dt = '2024-01-01';

条件聚合函数比较灵活,可根据业务情况自由发挥,核心是减少整体的扫描量,来提升查询性能。

1.9.3二级索引

MergeTree 系列的表引擎可以指定跳数索引。
跳数索引是指数据片段按照粒度 (建表时指定的 index_granularity) 分割成小块后,将 granularity_value 数量的小块组合成一个大的块,对这些大块写入索引信息,这样有助于使用 where 筛选时跳过大量不必要的数据,减少 SELECT 需要读取的数据量。

CREATE TABLE table_name(u64 UInt64,
i32 Int32,    
s String,    ...    
INDEX a (u64 * i32, s) 
TYPE minmax GRANULARITY 3,    
INDEX b (u64 * length(s)) 
TYPE set(1000) GRANULARITY 4) ENGINE = MergeTree()...

上例中的索引能让 ClickHouse 执行下面这些查询时减少读取数据量。

SELECT count() FROM table WHERE s < 'z' SELECT count() FROM table WHERE u64 * i32 == 10 AND u64 * length(s) >= 1234

支持的索引类型:

1.minmax:以 index granularity 为单位,存储指定表达式计算后的 min、max 值;在等值和范围查询中能够帮助快速跳过不满足要求的块,减少 IO。

2.set (max_rows):以 index granularity 为单位,存储指定表达式的 distinct value 集合,用于快速判断等值查询是否命中该块,减少 IO。

3.ngrambf_v1 (n, size_of_bloom_filter_in_bytes, number_of_hash_functions, random_seed):将 string 进行 ngram 分词后,构建 bloom filter,能够优化等值、like、in 等查询条件。

4.tokenbf_v1 (size_of_bloom_filter_in_bytes, number_of_hash_functions, random_seed): 与 ngrambf_v1 类似,区别是不使用 ngram 进行分词,而是通过标点符号进行词语分割。

5.bloom_filter ([false_positive]):对指定列构建 bloom filter,用于加速等值、like、in 等查询条件的执行。

创建二级索引:

Alter table wms.wms_order_sku_local 
ON cluster default 
ADD INDEX belongProvinceCode_idx belongProvinceCode 
TYPE set(0) GRANULARITY 5;

Alter table wms.wms_order_sku_local 
ON cluster default 
ADD INDEX productionEndTime_idx productionEndTime 
TYPE minmax GRANULARITY 5;

重建分区索引数据:在创建二级索引前插入的数据,不能走二级索引,需要重建每个分区的索引数据后才能生效

-- 拼接出所有数据分区的MATERIALIZE语句
select concat('alter table wms.wms_order_sku_local on cluster default ', 'MATERIALIZE INDEX productionEndTime_idx in PARTITION '||partition_id||',')from system.partswhere database = 'wms' and table = 'wms_order_sku_local'group by partition_id
-- 执行上述SQL查询出的所有MATERIALIZE语句进行重建分区索引数据
1.9.4final 替换 argMax 进行去重

对比下 final 和 argMax 两种方式的性能差距,如下 SQL

-- final方式:
select count(distinct groupOrderCode), sum(arriveNum), count(distinct sku) from tms.group_order final prewhere siteCode = 'WG0001544' and createTime >= '2024-02-24 22:00:00' and createTime <= '2024-02-27 22:00:00' where arriveNum > 0 and test <> '1'

-- argMax方式:
select count(distinct groupOrderCode), sum(arriveNumTemp), count(distinct sku) from (select argMax(groupOrderCode,version) as groupOrderCode, argMax(arriveNum,version) as arriveNumTemp, argMax(sku,version) as sku from tms.group_order prewhere siteCode = 'WG0001544' and createTime >= '2024-02-24 22:00:00' and createTime <= '2024-02-27 22:00:00' where arriveNum > 0 and test <> '1' group by docId)

在这里插入图片描述

final 方式的 TP99 明显要比 argMax 方式优秀很多

1.9.5prewhere 替代 where

ClickHouse 的语法支持额外的 prewhere 过滤条件,它会先于 where 条件进行判断,可以看做是更高效率的 where,作用都是过滤数据。当在 sql 的 filter 条件中加上 prewhere 过滤条件时,存储扫描会分两阶段进行,先读取 prewhere 表达式中依赖的列值存储块,检查是否有记录满足条件,再把满足条件的其他列读出来。以下述的 SQL 为例,其中 prewhere 方式会优先扫描 type,dt 字段,将符合条件的列取出来,当没有任何记录满足条件时,其他列的数据就可以跳过不读了。相当于在 Mark Range的基础上进一步缩小扫描范围。prewhere 相比 where 而言,处理的数据量会更少,性能会更高。

-- 常规方式:
select count(distinct orderNo) final from table_1 where type = 'outbound' and status = '1' and dt = '2024-01-01';

-- prewhere方式:
select count(distinct orderNo) final from table_1 prewhere type = 'outbound' and dt = '2024-01-01' where and status = '1' ;

PS小坑:prewhere 会优先于 final 进行执行,所以对于 status 这种值可变的字段处理过程中,能够查询到中间状态的数据行,导致最终数据不一致。

idversionstatusorderNotypedt
11101311outbound2024-01-01
11211311outbound2024-01-01
11321311outbound2024-01-01

如表所示,id:1 的业务数据,进行三次写入,到 version=13 的数据是最新版本数据,当我们使用 where 过滤 status 这个可变值字段时,语句 1,语句 2 结果如下。

--语句1:使用where + status=1 查询,无法命中id:1这行数据,select count(distinct orderNo) final from table_1 where type = 'outbound' and dt = '2024-01-01' and status = '1';

--语句2:使用where + status=2 查询,可以查询到id:1这行数据,select count(distinct orderNo) final from table_1 where type = 'outbound' and dt = '2024-01-01' and status = '2';

当我们prewhere 后,语句 3 写法:prewhere 过滤 status 字段时将 status=1,version=12 的数据会过滤出来,导致我们查询结果不正确。正确的写法是将不可变字段使用 prewhere 进行优化。

-- 语句3:错误方式,将status放到prewhere
select count(distinct orderNo) final from table_1 prewhere type = 'outbound' and dt = '2024-01-01' and status = '1';

-- 语句4:正确prewhere方式,status可变字段放到where上
select count(distinct orderNo) final from table_1 prewhere type = 'outbound' and dt = '2024-01-01' where and status = '1' ;

其他限制:prewhere 目前只能用于 MergeTree 系列的表引擎

1.9.6 where、group by 顺序

where 和 group by 中的列顺序,要和建表语句中 order by 的列顺序统一,并且放在最前面使得它们有连续不间断的公共前缀,否则会影响查询性能。

-- 建表语句create table group_order_local(    docId              String,    version            UInt64,    siteCode           String,    groupOrderCode     String,    sku                String,    ... 省略非关键字段 ...     createTime         DateTime) engine = ReplicatedReplacingMergeTree('/clickhouse/tms/group_order/{shard}', '{replica}', version)PARTITION BY toYYYYMM(createTime)ORDER BY (siteCode, groupOrderCode, sku);

--查询语句1:
select count(distinct groupOrderCode) groupOrderQty, ifNull(sum(arriveNum),0) arriveNumSum,count(distinct sku) skuQty from tms.group_order final prewhere createTime >= '2024-01-01 22:00:00' and createTime <= '2024-02-01 22:00:00'and siteCode = 'WG0000709'where arriveNum > 0 and test <> '1'

--查询语句2 (where/prewhere中字段):
select count(distinct groupOrderCode) groupOrderQty, ifNull(sum(arriveNum),0) arriveNumSum,count(distinct sku) skuQty from tms.group_order final prewhere siteCode = 'WG0000709' and createTime >= '2024-01-01 22:00:00' and createTime <= '2024-02-01 22:00:00'where arriveNum > 0 and test <> '1'

建表语句 ORDER BY (siteCode, groupOrderCode, sku),语句 1 没有符合要求经过压测 QPS6.4,TP99 0.56s,语句 2 符合要求经过压测 QPS 14.9,TP99 0.12s

在这里插入图片描述

1.9.7性能测试

clickhouse 提供的一个 clickhouse-benchmark 性能测试工具,可通过 docker 搭建 CK 环境。

clickhouse-benchmark -c 1 -h 链接地址 --port 端口号 --user 账号 --password 密码 <<< "具体SQL"

通过这种方式可以了解 SQL 级别的 QPS 和 TP99等信息,这样就可以测试语句优化前后的性能差异。

1.10 保证高并发 ClickHouse 可用性

1)降低查询速度,提高吞吐量

max_threads:位于 users.xml 中,表示单个查询所能使用的最大 CPU 个数,默认是 CPU 核数,假如机器是 32C,则会起 32 个线程来处理当前请求。可以把 max_threads 调低,牺牲单次查询速度来保证 ClickHouse 的可用性,提升并发能力。可通过 jdbc 的 url 来配置

在这里插入图片描述

max_threads 做的一个压测,接口级别压测,一次请求执行 5 次 SQL,处理数据量 508W 行。可以看出 max_threads 越小,QPS 越优秀 TP99 越差。可根据自身业务情况来进行调整一个合适的配置值。

在这里插入图片描述

2)接口增加一定时间的缓存
3)异步任务执行查询语句,将聚合指标结果落到 ES 中,应用查询 ES 中的聚合结果
4)物化视图,通过预聚合方式解决这种问题,但是我们这种业务场景不适用

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/752478.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

开源了!免费合规国内可用的 OpenAI-API 来了

你还在烦恼不能自主构建和调试AI模型吗&#xff1f;你还在为自己的AI项目寻找合适的工具和框架吗&#xff1f;如果你已有一定的AI和python基础&#xff0c;我有一个好消息要告诉你&#xff1a;lang2openai已经开源了&#xff01;这个开源项目让你可以按照openai的方式使用所有模…

【合作ACM出版,稳定EI、Scopus稳定检索】第五届城市工程与管理科学国际会议(ICUEMS 2024,8月2-4)

第五届城市工程与管理科学国际会议&#xff08;ICUEMS 2024&#xff09;将于2024年8月2-4日在天津举行。 会议的目的是为从事城市工程、管理科学相关领域的专家、学者、工程师和技术研究人员提供一个平台&#xff0c;分享科研成果和前沿技术&#xff0c;了解学术发展趋势&…

图象去噪1-使用中值滤波与均值滤波

1、中值滤波 使用中值滤波去除图像的异常像素点&#xff0c;使用cv2.cv2.medianBlur(img, 3)表示再图像在中值滤波窗口3*3的范围内&#xff0c;从下到大排序&#xff0c;将当前值替换为排序中值&#xff08;如下图所示&#xff09;将56替换为&#xff08;56&#xff0c;66,90,…

智慧海洋灾害监测预警系统解决方案

一、概述 近年来&#xff0c;我国海洋经济持续增长&#xff0c;为我国综合国力带来了新机遇。但是&#xff0c;每年由于海洋灾害带来的损失也不可忽略&#xff0c;这些灾害包括&#xff1a;风暴潮、海浪、海冰、海啸动力环境灾害、赤潮、绿潮等等。针对目前海洋监管力量薄弱&am…

Vite: 插件开发

概述 说到自定义的能力&#xff0c;肯定很容易想到 插件机制 &#xff0c;利用一个个插件来扩展构建工具自身的能力虽然 Vite 的插件机制是基于 Rollup 来设计的&#xff0c;但实际上 Vite 的插件机制也包含了自己独有的一部分&#xff0c;与Rollup 的各个插件 Hook 并非完全兼…

2024年6月京东睡眠呼吸暂停和低通气事件检测赛题-baseline

赛题地址&#xff1a;DC竞赛-大数据竞赛平台 (datacastle.cn) 一、数据集介绍 train_x训练数据集特征描述&#xff0c;其样本分布不均匀&#xff0c;0样本29808&#xff0c;1样本3221&#xff0c;2样本4520&#xff0c;共计37549条样本 第一维度&#xff1a;60 位受试样本数总…

数据恢复篇:如何在Android上恢复删除的短信

如果您不小心删除了Android设备上的短信并想要检索它们&#xff0c;则可以尝试以下方法&#xff1a; 如何在Android上恢复删除的短信 检查您的备份&#xff1a; 如果您之前备份了Android设备&#xff0c;则可以从备份中恢复已删除的短信。检查您设备的内部存储空间或 Google 云…

CentOS停止维护,如何应对?

一、事件背景 2020年12月08日&#xff0c;CentOS官方宣布了停止维护CentOS Linux的计划&#xff0c;并推出了CentOS Stream项目。 更多信息&#xff0c;请参见CentOS官方公告。 版本变化说明CentOS 9不再支持新的软件和补丁更新CentOS 82021年12月31日停止维护服务CentOS 720…

goLang小案例-获取从控制台输入的信息

goLang小案例-获取从控制台输入的信息 1. 案例代码展示 package mainimport ("bufio""fmt""log""os" )var pl fmt.Printlnfunc main() {//控制台输出欢迎提示pl("Hello Go")fmt.Print("what is your name? ")…

20240627优雅草新产品取得原始软件著作权授权

https://doc.youyacao.com/22/2153 20240627优雅草新产品取得原始软件著作权授权 介绍 历程消息&#xff1a;优雅草2024年新产品最新取得原始著作权两份&#xff0c;2款产品将在近期完成为商业授权产品在蜻蜓松鼠官网售卖&#xff0c;本两款产品是智慧园区能源监测管理系统解…

Harris角点检测原理及其在python-opencv的调用

文章目录 原理测试 原理 Harris 角点检测的基本思路如下&#xff1a;考虑一个局部的区域&#xff0c;将其作为一个窗口四处移动&#xff0c;若窗口灰度发生了较大的变化&#xff0c;那么&#xff0c;就认为窗口内存在角点&#xff0c;否则窗口内就不存在角点。 对于图像 I ( …

HBase与Hive数据交互

一、hbase数据导入hive hive通过建立外部表和普通表加载hbase表数据到hive表中。 两种方式加载hbase中的表到hive中&#xff0c;一是hive创建外部表关联hbase表数据&#xff0c;是hive创建普通表将hbase的数据加载到本地。 1.创建外部表 hbase中创建test表&#xff0c;且插入…

没有思考过 Embedding,不足以谈 AI

在当今的人工智能&#xff08;AI&#xff09;领域&#xff0c;Embedding 是一个不可或缺的概念。如果你没有深入理解过 Embedding&#xff0c;那么就无法真正掌握 AI 的精髓。接下来&#xff0c;我们将深入探讨 Embedding 的基本概念。 1. Embedding的基本概念 1.1 什么是 Emb…

ET实现游戏中邮件系统逻辑思路(服务端)

ET是一个游戏框架&#xff0c;用的编程语言是C#&#xff0c;游戏引擎是Unity&#xff0c;框架作者&#xff1a;熊猫 ET社区 在游戏中我们通常都会看到有邮件系统&#xff0c;邮件系统的作用有给玩家通知、发放奖励等 下面小编使用ET框架带大家看一下邮件系统的一种实现方…

远程过程调用RPC实现原理

Hi~&#xff01;这里是奋斗的小羊&#xff0c;很荣幸您能阅读我的文章&#xff0c;诚请评论指点&#xff0c;欢迎欢迎 ~~ &#x1f4a5;&#x1f4a5;个人主页&#xff1a;奋斗的小羊 &#x1f4a5;&#x1f4a5;所属专栏&#xff1a;C语言 &#x1f680;本系列文章为个人学习…

深度挖掘数据资产,洞察业务先机:利用先进的数据分析技术,精准把握市场趋势,洞悉客户需求,为业务决策提供有力支持,实现持续增长与创新

在当今日益激烈的商业竞争环境中&#xff0c;企业想要实现持续增长与创新&#xff0c;必须深入挖掘和有效运用自身的数据资产。数据不仅是企业运营过程中的副产品&#xff0c;更是洞察市场趋势、理解客户需求、优化业务决策的重要资源。本文将探讨如何通过利用先进的数据分析技…

多行业预约门店服务小程序源码系统 支持多门店预约 带完整的安装代码包以及搭建教程

系统概述 该系统基于先进的云计算和大数据技术&#xff0c;采用模块化设计&#xff0c;具有高度的可扩展性和可定制性。无论是餐饮、美容美发、健身房还是其他服务行业&#xff0c;都可以通过该系统轻松实现多门店预约功能。同时&#xff0c;我们还提供了丰富的接口和插件&…

stylelint 配置

1.vscode 安装插件Stylelint 2.项目安装插件 pnpm i stylelint stylelint-config-standard stylelint-config-recommended-scss stylelint-config-recommended-vue postcss postcss-html postcss-scss stylelint-config-recess-order stylelint-config-html -D 依赖 说明 备…

如何判断一个Repo是否是Private还是Internal?

Github的Repository分为三种类型&#xff0c;主要是用于决定谁可以访问、查看和克隆该仓库。GitHub 提供了几种不同的可见性选项&#xff0c;包括 Private、Public 和 Internal。 Private 只有仓库的拥有者和被明确邀请为协作者&#xff08;Collaborator&#xff09;的用户才能…

VMware虚拟机移植保姆级教程

文章目录 前言:一、打包与备份二、VMware移植1. 文件介绍2. 移植过程总结:前言: 前几日对电脑做了一个大的更新升级,不仅将硬件进行了升级,还对电脑的软件进行了升级也就是我从Win10今家庭版升级到Win11专业版啦,之前没有升级是因为数据量很多,怕升级后找不到自己需要的…