学习视频链接:
https://www.bilibili.com/video/BV1Yh411z7os/?spm_id_from=333.1007.top_right_bar_window_custom_collection.content.click&vd_source=05e91bcf9e12725fd6d21d00acc4851f
官方文档:
https://clickhouse.com/
部署推荐:128G服务器,100G内存,32线程内存。
使用过程中踩坑笔记:http://t.csdn.cn/elvDm
建议不用double,进行运算时会精度丢失,一般用于数值比较小,不涉及大量计算统计的,精度要求不高的数据。
例:1-0.9 = 0.099999999……
没有单独类型存储布尔值,可使用Uint8类型,取值限制为0、1
使用场景:一般金额字段、汇率、利率等字段为了保证小数点精度,都是用Decimal进行存储。
NOTE:
Using `Nullable` almost always negatively affects performance, keep this in mind when designing your databases.
如果要使用Null,可以定义字段中Null值为业务上没有意义的字段,比如:年龄的Null为-1。
https://clickhouse.com/docs/zh/engines/table-engines/
表引擎(即表的类型)决定了:
特别注意:引擎的命名大小写敏感。
命名方式:大驼峰:BigTableData
含义:将ClickHouse数据直接映射到外部引擎中,在ClickHouse上进行查询。
因此:ClickHouse这种方式并不拥有数据,只做查询。
思考:
是不是可以直接把所有游戏表映射到CH中,进行查询?
https://clickhouse.com/docs/en/engines/table-engines/integrations/mysql
建表语句
CREATE TABLE [IF NOT EXISTS] [db.]table_name [ON CLUSTER cluster]
(
name1 [type1] [DEFAULT|MATERIALIZED|ALIAS expr1] [TTL expr1],
name2 [type2] [DEFAULT|MATERIALIZED|ALIAS expr2] [TTL expr2],
...
INDEX index_name1 expr1 TYPE type1(...) GRANULARITY value1,
INDEX index_name2 expr2 TYPE type2(...) GRANULARITY value2,
...
PROJECTION projection_name_1 (SELECT <COLUMN LIST EXPR> [GROUP BY] [ORDER BY]),
PROJECTION projection_name_2 (SELECT <COLUMN LIST EXPR> [GROUP BY] [ORDER BY])
) ENGINE = MergeTree()
ORDER BY expr // 必选项
[PARTITION BY expr]
[PRIMARY KEY expr]
[SAMPLE BY expr]
[TTL expr
[DELETE|TO DISK 'xxx'|TO VOLUME 'xxx' [, ...] ]
[WHERE conditions]
[GROUP BY key_expr [SET v1 = aggr_func(v1) [, v2 = aggr_func(v2) ...]] ] ]
[SETTINGS name=value, ...]
CREATE TABLE example_table
(
d DateTime,
a Int TTL d + INTERVAL 1 MONTH,
b Int TTL d + INTERVAL 1 MONTH,
c String
)ENGINE = MergeTree
PARTITION BY toYYYYMM(d)
primary key(a) // 注意:主键不存在唯一约束!!!
ORDER BY d;
作用
降低扫描范围,优化查询速度。
如果不填
只会使用一个分区-all。
分区目录
以列文件+索引文件+表定义文件组成。
并行
分区后,以分区为单位并行处理。
官方建议以“天”为分区条件。
<分区值_最小分区块编号_最大分区块编号_合并层级>
PartitionId
数据分区ID生成规则:
数据分区规则由分区ID决定,分区ID由PARTITION BY分区键决定。根据分区键字段类型,ID生成规则可分为:
未定义分区键:
- 没有定义PARTITION BY,默认生成一个目录名为all的数据分区,所有数据均存放在all目录下。
整形分区键:
- 分区键为整形,那么直接用该整形值的字符串形式作为分区ID。
日期类分区键:
- 分区键为日期类型,或者可以转化成日期类型。
其他类型分区键:
- String、Float类型等,通过128位的Hash算法取其Hash值作为分区ID。
MinBlockNum
最小分区块编号,自增类型,从1开始向上递增。每产生一个新的目录分区就香上递增一个数字。
MaxBlockNum
最大分区块编号,新创建的分区MinBlockNum等于MaxBlockNum的编号。
Level
合并的层级,被合并的次数。合并次数越多,层级值越大。
bin文件:数据文件
mrk:标记文件
count.txt:存储数据条数
data.bin
数据文件,表里面的数据存储在这里面
老版本中(21.7之前)是每一列存储一个.bin和.mrk2文件
columns.txt
列的结构信息
primary.idx
索引文件——>稀疏索引
minmax_create_time.idx
分区键的最大最小值
任何一个批次的数据写入都会产生一个临时分区,不会纳入任何已有分区。写入后一段时间(10-15mins后),clickhouse会自动执行合并操作(也可以手动通过optimize执行合并),将临时分区的数据,合并到以后分区中。
-- 加入了分区名表示仅对某个分区进行合并
optimize table 表名 [partition 分区名] final;
仅提供了一级索引,但不是唯一约束。
index granularity:索引粒度,指在稀疏索引中两个相邻索引对应数据的间隔。ClickHosue中的MergeTree默认是8192。官方不建议修改这个值,除非该列存在大量重复值,例如:一个分区中几万行才有一个不同数据。
分区内排序!!!
要求:主键必须是order by字段的前缀字段!!!
例如:order by的字段是(id,sku_id),主键若只有一个,只能是id,不能是sku_id。
在一级索引分块的基础上,再次进行汇总
create table t_order_mt2(
id UInt32,
sku_id String,
total_amount Decimal(16,2),
create_time Datetime,
-- GRANULARITY 是设定二级索引对于一级索引粒度的分区粒度
-- 例如:一级索引:[0,5],[5,10],[10,15],[15,20]……
-- GRANULARITY 2 ----> 则二级索引:[0,10],[10,20]……
INDEX a total_amount TYPE min max GRANULARITY 5
)engine = MergeTree
partition by toYYYYMMDD(create_time)
primary key(id)
order by(id,sku_id)
TTL:Time To Live,MergeTree提供了可以管理数据表or列的生命周期功能。
TTL不能用于键列。
https://clickhouse.com/docs/en/engines/table-engines/mergetree-family/mergetree/#creating-a-table-with-ttl
Creating a table with TTL
CREATE TABLE example_table
(
d DateTime,
a Int TTL d + INTERVAL 1 MONTH,
b Int TTL d + INTERVAL 1 MONTH,
c String
)
ENGINE = MergeTree
PARTITION BY toYYYYMM(d)
ORDER BY d;
Adding TTL to a column of an existing table
ALTER TABLE example_table
MODIFY COLUMN
c String TTL d + INTERVAL 1 DAY;
https://clickhouse.com/docs/en/engines/table-engines/mergetree-family/mergetree/#mergetree-table-ttl
过期可以移动数据到指定位置。
相对于MergeTree,只是多一个去重的功能。根据order by的字段去重。
去重时机
数据去重只会在合并的过程中出现。合并会在未知的时间在后台进行,所以无法预先做出计划,有一些数据可能仍未被处理。
去重范围
去重只会在分区内部进行去重,不能执行跨分区的去重。
因此,ReplacingMergeTree能力有限,不能保证没有重复的数据出现。
create table t_order_rmt(
in UInt32,
sku_id String,
total_amount Decimal(16,2),
create_time Datetime
)engine = ReplacingMergeTree(create_time) -- 这里create_time为版本字段
partition by toYYYYMMDD(create_time)
primary key(id)
order by(id,sku_id);
ReplacingMergeTree()填入的参数为版本字段,重复数据保留版本字段值最大的。如果不填版本字段,默认按照插入顺序保留最后一条。
使用场景: 对于不查询明细,只关心以维度进行汇总聚合结果的场景。
预聚合功能:①分区内聚合;②分片合并时才会聚合。
create table t_order_rmt(
in UInt32,
sku_id String,
total_amount Decimal(16,2),
create_time Datetime
)engine = SummingMergeTree(total_amount) -- 这里total_amount为聚合字段
partition by toYYYYMMDD(create_time)
primary key(id)
order by(id,sku_id); -- 根据这两个字段进行group by,预聚合
开发建议:设计聚合表的话,唯一键值、流水号可以去掉,所有字段全部是维度、度量或者时间戳。
这类操作被称为Mutation查询(可变查询),可以看作Alter的一种。
alter table t_order_smt update total_amount = toDecimal32(2000.00,2) where id =102;
alter table t_order_smt delete where sku_id = 'sku_001';
create table A(
a XXX,
b XXX,
c XXX,
_sign UInt8,
_version UInt32
)
-- 更新:插入一条新的数据,_version + 1
-- 查询:where version = max_version
-- 删除:_sign 0 表示未删除,1表示已删除
-- 查询:where sign = 0 and version = max_version
-- 问题:时间久了,数据会膨胀
-- 需要解决:类似合并机制,清理过期数据
KEY words:with rollup, with cube, with totals
-- 这两个语句等同
SELECT year, month, day, count(*) FROM t GROUP BY CUBE(year, month, day);
SELECT year, month, day, count(*) FROM t GROUP BY year, month, day WITH CUBE;
假设维度是a,b
rollup:上卷,从右到左,维度增加
cube:多维分析
totals:总计
https://clickhouse.com/docs/en/interfaces/formats
clickhouse-client --query "select * from t_order_mt where create_time = '2020-06-01'" --format CSVWithNames> [导出地址]
https://clickhouse.com/docs/en/sql-reference/statements/explain/#explain-types
EXPLAIN [AST | SYNTAX |PLAN|PIPELINE] [setting = value,...] select ...
跑语句之前可以使用这个语法查看,用于优化语法;
用于查看Pipeline计划
建表时能用数值型或日期时间类型表示的字段就不要用字符串,虽然ClickHouse底层将DateTime存储为时间戳Long类型,但不建议存储Long类型,因为DateTime不需要经过函数转换,执行效率高、可读性好。
create table test(
id UInt32,
sku_id String,
total_amount Decimal(16,2),
create_time Int32
)engine = ReplacingMergeTree(create_time)
partition by toYYYYMMDD(toDate(create_time)) -- 需要转换一次,否则报错
primary key (id)
order by (id,sku_id)
一般CH的瓶颈出现在CPU压力过大、内存不足、IO压力过大
配置 | 描述 |
---|---|
background_pool_size | 后台线程池的大小,merge线程就是在该线程池中执行,该线程池不仅仅是给merge线程使用的,默认值16,允许的前提下建议改成CPU个数的2倍(一核可以虚拟化成两线程) |
background_schedule_pool_size | 执行后台任务的线程数,默认128,建议改成CPU个数的2倍(线程数) |
background_distributed_schedule_pool_size | 设置为分布式发送执行后台任务的线程数,默认16,建议改成CPU个数的2倍(线程数) |
max_concurrent_queries | 最大并发处理请求数(包含select、insert等),默认值100,推荐150(不够再加)~300,默认单位为个/每秒 |
max_threads | 设置单个查询所能使用的最大CPU个数,默认是CPU核数 |
配置 | 描述 |
---|---|
max_memory_usage | 此参数在users.xml中,表示单词Query占用内存最大值,该值可以设置的比较大,这样可以提升集群查询的上限。保留一点给OS,比如128G内存的机器,设置为100G。 |
max_bytes_before_external_group_by | 一般按照max_memory_usage的一半设置内存,当group使用内存超过阈值后会刷新到磁盘进行。因为Click house聚合分为两个阶段:查询并建立中间数据、合并中间数据,结合上一项,建议50GB。 |
max_bytes_before_external_sort | 当 order by已使用max_bytes_before_external_sort内存就进行溢写磁盘(基于磁盘排序),如果不设置该值,那么当内存不够时直接抛错,设置了该值 order by可以正常完成,但是速度相对存内存来说肯定要慢点(实测慢的非常多,无法接受)。 |
max_table_size_to_drop | 此参数在 config.xml 中,应用于需要删除表或分区的情况,默认是50GB,意思是如果删除50GB以上的分区表会失败。建议修改为0,这样不管多大的分区表都可以删除。 |
ClickHouse不支持设置多数据目录,为了提升数据IO性能,可以挂在虚拟券组,一个券组绑定多块物理磁盘提升读写性能,多数据查询性能场景SSD会比普通机械硬盘快2-3倍。
在调用count函数时,如果使用的是count() or count(*),且没有where条件,则会直接使用system.tables 的 total_rows。
explain syntax select count(*) from zs_game_role zgr
-- 结果
SELECT count()
FROM zs_game_role AS zgr
explain
syntax
select
a.role_id,
a.member_id,
a.main_game_id
from
zs_game_role b
left join
(
select
role_id,
role_id,
member_id,
main_game_id,
role_id
from
zs_game_role zgr
) as a
using (role_id)
limit 3
-- 结果
SELECT
a.role_id,
a.member_id,
a.main_game_id
FROM zs_game_role AS b
ALL LEFT JOIN
(
SELECT
role_id,
member_id,
main_game_id
FROM zs_game_role AS zgr
) AS a USING (role_id)
LIMIT 3
explain
syntax
select
*
from(
select
role_id
from
zs_game_role zgr
) as a
WHERE a.role_id = 20
-- 结果
SELECT role_id
FROM
(
SELECT role_id
FROM zs_game_role AS zgr
WHERE role_id = 20
) AS a
WHERE role_id = 20
explain
syntax
SELECT
*
FROM
(
select
role_id
from
zs_game_role zgr
union all
select
role_id
from
zs_game_role zgr
) as a
WHERE
a.role_id = 20
-- 结果
SELECT role_id
FROM
(
SELECT role_id
FROM zs_game_role AS zgr
WHERE role_id = 20
UNION ALL
SELECT role_id
FROM zs_game_role AS zgr
WHERE role_id = 20
) AS a
WHERE role_id = 20
explain
syntax
SELECT sum(role_id * 2)
from
zs_game_role zgr
-- 结果
SELECT sum(role_id) * 2
FROM zs_game_role AS zgr
如果对聚合键,也就是group by key 使用 min、max、any聚合函数,则将函数消除。
explain
syntax
SELECT
sum(member_id * 2),
max(game_id),
any(role_id)
from
zs_game_role zgr
group by
role_id
-- 结果
SELECT
sum(member_id) * 2,
max(game_id),
role_id
FROM zs_game_role AS zgr
GROUP BY role_id
explain
syntax
SELECT
role_id,
member_id,
main_game_id
from
zs_game_role zgr
order by
role_id,
role_id ,
member_id ,
member_id
-- 结果
SELECT
role_id,
member_id,
main_game_id
FROM zs_game_role AS zgr
ORDER BY
role_id ASC,
member_id ASC
explain
syntax
SELECT
role_id,
member_id,
main_game_id
from
zs_game_role zgr
limit 3 by role_id ,
member_id ,
role_id ,
member_id
-- 结果:会优化
SELECT
role_id,
member_id,
main_game_id
FROM zs_game_role AS zgr
LIMIT 3 BY
role_id,
member_id
explain
syntax
SELECT
zgr2.role_id,
zgr2.member_id,
zgr2.main_game_id
from
zs_game_role zgr
left join zs_game_role zgr2 on zgr.role_id = zgr2.role_id
limit 3 by zgr.role_id ,
zgr.member_id ,
zgr2.role_id ,
zgr2.member_id
-- 结果:不会优化
SELECT
zgr2.role_id,
zgr2.member_id,
zgr2.main_game_id
FROM zs_game_role AS zgr
ALL LEFT JOIN zs_game_role AS zgr2 ON role_id = zgr2.role_id
LIMIT 3 BY
role_id,
member_id,
zgr2.role_id,
zgr2.member_id
标量:写死的值
explain
syntax
with
(
SELECT
count()
from
zs_game_role zgr3
) as total_num
SELECT
zgr.role_id,
zgr.member_id,
SUM(zgr.main_game_id) / total_num as a
from
zs_game_role zgr
group by
zgr.role_id,
zgr.member_id
order by
zgr.role_id,
zgr.member_id
limit 5
-- 结果:with的内容替换成一个标量值了
WITH identity(_CAST(0, 'Nullable(UInt64)')) AS total_num
SELECT
role_id,
member_id,
sum(main_game_id) / total_num AS a
FROM zs_game_role AS zgr
GROUP BY
role_id,
member_id
ORDER BY
role_id ASC,
member_id ASC
LIMIT 5
Prewhere 和where 语句的作用相同,用来过滤数据。不同之处在于 prewhere 只支持MergeTree 族系列引擎的表,首先会读取指定的列数据,来判断数据过滤,等待数据过滤之后再读取 select 声明的列字段来补全其余属性。
当查询列明显多于筛选列时使用Prewhere可十倍提升查询性能,Prewhere会自动优化执行过滤阶段的数据读取方式,降低io操作。
在某些场合下,prewhere 语句比 where 语句处理的数据量更少性能更高。
某些场景不会自动转换成prewhere:
通过采样运算可极大提升数据分析的性能。
select
Title,count(*) as pageViews
from
table
SAMPLE 0.1
where countID = XX
group by XX
order by XX
千万以上的数据集进行order by查询时需要搭配where条件和limit语句一起使用。
select
a,
b,
a+b -- 虚拟列
from
table
虚拟列非常消耗资源,浪费性能。
性能可提升10倍以上,uniqCombined底层采用类似HyperLogLog算法实现。
不建议对千万以上的对准确度没有要求的数据进行精确去重,使用近似去重,例如:精确活跃率1千万,近似去重:0.999千万。
视图:保存的是SQL的操作逻辑。
物化视图:不仅保存SQL的操作逻辑,还保存操作过后的结果。
为了避免因个别慢查询引起的服务雪崩问题,除了可以为单个查询设置超时以外,还可以配置周期熔断,在一个查询周期内,如果用户频繁进行慢查询操作超出规定阈值后将无法继续进行查询。
物理内存和虚拟内存的数据交换,会导致查询变慢,资源允许的情况下关闭虚拟内存。
为每一个账户添加join_use_nulls配置,左表中的一条记录在右表中不存在,右表的相应字段会返回该字段相应数据类型的默认值,而不是标准SQL中的Null值。
批量写入数据时,必须控制每个批次的数据中涉及到的分区的数量,在写入之前最好对需要导入的数据进行排序。无序的数据或者涉及的分区太多,会导致 CIick House 无法及时对新导入的数据进行合并,从而影响查询性能。
CPU一般在50%左右会出现查询波动,达到70%会出现大范围的查询超时,CPU是关键指标,要非常关注。
CH的JOIN:
-- 建表的时候,想要复制表结构:
create table XXX as select * from XXXX where 1 = 0; -- 条件不成立,数据永远不会写进来
A join B,将B表全部加载到内存中,A表中的数据会逐条匹配内存中的B表。
select table_a.* from table_a where table_a.count_id in (select count_id from table_b);
多表 join 时要满足小表在右的原则,右表关联时被加载到内存中与左表进行比较, Click House 中无论是 Left join 、 Right join 还是 Inner join 永远都是拿着右表中的每一条记录到左表中查找该记录是否存在,所以右表必须是小表。
ClickHouse 在join查询时不会主动发起谓词下推的操作,需要每个子查询提前完成过滤操作,需要注意的是,是否执行谓词下推,对性能影响差别很大(新版本中已经不存在此问题,但是需要注意谓词的位置的不同依然有性能的差异)
两张分布式表上的IN和JOIN之前必须加上GLOBAL关键字,右表只会在接收查询请求的那个节点查询一次,并将其分发到其他节点上。如果不加GLOBAL关键字的话,每个节点都会单独发起一次对右表的查询,而右表又是分布式表,就导致右表一共会被查询N²次(N是该分布式表的分片数量),这就是查询放大,会带来很大开销。
将一些需要关联分析的业务创建成字典表进行join操作,前提是字典表不宜太大,因为字典表会常驻内存。
通过增加逻辑过滤可以减少数据扫描,达到提高执行速度、降低内存消耗的目的。
create table test_a(
user_id UInt64,
score String,
deleted UInt8 DEFAULT 0,
create_time DateTime DEFAULT toDateTime(0)
)ENGINE = ReplacingMergeTree(create_time)
order by
user_id;
INSERT
into
table test_a(user_id,
score)
with(
select
['A',
'B',
'C',
'D',
'E',
'F',
'G']
)as dict
select
number as user_id,
dict[number%7 + 1]
from
numbers(10000000);
INSERT
into
table test_a (user_id,
score,
create_time)
with(
select
['AA',
'BB',
'CC',
'DD',
'EE',
'FF',
'GG']) as dict
select
number as user_id,
dict[number%7 + 1],
NOW() as create_time
from
numbers(500000);
optimize table test_a final
SELECT
user_id,
argMax(score,
create_time) as score,
argMax(deleted,
create_time) as deleted,
max(create_time) as ctime
from
test_a ta
group by
user_id
having
deleted = 0;
create view view_test_a as
SELECT
user_id,
argMax(score,
create_time) as score,
argMax(deleted,
create_time) as deleted,
max(create_time) as ctime
from
test_a ta
group by
user_id
having
deleted = 0;
insert into table test_a(user_id,score,create_time) values(0,'AAAA',now());
SELECT * FROM view_test_a WHERE user_id = 0;
>>> 结果
id user_id score create_time
0 AAAA 0 2022-09-13 15:43:38.000
insert into table test_a (user_id,score,deleted,create_time) values(0,'AAAA',1,now());
select * from view_test_a WHERE user_id = 0;
>>> 结果:无数据
在查询语句后增加Final修饰符,这样在查询的过程中将会执行Merge的特殊逻辑(如:数据去重,预聚合等)。
在v20.5.2.7-stable版本之后,FINAL查询支持多线程执行,并且可以通过max_final_threads参数控制单个查询的线程数。但是目前读取part部分的动作依然是串行的。
FINAL查询最终的性能和很多因素相关,列字段的大小、分区的数量等等都会影响到最终的查询时间,所以还要结合实际场景取舍。
参考链接:https://github.com/ClickHouse/ClickHouse/pull/10463
explain pipeline SELECT * from test_a ta WHERE create_time > toDateTime(0);
>>>结果
(Expression)
ExpressionTransform × 6
(ReadFromMergeTree)
MergeTreeThread × 6 0 → 1
视图:保存的是SQL的操作逻辑。
物化视图:不仅保存SQL的操作逻辑,还保存操作过后的结果,结果根据相应的引擎存到磁盘或内存中。
ClickHouse 的物化视图是一种查询结果的持久化,它确实是给我们带来了查询效率的提升。用户查起来跟表没有区别,它就是一张表,它也像是一张时刻在预计算的表,创建的过程它是用了一个特殊引擎,加上后来 as select,就是 create一个table as select的写法。
“查询结果集”的范围很宽泛,可以是基础表中部分数据的一份简单拷贝,也可以是多表join之后产生的结果或其子集,或者原始数据的聚合指标等等。所以,物化视图不会随着基础表的变化而变化,所以它也称为快照(snapshot)。
优点:查询速度快,要是把物化视图这些规则全部写好,它比原数据查询快了很多,总的行数少了,因为都预计算好了。
缺点:它的本质是一个流式数据的使用场景,是累加式的技术,所以如果要用历史数据做去重、去核这样的分析,在物化视图里面是不太好用的。在某些场景的使用也是有限的。而且如果一张表加了好多物化视图,在写这张表的时候,就会消耗很多机器的资源,比如数据带宽占满、存储一下子增加了很多。
创建时会创建一个隐藏的目标表来保存视图数据。也可以TO表明,保存到一张显式的表。没有加TO表名,表名默认就是.inner.物化视图名。
CREATE MATERIALIZED VIEW [IF NOT EXISTS] [db.]table_name [ON CLUSTER] [TO[db.]name] [ENGINE = engine] [POPULATE] AS SELECT ...
[POPULATE]:添加之后,在创建视图时会遍历历史数据,会增加服务器负载,如果要历史数据,使用INSERT INTO写入数据。
在创建没有TO [db].[table]
的物化视图时,您必须指定ENGINE
- 用于存储数据的表引擎。
使用TO [db].[table]
创建物化视图时,不得使用POPULATE
.
物化视图的实现方式如下:向 中指定的表中插入数据时,插入的SELECT
部分数据通过该SELECT
查询进行转换,并将结果插入到视图中。
查询语句可以包含下面的子句:DISTINCT , GROUP BY , ORDER BY , LIMIT……
create table test_a_test(
user_id UInt64,
score String,
deleted UInt8 DEFAULT 0,
create_time Date
)ENGINE = MergeTree()
partition by toYYYYMM(create_time)
order by
(create_time,
intHash32(user_id))
sample by intHash32(user_id)
SETTINGS index_granularity = 8192;
insert
into
test_a_test
select
*
FROM
test_a
limit 10000;
create materialized view test_mview
engine = SummingMergeTree
Partition by toYYYYMM(create_time)
order by
(create_time,
intHash32(user_id))
as
SELECT
user_id,
create_time,
count(score),
sum(deleted)
from
test_a_test ta
WHERE
create_time >= toDate(0)
group by user_id,create_time ;
show tables;
>>>结果
.inner_id.5bfba660-812e-49ec-885f-3fa63e16f2f4 -- 默认存储数据的表格
test_a
test_a_test
test_mview
view_test_a
SELECT * from test_mview;-- 第一次查询结果为空
insert
into
test_a_test
select
*
FROM
test_a
limit 10;
SELECT * from test_mview;-- 插入后在查询有10条新增数据
select * from `.inner_id.068a0cde-c260-4fee-b902-c7f74cc4f194`; -- 自动创建的表中也有数据
insert
into
test_mview
-- 将物化视图的逻辑再写一遍
SELECT
user_id,
create_time,
count(score),
sum(deleted)
from
test_a_test ta
WHERE
create_time >= toDate(0)
group by user_id,create_time ;