ClickHouse--11--物化视图

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

  • 1.物化视图
      • 什么是物化视图?
    • 1.1 普通视图
    • ==1.2 物化视图==
    • 1.3 优缺点
    • 1.4 基本语法
    • 1.5 在生产环境中创建物化视图
    • 1.6 AggregatingMergeTree 表引擎
      • 3.1 概念
      • 3.2 AggregatingMergeTree 建表语句
      • 3.3 搭配使用 案例
  • 物化视图--- 案例1
    • 1.原始数据表
    • 物化视图---方式1:
    • 物化视图---方式2:
      • 为什么查询物化视图 还要GROUP BY +sum ?
        • ==在使用 MV 的聚合引擎时,也需要按照聚合查询 配合 GROUP BY 来写sql,因为聚合时机不可控==
    • 物化视图---方式3:
  • 物化视图--- 案例2
    • 1.原始数据表----以Wikistat的10亿行数据集为例:
    • 2.创建物化视图
    • 3.填充数据
    • 4.测试查询 物化视图
      • 为什么查询物化视图 还要GROUP BY +sum ?
        • ==在使用 MV 的聚合引擎时,也需要按照聚合查询 配合 GROUP BY 来写sql,因为聚合时机不可控==
    • 5.更新物化视图中的数据
        • ==在使用 MV 的聚合引擎时,也需要按照聚合查询 配合 GROUP BY 来写sql,因为聚合时机不可控==
    • 6.使用物化视图加速聚合(AggregatingMergeTree引擎)
        • ==在使用 MV 的聚合引擎时,也需要按照聚合查询 配合 GROUP BY 来写sql,因为聚合时机不可控==
    • 7.验证和过滤数据
    • 8.数据路由到表格
    • 9.数据转换
    • 10.物化视图和JOIN操作
        • ClickHouse博客; [https://blog.csdn.net/ClickHouseDB?type=blog](https://blog.csdn.net/ClickHouseDB?type=blog)


1.物化视图

ClickHouse 中视图分为普通视图和物化视图,两者区别如图所示

在这里插入图片描述
在这里插入图片描述

什么是物化视图?

  • 物化视图是一种特殊的触发器,当数据被插入时,它将数据上执行 SELECT 查询的结果存储为到一个目标表中:

在这里插入图片描述

1.1 普通视图

  • 普通视图不存储数据,它只是一层 select 查询映射,类似于表的别名或者同义词,能简化查询,对原有表的查询性能没有增强的作用,具体性能依赖视图定义的语句,
  • 当从视图中查询时,视图只是替换了映射的查询语句。普通视图当基表删除后不可用。
    在这里插入图片描述

1.2 物化视图

  • 物化视图是查询结果集的一份持久化存储,所以它与普通视图完全不同,而非常趋近于表。”查询结果集”的范围很宽泛,可以是基础表中部分数据的一份简单拷贝,也可以是多 表 join 之后产生的结果或其子集,或者原始数据的聚合指标等等。
  • 物化视图创建好之后,若源表被写入新数据则物化视图也会同步更新,POPULATE 关键字决定了物化视图的更新策略,若有 POPULATE 则在创建视图的过程会将源表已经存在的 数据一并导入,类似于 create table … as,若无 POPULATE则物化视图在创建之后 没有数据,只会在创建只有同步之后写入源表的数据,clickhouse 官方并不推荐使用 populated,因为在创建物化视图的过程中同时写入的数据不能被插入物化视图。
  • 物化视图是种特殊的数据表,创建时需要指定引擎,可以用 show tables 查看。另外,物化视图不支持 alter 操作。
  • 产生物化视图的过程就叫做“物化”(materialization),广义地讲,物化视图是 数据库中的预计算逻辑+显式缓存,典型的空间换时间思路,所以用得好的话,它可以避免 对基础表的频繁查询并复用结果,从而显著提升查询的性能。
    在这里插入图片描述

1.3 优缺点

优点:查询速度快

缺点:写入过程中消耗较多机器资源,比如带宽占满,存储增加等。本质是一个流式数据的使用场景,是累加式的技术,所以要用历史数据做去重、去核的分析操作不太好用。

1.4 基本语法

创建一个隐藏的目标表来保存视图数据,表名默认是.inner.物化视图名。如果加了TO表名,将保存到显式的表。

create [materialized] view [if not exists] [db.]table_name [to [db.]name] 
[engine = engine] [populate]
as select ...

限制条件:

  1. 必须指定物化视图的engine用于数据存储(要么是物化视图,要么是指定的显式表)
  2. to [db.]table的时候,不得使用populate
  3. 查询语句可以包含子句:distinct, group by, order by, limit …

1.5 在生产环境中创建物化视图

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

1.6 AggregatingMergeTree 表引擎

在这里插入图片描述

3.1 概念

  • 该表引擎继承自 MergeTree,可以使用 AggregatingMergeTree表来做增量数据统计聚合。如果要按一组规则来合并减少行数,则使AggregatingMergeTree 是合适的。AggregatingMergeTree 是通过预先定义的聚合函数计算数据并通过二进制的格式存入表内。
  • 与 SummingMergeTree 的区别在于:SummingMergeTree 对非主键列进行 sum 聚 合,而 AggregatingMergeTree 则可以指定各种聚合函数。对某些字段需要进行聚合时, 需要在创建表字段时指定成 AggregateFunction 类型

3.2 AggregatingMergeTree 建表语句

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3.3 搭配使用 案例

  • 以上方式使用 AggregatingMergeTree 表引擎比较不方便,更多情况下,我们将AggregatingMergeTree 作为物化视图的表引擎与 MergeeTree 搭配使用

示例:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

物化视图— 案例1

1.原始数据表

-- 建表
CREATE TABLE test.tb_mtview_counter (create_time DateTime DEFAULT now(),device UInt32,value Float32
) 
ENGINE=MergeTree
PARTITION BY toYYYYMM(create_time)
ORDER BY (device, create_time);
  • 插入数据
-- 插入数据
INSERT INTO test.tb_mtview_counter
SELECTtoDateTime('2015-01-01 00:00:00') + toInt64(number/10) AS create_time,(number % 10) + 1 AS device,(device * 3) +  (number/10000) + (rand() % 53) * 0.1 AS value
FROM system.numbers LIMIT 100000;-- 查询源表
SELECTtoStartOfMonth(create_time) as day,device,count(*) as count,sum(value) as sum,max(value) as max,min(value) as min,avg(value) as avg
from test.tb_mtview_counter 
GROUP BY device, day
ORDER BY day, device;
  • 数据表查询结果如下:
┌────────day─┬─device─┬─count─┬────────────────sum─┬─────max─┬─────min─┬────────────────avg─┐
│ 2015-01-01110000106363.5000033378618.0963.02110.636350000333787 │
│ 2015-01-01210000136010.9000520706221.10116.170113.601090005207062 │
│ 2015-01-01310000166312.000029563924.19429.002216.63120000295639 │
│ 2015-01-01410000195977.0000085830727.178312.000319.597700000858307 │
│ 2015-01-01510000226182.1999397277830.182415.028422.61821999397278 │
│ 2015-01-01610000256159.1999626159733.079518.029525.615919996261596 │
│ 2015-01-01710000286113.200002670336.197621.101628.61132000026703 │
│ 2015-01-01810000315845.900049209639.088724.123731.58459000492096 │
│ 2015-01-01910000345891.3000373840342.042827.004834.589130003738404 │
│ 2015-01-011010000376212.099996566845.078930.000937.62120999965668 │
└────────────┴────────┴───────┴────────────────────┴─────────┴─────────┴────────────────────┘

物化视图—方式1:

-- 创建物化视图 -- 没有to table时必须有engine
CREATE MATERIALIZED VIEW test.tb_mtview_counter_daily_mv
ENGINE = SummingMergeTree()
PARTITION BY toYYYYMM(day) ORDER BY (device, day)
AS SELECTtoStartOfMonth(create_time) as day,device,count(*) as count,sum(value) as sum,max(value) as max,min(value) as min,avg(value) as avg
FROM test.tb_mtview_counter
WHERE create_time >= toDate('2016-01-01 00:00:00')
GROUP BY device, day
ORDER BY device, day;
  • 查询物化视图
-- 查询物化视图
SELECTdevice, day, count, sum, max, min, avg
FROM test.tb_mtview_counter_daily_mv;

查询结果,物化视图中此时没有数据,为何?这是因为未使用 populate 关键字,该关键字并不推荐使用。

思考:为什么建表要加条件 WHERE create_time >= toDate(‘2016-01-01 00:00:00’) ?

在后续的测试中发现其实不加也没有问题,猜测加过滤条件是为了确保建表时数据无法被导入,从而确保数据不会出错。

  • 向源表插入数据
-- 源表插入数据
INSERT INTO test.tb_mtview_counter
SELECTtoDateTime('2017-01-01 00:00:00') + toInt64(number/10) AS create_time,(number % 10) + 1 AS device,(device * 3) +  (number/10000) + (rand() % 53) * 0.1 AS value
FROM system.numbers LIMIT 100000;-- 查询物化视图
SELECTdevice, day, count, sum, max, min, avg
FROM test.tb_mtview_counter_daily_mv;
  • 结果如下:
┌─device─┬────────day─┬─count─┬────────────────sum─┬─────max─┬─────min─┬────────────────avg─┐
│      12017-01-0110000106250.9000189304418.1533.12210.625090001893044 │
│      22017-01-0110000135986.0999360084521.18516.105113.598609993600846 │
│      32017-01-0110000165927.1999521255524.18629.203216.592719995212555 │
│      42017-01-0110000195965.700024604827.174312.019319.59657000246048 │
│      52017-01-0110000226203.9000482559230.122415.005422.62039000482559 │
│      62017-01-0110000256157.800012588533.191518.002525.61578000125885 │
│      72017-01-0110000285870.000099182136.157621.003628.587000009918214 │
│      82017-01-0110000315986.5999450683639.147724.026731.598659994506836 │
│      92017-01-0110000346104.499822616642.197827.002834.610449982261656 │
│     102017-01-0110000376108.500066757245.180930.005937.61085000667572 │
└────────┴────────────┴───────┴────────────────────┴─────────┴─────────┴────────────────────┘
  • 向物化视图插入数据
-- 向物化视图插入数据
insert into test.tb_mtview_counter_daily_mv
SELECTtoStartOfMonth(create_time) as day,device,count(*) as count,sum(value) as sum,max(value) as max,min(value) as min,avg(value) as avg
from test.tb_mtview_counter where create_time < toDate('2016-01-01 00:00:00')
GROUP BY device, day
ORDER BY device, day;-- 查询物化视图
SELECTdevice, day, count, sum, max, min, avg
FROM test.tb_mtview_counter_daily_mv;
  • 结果如下:
┌─device─┬────────day─┬─count─┬────────────────sum─┬─────max─┬─────min─┬────────────────avg─┐
│      12017-01-0110000106250.9000189304418.1533.12210.625090001893044 │
│      22017-01-0110000135986.0999360084521.18516.105113.598609993600846 │
│      32017-01-0110000165927.1999521255524.18629.203216.592719995212555 │
│      42017-01-0110000195965.700024604827.174312.019319.59657000246048 │
│      52017-01-0110000226203.9000482559230.122415.005422.62039000482559 │
│      62017-01-0110000256157.800012588533.191518.002525.61578000125885 │
│      72017-01-0110000285870.000099182136.157621.003628.587000009918214 │
│      82017-01-0110000315986.5999450683639.147724.026731.598659994506836 │
│      92017-01-0110000346104.499822616642.197827.002834.610449982261656 │
│     102017-01-0110000376108.500066757245.180930.005937.61085000667572 │
└────────┴────────────┴───────┴────────────────────┴─────────┴─────────┴────────────────────┘
┌─device─┬────────day─┬─count─┬────────────────sum─┬─────max─┬─────min─┬────────────────avg─┐
│      12015-01-0110000105885.8000040054318.1663.02110.588580000400544 │
│      22015-01-0110000136100.1999921798721.03616.039113.610019999217988 │
│      32015-01-0110000166083.2999429702824.09429.043216.608329994297026 │
│      42015-01-0110000196134.9000501632727.178312.036319.613490005016327 │
│      52015-01-0110000225946.5999670028730.199415.158422.594659996700287 │
│      62015-01-0110000256283.3999137878433.190518.013525.628339991378784 │
│      72015-01-0110000286232.7000560760535.991621.006628.623270005607605 │
│      82015-01-0110000315966.500055313139.153724.057731.596650005531313 │
│      92015-01-0110000345799.699810028142.138827.053834.579969981002805 │
│     102015-01-0110000375820.099843978945.150930.077937.582009984397885 │
└────────┴────────────┴───────┴────────────────────┴─────────┴─────────┴────────────────────┘
  • 向源表插入同分区数据
  • 插入一个同分区(对物化视图而言),但日期不同的数据。对比查询源表结果,与物化视图的结果有什么区别
-- 源表插入数据
INSERT INTO test.tb_mtview_counter
SELECTtoDateTime('2017-01-02 00:00:00') + toInt64(number/10) AS create_time,(number % 10) + 1 AS device,(device * 3) +  (number/10000) + (rand() % 53) * 0.1 AS value
FROM system.numbers LIMIT 100000;-- 查询物化视图
SELECTdevice, day, count, sum, max, min, avg
FROM test.tb_mtview_counter_daily_mv;
  • 结果如下:
    在这里插入图片描述
┌─device─┬────────day─┬─count─┬────────────────sum─┬─────max─┬─────min─┬────────────────avg─┐
│      12015-01-0110000105885.8000040054318.1663.02110.588580000400544 │
│      22015-01-0110000136100.1999921798721.03616.039113.610019999217988 │
│      32015-01-0110000166083.2999429702824.09429.043216.608329994297026 │
│      42015-01-0110000196134.9000501632727.178312.036319.613490005016327 │
│      52015-01-0110000225946.5999670028730.199415.158422.594659996700287 │
│      62015-01-0110000256283.3999137878433.190518.013525.628339991378784 │
│      72015-01-0110000286232.7000560760535.991621.006628.623270005607605 │
│      82015-01-0110000315966.500055313139.153724.057731.596650005531313 │
│      92015-01-0110000345799.699810028142.138827.053834.579969981002805 │
│     102015-01-0110000375820.099843978945.150930.077937.582009984397885 │
└────────┴────────────┴───────┴────────────────────┴─────────┴─────────┴────────────────────┘
┌─device─┬────────day─┬─count─┬────────────────sum─┬─────max─┬─────min─┬────────────────avg─┐
│      12017-01-0110000106250.9000189304418.1533.12210.625090001893044 │
│      22017-01-0110000135986.0999360084521.18516.105113.598609993600846 │
│      32017-01-0110000165927.1999521255524.18629.203216.592719995212555 │
│      42017-01-0110000195965.700024604827.174312.019319.59657000246048 │
│      52017-01-0110000226203.9000482559230.122415.005422.62039000482559 │
│      62017-01-0110000256157.800012588533.191518.002525.61578000125885 │
│      72017-01-0110000285870.000099182136.157621.003628.587000009918214 │
│      82017-01-0110000315986.5999450683639.147724.026731.598659994506836 │
│      92017-01-0110000346104.499822616642.197827.002834.610449982261656 │
│     102017-01-0110000376108.500066757245.180930.005937.61085000667572 │
└────────┴────────────┴───────┴────────────────────┴─────────┴─────────┴────────────────────┘
┌─device─┬────────day─┬─count─┬────────────────sum─┬─────max─┬─────min─┬────────────────avg─┐
│      12017-01-0110000106321.7000019550318.1963.00710.632170000195503 │
│      22017-01-0110000135868.999980926521.15016.004113.586899998092651 │
│      32017-01-0110000165784.1999101638824.15729.055216.57841999101639 │
│      42017-01-0110000196065.5000419616727.188312.076319.606550004196166 │
│      52017-01-0110000225919.3999738693230.137415.028422.591939997386934 │
│      62017-01-0110000256105.4000186920233.186518.085525.6105400018692 │
│      72017-01-0110000286283.8001251220736.198621.099628.628380012512206 │
│      82017-01-0110000316006.700065612839.121724.016731.60067000656128 │
│      92017-01-0110000345796.39997482342.177827.026834.5796399974823 │
│     102017-01-0110000376074.899896621745.162930.064937.60748998966217 │
└────────┴────────────┴───────┴────────────────────┴─────────┴─────────┴────────────────────┘

数据暂时未合并,可以执行 optimize 来手动进行合并(就算不手动执行,未来某个时刻也会自动合并)

optimize table test.tb_mtview_counter_daily_mv final;
  • 合并后查询结果:
┌─device─┬────────day─┬─count─┬────────────────sum─┬─────max─┬─────min─┬────────────────avg─┐
│      12015-01-0110000105885.8000040054318.1663.02110.588580000400544 │
│      22015-01-0110000136100.1999921798721.03616.039113.610019999217988 │
│      32015-01-0110000166083.2999429702824.09429.043216.608329994297026 │
│      42015-01-0110000196134.9000501632727.178312.036319.613490005016327 │
│      52015-01-0110000225946.5999670028730.199415.158422.594659996700287 │
│      62015-01-0110000256283.3999137878433.190518.013525.628339991378784 │
│      72015-01-0110000286232.7000560760535.991621.006628.623270005607605 │
│      82015-01-0110000315966.500055313139.153724.057731.596650005531313 │
│      92015-01-0110000345799.699810028142.138827.053834.579969981002805 │
│     102015-01-0110000375820.099843978945.150930.077937.582009984397885 │
└────────┴────────────┴───────┴────────────────────┴─────────┴─────────┴────────────────────┘
┌─device─┬────────day─┬─count─┬────────────────sum─┬───────max─┬───────min─┬────────────────avg─┐
│      12017-01-0120000212572.6000208854736.3496.128999721.257260002088547 │
│      22017-01-0120000271855.0999169349742.335212.109227.185509991693497 │
│      32017-01-0120000331711.399862289448.343418.258433.171139986228944 │
│      42017-01-0120000392031.2000665664754.362624.095639.20312000665665 │
│      52017-01-0120000452123.3000221252460.259830.033845.212330002212525 │
│      62017-01-0120000512263.200031280566.37800636.08851.22632000312805 │
│      72017-01-0120000572153.800224304272.356242.103257.21538002243042 │
│      82017-01-0120000631993.300010681278.269448.043463.19933000106812 │
│      92017-01-0120000691900.899797439684.37559554.02960269.19008997974396 │
│     102017-01-0120000752183.399963378990.34379660.070875.21833999633789 │
└────────┴────────────┴───────┴────────────────────┴───────────┴───────────┴────────────────────┘

从结果可以看到,分区数据执行了合并,而且合并是依据order by字段进行的,除了device和day以外的字段全部自动求和了,这个就是SummingMergeTree的特性。

物化视图—方式2:

在前一种物化视图中,我们看到,SummingMergeTree()对常规的除了求和以外的聚合函数支持并不好,在本例中,我们采用 聚合函数名称加-State后缀 的函数形式来获取聚合值,通过例子来了解具体是怎么一回事。

官网介绍:以-State后缀的函数总是返回AggregateFunction类型的数据的中间状态。对于SELECT而言AggregateFunction类型总是以特定的二进制形式展现在所有的输出格式中。
参考:https://clickhouse.com/docs/zh/sql-reference/data-types/aggregatefunction

-- 没有to table时必须有engine
CREATE MATERIALIZED VIEW test.tb_mtview_counter_daily_mv2
ENGINE = SummingMergeTree()
PARTITION BY toYYYYMM(day) ORDER BY (device, day)
AS SELECTtoStartOfMonth(create_time) as day,device,count(*) as count,sum(value) as sum,maxState(value) AS max_value_state,minState(value) AS min_value_state,avgState(value) AS avg_value_state
FROM test.tb_mtview_counter
WHERE create_time >= toDate('2016-01-01 00:00:00')
GROUP BY device, day
ORDER BY device, day;
  • 源表新增数据
INSERT INTO test.tb_mtview_counter
SELECTtoDateTime('2016-01-01 00:00:00') + toInt64(number/10) AS create_time,(number % 10) + 1 AS device,(device * 3) +  (number/10000) + (rand() % 53) * 0.1 AS value
FROM system.numbers LIMIT 100000;-- 查询源表
SELECTtoStartOfMonth(create_time) as day,device,count(*) as count,sum(value) as sum,max(value) as max,min(value) as min,avg(value) as avg
from test.tb_mtview_counter 
GROUP BY device, day
ORDER BY day, device;-- 查询物化视图
SELECTday, device, sum(count) AS count,sum(sum) as sum, maxMerge(max_value_state) AS max,minMerge(min_value_state) AS min,avgMerge(avg_value_state) AS avg
FROM test.tb_mtview_counter_daily_mv2
GROUP BY device, day
ORDER BY day, device;-- 显示当前数据库所有表
show tables from test;
  • 二者结果均为:
┌────────day─┬─device─┬─count─┬────────────────sum─┬─────max─┬─────min─┬────────────────avg─┐
│ 2016-01-01110000105755.7999913692518.113.00410.575579999136925 │
│ 2016-01-01210000136052.3999481201221.19716.041113.605239994812012 │
│ 2016-01-01310000165888.8000183105524.19529.001216.588880001831054 │
│ 2016-01-01410000196001.4000816345227.156312.022319.600140008163454 │
│ 2016-01-01510000225971.7999095916730.092415.007422.597179990959166 │
│ 2016-01-01610000256052.8000469207833.168518.075525.605280004692077 │
│ 2016-01-01710000286267.60006904636.166621.009628.6267600069046 │
│ 2016-01-01810000315901.299957275439.111724.054731.59012999572754 │
│ 2016-01-01910000345774.4999389648442.167827.134834.57744999389649 │
│ 2016-01-011010000375901.9999046325745.145930.000937.590199990463255 │
└────────────┴────────┴───────┴────────────────────┴─────────┴─────────┴────────────────────┘

为什么查询物化视图 还要GROUP BY +sum ?

  • 由于SummingMergeTree引擎是异步的(这节省了资源并减少了对查询处理的影响),所以某些值可能尚未被计算,我们仍然需要在此使用 GROUP BY 。
在使用 MV 的聚合引擎时,也需要按照聚合查询 配合 GROUP BY 来写sql,因为聚合时机不可控

物化视图—方式3:

  1. 一般需要先建一张表
  2. 再采用使用to db.table方法,将物化视图的实体表指定为特定名称的表。
-- 先建一张表
CREATE TABLE test.tb_mtview_counter_daily (day Date,device UInt32,count UInt64,sum Float64,max_value_state AggregateFunction(max, Float32),min_value_state AggregateFunction(min, Float32),avg_value_state AggregateFunction(avg, Float32)
)
ENGINE = SummingMergeTree()
PARTITION BY toYYYYMM(day)
ORDER BY (device, day);-- 将物化视图的实体表指定为特定名称的表
CREATE MATERIALIZED VIEW test.tb_mtview_counter_daily_mv3
TO test.tb_mtview_counter_daily
AS SELECTtoStartOfMonth(create_time) as day,device,count(*) as count,sum(value) as sum,maxState(value) AS max_value_state,minState(value) AS min_value_state,avgState(value) AS avg_value_state
FROM test.tb_mtview_counter
WHERE create_time >= toDate('2019-01-01 00:00:00')
GROUP BY device, day
ORDER BY device, day;
  • 向源表导入数据
INSERT INTO test.tb_mtview_counter
SELECTtoDateTime('2020-01-01 00:00:00') + toInt64(number/10) AS create_time,(number % 10) + 1 AS device,(device * 3) +  (number/10000) + (rand() % 53) * 0.1 AS value
FROM system.numbers LIMIT 10000;
-- 查询物化视图实体表
SELECTdevice, day, sum(count) AS count,sum(sum) as sum, maxMerge(max_value_state) AS max,minMerge(min_value_state) AS min,avgMerge(avg_value_state) AS avg
FROM test.tb_mtview_counter_daily
GROUP BY device, day
ORDER BY day, device;-- 查询物化视图
SELECTdevice, day, sum(count) AS count,sum(sum) as sum, maxMerge(max_value_state) AS max,minMerge(min_value_state) AS min,avgMerge(avg_value_state) AS avg
FROM test.tb_mtview_counter_daily_mv3
GROUP BY device, day
ORDER BY day, device;

结果二者查询的结果一致:

┌─device─┬────────day─┬─count─┬────────────────sum─┬─────max─┬─────min─┬────────────────avg─┐
│      12020-01-0110006121.6999993324289.1653.1116.121699999332428 │
│      22020-01-0110009138.19999694824212.17416.00419.138199996948241 │
│      32020-01-01100012132.70000171661415.18629.014212.132700001716614 │
│      42020-01-01100015055.10000228881818.176312.016315.055100002288818 │
│      52020-01-01100018032.10001277923621.145415.052418.032100012779235 │
│      62020-01-01100021153.60003852844224.172518.126521.15360003852844 │
│      72020-01-01100024141.30000495910627.123621.119624.141300004959106 │
│      82020-01-01100027071.19997596740730.115724.098727.071199975967406 │
│      92020-01-01100030123.69999313354533.141827.052830.123699993133545 │
│     102020-01-01100033096.89997863769536.094930.050933.096899978637694 │
└────────┴────────────┴───────┴────────────────────┴─────────┴─────────┴────────────────────┘

物化视图— 案例2

1.原始数据表----以Wikistat的10亿行数据集为例:

CREATE TABLE wikistat
(`time` DateTime CODEC(Delta(4), ZSTD(1)),`project` LowCardinality(String),`subproject` LowCardinality(String),`path` String,`hits` UInt64
)
ENGINE = MergeTree
ORDER BY (path, time);Ok.INSERT INTO wikistat SELECT *
FROM s3('https://ClickHouse-public-datasets.s3.amazonaws.com/wikistat/partitioned/wikistat*.native.zst') LIMIT 1e9
  • 假设我们经常查询某个日期最受欢迎的项目:
SELECTproject,sum(hits) AS h
FROM wikistat
WHERE date(time) = '2015-05-01'
GROUP BY project
ORDER BY h DESC
LIMIT 10
  • 这个查询在测试实例上需要15秒来完成:
┌─project─┬────────h─┐
│ en      │ 34521803 │
│ es      │  4491590 │
│ de      │  4490097 │
│ fr      │  3390573 │
│ it      │  2015989 │
│ ja      │  1379148 │
│ pt      │  1259443 │
│ tr      │  1254182 │
│ zh      │   988780 │
│ pl      │   985607 │
└─────────┴──────────┘10 rows in set. Elapsed: 14.869 sec. Processed 972.80 million rows, 10.53 GB (65.43 million rows/s., 708.05 MB/s.)

2.创建物化视图

CREATE TABLE wikistat_top_projects
(`date` Date,`project` LowCardinality(String),`hits` UInt32
)
ENGINE = SummingMergeTree
ORDER BY (date, project);Ok.CREATE MATERIALIZED VIEW wikistat_top_projects_mv TO wikistat_top_projects AS
SELECTdate(time) AS date,project,sum(hits) AS hits
FROM wikistat
GROUP BYdate,project;

在这两个查询中:

  • wikistat_top_projects 是我们要用来保存物化视图的表的名称,
  • wikistat_top_projects_mv 是物化视图本身(触发器)的名称,
  • 我们使用了SummingMergeTree表引擎,因为我们希望为每个date/project汇总hits值,
  • AS 后面的内容是构建物化视图的查询。

我们可以创建任意数量的物化视图,但每一个新的物化视图都是额外的存储负担,因此保持总数合理,即每个表下的物化视图数目控制在10个以内。

3.填充数据

现在,我们使用与 wikistat 表相同的查询来填充物化视图的目标表:

INSERT INTO wikistat_top_projects SELECTdate(time) AS date,project,sum(hits) AS hits
FROM wikistat
GROUP BYdate,project

请注意,这只花费了ClickHouse 3ms来产生相同的结果,而原始查询则花费了15秒

4.测试查询 物化视图

由于 wikistat_top_projects 是一个表,我们可以利用ClickHouse的SQL功能进行查询:

SELECTproject,sum(hits) hits
FROM wikistat_top_projects
WHERE date = '2015-05-01'
GROUP BY project
ORDER BY hits DESC
LIMIT 10┌─project─┬─────hits─┐
│ en      │ 34521803 │
│ es      │  4491590 │
│ de      │  4490097 │
│ fr      │  3390573 │
│ it      │  2015989 │
│ ja      │  1379148 │
│ pt      │  1259443 │
│ tr      │  1254182 │
│ zh      │   988780 │
│ pl      │   985607 │
└─────────┴──────────┘10 rows in set. Elapsed: 0.003 sec. Processed 8.19 thousand rows, 101.81 KB (2.83 million rows/s., 35.20 MB/s.)

请注意,这只花费了ClickHouse 3ms来产生相同的结果,而原始查询则花费了15秒

为什么查询物化视图 还要GROUP BY +sum ?

  • 由于SummingMergeTree引擎是异步的(这节省了资源并减少了对查询处理的影响),所以某些值可能尚未被计算,我们仍然需要在此使用 GROUP BY 。
在使用 MV 的聚合引擎时,也需要按照聚合查询 配合 GROUP BY 来写sql,因为聚合时机不可控

5.更新物化视图中的数据

  • 物化视图的最强大的特点是当向源表插入数据时,目标表中的数据会使用 SELECT 语句自动更新:
    在这里插入图片描述
  • 因此,我们不需要额外地刷新物化视图中的数据 - ClickHouse会自动完成一切操作。假设我们向 wikistat 表插入新数据:
INSERT INTO wikistat
VALUES(now(), 'test', '', '', 10),(now(), 'test', '', '', 10),(now(), 'test', '', '', 20),(now(), 'test', '', '', 30);

现在,让我们查询物化视图的目标表,以验证 hits 列是否已正确汇总。我们使用FINAL修饰符以确保SummingMergeTree引擎返回汇总的hits,而不是单个、未合并的行:

SELECT hits
FROM wikistat_top_projects
FINAL
WHERE (project = 'test') AND (date = date(now()))┌─hits─┐
│   70 │
└──────┘1 row in set. Elapsed: 0.005 sec. Processed 7.15 thousand rows, 89.37 KB (1.37 million rows/s., 17.13 MB/s.)
  • 在生产环境中,避免在大表上使用 FINAL ,并始终优先使用 sum(hits)。还请检查optimize_on_insert参数设置,该选项控制如何合并插入的数据。
在使用 MV 的聚合引擎时,也需要按照聚合查询 配合 GROUP BY 来写sql,因为聚合时机不可控

6.使用物化视图加速聚合(AggregatingMergeTree引擎)

  • 如前一节所示,物化视图是一种提高查询性能的方法。对于分析查询,常见的聚合操作不仅仅是前面示例中展示的 sum() 。SummingMergeTree非常适用于计算汇总数据,但还有更高级的聚合可以使用AggregatingMergeTree引擎进行计算。

假设我们经常执行以下类型的查询:

SELECTtoDate(time) AS date,min(hits) AS min_hits_per_hour,max(hits) AS max_hits_per_hour,avg(hits) AS avg_hits_per_hour
FROM wikistat
WHERE project = 'en'
GROUP BY date
  • 这为我们提供了给定项目的每日点击量的月最小值、最大值和平均值:
┌───────date─┬─min_hits_per_hour─┬─max_hits_per_hour─┬──avg_hits_per_hour─┐
│ 2015-05-011368024.586310181621408 │
│ 2015-05-021233314.241388590780171 │
│ 2015-05-031246784.317835245126423...
└────────────┴───────────────────┴───────────────────┴────────────────────┘38 rows in set. Elapsed: 8.970 sec. Processed 994.11 million rows

在这里插入图片描述

  • 在我们的示例中,我们将使用 min 、 max 和 avg 状态。在新物化视图的目标表中,我们将使用AggregateFunction 类型存储聚合状态而不是值
CREATE TABLE wikistat_daily_summary
(`project` String,`date` Date,`min_hits_per_hour` AggregateFunction(min, UInt64),`max_hits_per_hour` AggregateFunction(max, UInt64),`avg_hits_per_hour` AggregateFunction(avg, UInt64)
)
ENGINE = AggregatingMergeTree
ORDER BY (project, date);Ok.CREATE MATERIALIZED VIEW wikistat_daily_summary_mv
TO wikistat_daily_summary AS
SELECTproject,toDate(time) AS date,minState(hits) AS min_hits_per_hour,maxState(hits) AS max_hits_per_hour,avgState(hits) AS avg_hits_per_hour
FROM wikistat
GROUP BY project, date
  • 现在,让我们为它填充数据:
INSERT INTO wikistat_daily_summary SELECTproject,toDate(time) AS date,minState(hits) AS min_hits_per_hour,maxState(hits) AS max_hits_per_hour,avgState(hits) AS avg_hits_per_hour
FROM wikistat
GROUP BY project, date0 rows in set. Elapsed: 33.685 sec. Processed 994.11 million rows
  • 在查询时,我们使用相应的 Merge 组合器来检索值:
在使用 MV 的聚合引擎时,也需要按照聚合查询 配合 GROUP BY 来写sql,因为聚合时机不可控
SELECTdate,minMerge(min_hits_per_hour) min_hits_per_hour,maxMerge(max_hits_per_hour) max_hits_per_hour,avgMerge(avg_hits_per_hour) avg_hits_per_hour
FROM wikistat_daily_summary
WHERE project = 'en'
GROUP BY date

请注意,我们得到的结果完全相同,但速度快了数千倍:

┌───────date─┬─min_hits_per_hour─┬─max_hits_per_hour─┬──avg_hits_per_hour─┐
│ 2015-05-011368024.586310181621408 │
│ 2015-05-021233314.241388590780171 │
│ 2015-05-031246784.317835245126423...
└────────────┴───────────────────┴───────────────────┴────────────────────┘32 rows in set. Elapsed: 0.005 sec. Processed 9.54 thousand rows, 1.14 MB (1.76 million rows/s., 209.01 MB/s.)

任何聚合函数都可以作为一个聚合物化视图的一部分与State/Merge组合器一起使用。

7.验证和过滤数据

使用物化视图的另一个流行的示例是在插入后立即处理数据。数据验证就是一个很好的例子。
在这里插入图片描述
假设我们想要滤掉所有包含不需要的符号的path,再保存到结果表中。我们的表中有大约1%这样的值:

SELECT count(*)
FROM wikistat
WHERE NOT match(path, '[a-z0-9\\-]')
LIMIT 5┌──count()─┐
│ 12168918 │
└──────────┘1 row in set. Elapsed: 46.324 sec. Processed 994.11 million rows, 28.01 GB (21.46 million rows/s., 604.62 MB/s.)

为了实现验证过滤,我们需要两个表 - 一个带有所有数据的表和一个只带有干净数据的表。

  1. 物化视图的目标表将扮演一个只带有干净数据的最终表的角色,
  2. 源表将是暂时的。我们可以根据TTL从源表中删除数据,就像我们在上一节中所做的那样,或者将此表的引擎更改为Null,该引擎不存储任何数据(数据只会存储在物化视图中):
CREATE TABLE wikistat_src
(`time` DateTime,`project` LowCardinality(String),`subproject` LowCardinality(String),`path` String,`hits` UInt64
)
ENGINE = Null

现在,让我们使用数据验证查询创建一个物化视图:

CREATE TABLE wikistat_clean AS wikistat;Ok.CREATE MATERIALIZED VIEW wikistat_clean_mv TO wikistat_clean
AS SELECT *
FROM wikistat_src
WHERE match(path, '[a-z0-9\\-]')

当我们插入数据时, wikistat_src 将保持为空:

INSERT INTO wikistat_src SELECT * FROM s3('https://ClickHouse-public-datasets.s3.amazonaws.com/wikistat/partitioned/wikistat*.native.zst') LIMIT 1000

在这里插入图片描述

8.数据路由到表格

物化视图可以用于的另一个示例是基于某些条件将数据路由到不同的表:
在这里插入图片描述
例如,我们可能希望将无效数据路由到另一个表,而不是删除它。在这种情况下,我们创建另一个物化视图,但使用不同的查询:

CREATE TABLE wikistat_invalid AS wikistat;Ok.CREATE MATERIALIZED VIEW wikistat_invalid_mv TO wikistat_invalid
AS SELECT *
FROM wikistat_src
WHERE NOT match(path, '[a-z0-9\\-]')

当我们有单个物化视图用于同一源表时,它们将按字母顺序进行处理。请记住,不要为源表创建超过几十个物化视图,因为插入性能可能会下降。

如果我们再次插入相同的数据,我们会在 wikistat_invalid 物化视图中找到942个无效的行:

SELECT count(*)
FROM wikistat_invalid┌─count()─┐
│     942 │
└─────────┘

9.数据转换

由于物化视图基于查询的结果,所以我们可以在SQL中使用所有ClickHouse函数的功能来转换源值,以丰富和提升数据的清晰度。作为一个快速的例子,让我们将project、subproject和path列合并到一个单一的page列,并将时间分割为date和hour列:

CREATE TABLE wikistat_human
(`date` Date,`hour` UInt8,`page` String
)
ENGINE = MergeTree
ORDER BY (page, date);Ok.CREATE MATERIALIZED VIEW wikistat_human_mv TO wikistat_human
AS SELECTdate(time) AS date,toHour(time) AS hour,concat(project, if(subproject != '', '/', ''), subproject, '/', path) AS page,hits
FROM wikistat

现在, wikistat_human 将填充转换后的数据:

┌───────date─┬─hour─┬─page──────────────────────────┬─hits─┐
│ 2015-11-088 │ en/m/Angel_Muñoz_(politician)1 │
│ 2015-11-093 │ en/m/Angel_Muñoz_(politician)1 │
└────────────┴──────┴───────────────────────────────┴──────┘

10.物化视图和JOIN操作

由于物化视图是基于SQL查询的结果工作的,我们可以使用JOIN操作以及任何其他SQL功能。但是应该小心使用JOIN操作。

假设我们有一个带有页面标题的表:

CREATE TABLE wikistat_titles
(`path` String,`title` String
)
ENGINE = MergeTree
ORDER BY path

这个表中的title与path关联:

SELECT *
FROM wikistat_titles┌─path─────────┬─title────────────────┐
│ Ana_Sayfa    │ Ana Sayfa - artist   │
│ Bruce_Jenner │ William Bruce Jenner │
└──────────────┴──────────────────────┘

现在我们可以创建一个物化视图,从 wikistat_titles 表中通过joinpath值连接title:

CREATE TABLE wikistat_with_titles
(`time` DateTime,`path` String,`title` String,`hits` UInt64
)
ENGINE = MergeTree
ORDER BY (path, time);Ok.CREATE MATERIALIZED VIEW wikistat_with_titles_mv TO wikistat_with_titles
AS SELECT time, path, title, hits
FROM wikistat AS w
INNER JOIN wikistat_titles AS wt ON w.path = wt.path

注意,我们使用了 INNER JOIN ,所以在填充后,我们只会得到在 wikistat_titles 表中有对应值的记录:

SELECT * FROM wikistat_with_titles LIMIT 5┌────────────────time─┬─path──────┬─title──────────────┬─hits─┐
│ 2015-05-01 01:00:00 │ Ana_Sayfa │ Ana Sayfa - artist │    5 │
│ 2015-05-01 01:00:00 │ Ana_Sayfa │ Ana Sayfa - artist │    7 │
│ 2015-05-01 01:00:00 │ Ana_Sayfa │ Ana Sayfa - artist │    1 │
│ 2015-05-01 01:00:00 │ Ana_Sayfa │ Ana Sayfa - artist │    3 │
│ 2015-05-01 01:00:00 │ Ana_Sayfa │ Ana Sayfa - artist │  653 │
└─────────────────────┴───────────┴────────────────────┴──────┘

我们在 wikistat 表中插入一个新记录,看看我们的新物化视图是如何工作的:

INSERT INTO wikistat VALUES(now(), 'en', '', 'Ana_Sayfa', 123);1 row in set. Elapsed: 1.538 sec.

注意这里的插入时间 - 1.538秒。我们可以在 wikistat_with_titles 中看到我们的新行:

SELECT *
FROM wikistat_with_titles
ORDER BY time DESC
LIMIT 3┌────────────────time─┬─path─────────┬─title────────────────┬─hits─┐
│ 2023-01-03 08:43:14 │ Ana_Sayfa    │ Ana Sayfa - artist   │  123 │
│ 2015-06-30 23:00:00 │ Bruce_Jenner │ William Bruce Jenner │  115 │
│ 2015-06-30 23:00:00 │ Bruce_Jenner │ William Bruce Jenner │   55 │
└─────────────────────┴──────────────┴──────────────────────┴──────┘

但是,如果我们向 wikistat_titles 表添加数据会发生什么呢?:

INSERT INTO wikistat_titles
VALUES('Academy_Awards', 'Oscar academy awards');

尽管我们在 wikistat 表中有相应的值,但物化视图中不会出现任何内容:

SELECT *
FROM wikistat_with_titles
WHERE path = 'Academy_Awards'0 rows in set. Elapsed: 0.003 sec.

这是因为物化视图只在其源表接收插入时触发。它只是源表上的一个触发器,对连接表一无所知。注意,这不仅仅适用于join查询,并且在物化视图的SELECT语句中引入任何外部表时都很相关,例如使用 IN SELECT 。

在我们的情况下, wikistat 是物化视图的源表,而 wikistat_titles 是我们要连接的表:
在这里插入图片描述
在这里插入图片描述
要小心,因为JOIN操作可能会在连接大表时显著降低插入性能,如上所示。考虑使用字典作为更有效的替代方法。

ClickHouse博客; https://blog.csdn.net/ClickHouseDB?type=blog

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

面试算法-87-分隔链表

题目 给你一个链表的头节点 head 和一个特定值 x &#xff0c;请你对链表进行分隔&#xff0c;使得所有 小于 x 的节点都出现在 大于或等于 x 的节点之前。 你应当 保留 两个分区中每个节点的初始相对位置。 示例 1&#xff1a; 输入&#xff1a;head [1,4,3,2,5,2], x …

【JAVA】通过JAVA实现用户界面的登录

&#x1f308;个人主页: Aileen_0v0 &#x1f525;热门专栏: 华为鸿蒙系统学习|计算机网络|数据结构与算法|MySQL| ​&#x1f4ab;个人格言:“没有罗马,那就自己创造罗马~” #mermaid-svg-wyCvaz0EBNwHcwsi {font-family:"trebuchet ms",verdana,arial,sans-serif;f…

Linux系统——硬件命令

目录 一.网卡带宽 1.查看网卡速率——ethtool 网卡名 2.查看mac地址——ethtool -P 网卡名 二、内存相关 1.显示系统中内存使用情况——free -h 2.显示内存模块的详细信息——dmidecode -t memory 三、CPU相关 1.查看CPU架构信息——lscpu 2.性能模式 四、其他硬件命…

C语言字节对齐关键字#pragma pack(n)的使用

0 前言 在进行嵌入式开发的过程中&#xff0c;我们经常会见到对齐操作。这些对齐操作有些是为了便于实现指针操作&#xff0c;有些是为了加速对内存的访问。因此&#xff0c;学习如何使用对齐关键字是对于嵌入式开发是很有必要的。 1 对齐规则 1.0 什么叫做对齐 众所周知&a…

牛客NC170 最长不含重复字符的子字符串【高频 中等 map、滑动窗口 Java,Go,PHP】

题目 题目链接&#xff1a; https://www.nowcoder.com/practice/48d2ff79b8564c40a50fa79f9d5fa9c7 思路 用一个hashmap记录每个字母的index如果这个字母已经在map里了说明已经有重复了这样就更新看这个字母上次出现的index需要注意的是这种情况&#xff1a;“bacbca”这里的a…

PCB中常用电子器件封装学习——【一网打尽】

‘ 上图是这个世界上大概所有的封装种类&#xff0c;当然我们日常硬件电路设计肯定用不到这么多&#xff0c;接下来我将介绍几种工程上常用的封装&#xff0c;配以图片方便大家理解学习。在电子器件选型的时候&#xff0c;避免选择到一些非常难以焊接的封装电子器件。

使用amd架构的计算机部署其他架构的虚拟机(如:arm)

1 下载quem模拟器 https://qemu.weilnetz.de/w64/2 QEMU UEFI固件文件下载(引导文件) 推荐使用&#xff1a;https://releases.linaro.org/components/kernel/uefi-linaro/latest/release/qemu64/QEMU_EFI.fd3 QEMU 安装 安装完成之后&#xff0c;需要将安装目录添加到环境变…

Jenkins的快速入门

文章目录 一、Jenkins是什么&#xff1f;二、Jenkins安装和持续集成环境配置1.持续集成流程说明2.Gitlab代码托管服务器安装Gitlab简介&#xff1a;Gitlab安装Gitlab的使用切换中文添加组创建用户将用户添加到组创建项目idea中代码上传Gitlab 3.Jenkins持续集成环境服务器安装J…

华为北向网管NCE开发教程(5)打包org.omg.CosNotification找不到

1问题描述 在IDE中&#xff0c;代码能正常运行&#xff0c;但是打包的时候&#xff0c;会抱不到一些类 2问题原因 导入的本地包中&#xff0c;能在IDE中找到&#xff0c;但是在使用maven打包时&#xff0c;maven找不到这些依赖包 3解决办法 将依赖包通过maven安装到maven…

通过 Socket 手动实现 HTTP 协议

你好&#xff0c;我是 shengjk1&#xff0c;多年大厂经验&#xff0c;努力构建 通俗易懂的、好玩的编程语言教程。 欢迎关注&#xff01;你会有如下收益&#xff1a; 了解大厂经验拥有和大厂相匹配的技术等 希望看什么&#xff0c;评论或者私信告诉我&#xff01; 文章目录 一…

[Python人工智能] 四十四.命名实体识别 (5)利用bert4keras构建Bert-CRF实体识别模型(实体位置)

从本专栏开始,作者正式研究Python深度学习、神经网络及人工智能相关知识。前文讲解如何实现中文命名实体识别研究,构建BiGRU-CRF模型实现。这篇文章将继续以中文语料为主,介绍融合Bert的实体识别研究,使用bert4keras和kears包来构建Bert+BiLSTM-CRF模型。然而,该代码最终结…

红外遥控器的使用和详细解释

infrared.c #include "infrared.h"/* 红外 --- PA8*/void Infrared_Init(void) {GPIO_InitTypeDef GPIO_InitStruct; EXTI_InitTypeDef EXTI_InitStruct;NVIC_InitTypeDef NVIC_InitStruct;//使能SYSCFG时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, E…

34.网络游戏逆向分析与漏洞攻防-游戏网络通信数据解析-登录数据包的监视与模拟

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 如果看不懂、不知道现在做的什么&#xff0c;那就跟着做完看效果 内容参考于&#xff1a;易道云信息技术研究院VIP课 上一个内容&#xff1a;33.游戏登录数据…

c#绘制图形

窗体工具控件 如果选纹理 ,需要在ImageList中选择图像(点击添加选择图片路径) using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Drawing.Drawing2D; using System.Linq; using System.…

第二十章 TypeScript(webpack构建ts+vue3项目)

构建项目目录 src-- main.ts-- App.vue--shim.d.tswebpack.config.jsindex.htmlpackage.jsontsconfig.json 基础构建 npm install webpack -D npm install webpack-dev-server -D npm install webpack-cli -D package.json 添加打包命令和 启动服务的命令 {"scripts…

Spring相关框架八股

单例bean是线程安全的吗&#xff1f; AOP 事务失效 Bean生命周期 Bean循环依赖解决 MVC执行流程 自动装配原理 Spring常见注解 SpringMVC注解 SpringBoot注解 MyBatis执行流程 MyBatis延迟加载 MyBatis缓存 SpringCloud五大组件 注册中心Nacos、Eureka 负载均衡Ribbon 服务雪崩…

Apache HBase(二)

一、Apache HBase 1、HBase Shell操作 先启动HBase。再进行下面命令行操作。 1、进入HBase客户端命令行 [rootnode1 hbase-3.0.0]# bin/hbase shell SLF4J: Class path contains multiple SLF4J bindings. SLF4J: Found binding in [jar:file:/export/server/hadoop-3.3.6/…

[Java基础揉碎]单例模式

目录 什么是设计模式 什么是单例模式 饿汉式与懒汉式 饿汉式vs懒汉式 懒汉式存在线程安全问题 什么是设计模式 1.静态方法和属性的经典使用 2.设计模式是在大量的实践中总结和理论化之后优选的代码结构、编程风格、 以及解决问题的思考方式。设计模式就像是经典的棋谱&am…

网络分类简述与数据链路层协议(PPP)

实验拓扑 实验要求 1、R1和R2使用PPP链路直连&#xff0c;R2和R3把2条PPP链路捆绑为PPP MP直连按照图示配置IP地址 2、R2对R1的PPP进行单向chap验证 3、R2和R3的PPP进行双向chap验证 实验思路 给R1、R2的S3/0/0接口配置IP地址&#xff0c;已给出网段192.168.1.0/24R2作为主…

Python 潮流周刊#43:在开源与家庭之间,他选择了家庭

△△请给“Python猫”加星标 &#xff0c;以免错过文章推送 你好&#xff0c;我是猫哥。这里每周分享优质的 Python、AI 及通用技术内容&#xff0c;大部分为英文。本周刊开源&#xff0c;欢迎投稿[1]。另有电报频道[2]作为副刊&#xff0c;补充发布更加丰富的资讯&#xff0c;…