深入分析 Flink SQL 工作机制

摘要:本文整理自 Flink Forward 2020 全球在线会议中文精华版,由 Apache Flink PMC 伍翀(云邪)分享,社区志愿者陈婧敏(清樾)整理。旨在帮助大家更好地理解 Flink SQL 引擎的工作原理。文章主要分为以下四部分:

  1. Flink SQL Architecture
  2. How Flink SQL Works?
  3. Flink SQL Optimizations
  4. Summary and Futures

Tips:点击下方链接可查看作者分享的原版视频~
https://ververica.cn/developers/flink-forward-virtual-conference/

Apache Flink 社区在最近的两个版本(1.9 & 1.10 )中为面向未来的统一流批处理在架构层面做了很多优化,其中一个重大改造是引入了 Blink Planner,开始支持 SQL & Table API 使用不同的 SQL Planner 进行编译(Planner 的插件化)。

本文首先会介绍推动这些优化背后的思考,展示统一的架构如何更好地处理流式和批式查询,其次将深入剖析 Flink SQL 的编译及优化过程,包括:

  1. Flink SQL 利用 Apache Calcite 将 SQL 翻译为关系代数表达式,使用表达式折叠(Expression Reduce),下推优化(Predicate / Projection Pushdown )等优化技术生成物理执行计划(Physical Plan),利用 Codegen 技术生成高效执行代码。
  2. Flink SQL 使用高效的二进制数据存储结构 BinaryRow 加速计算性能;使用 Mini-batch 攒批提高吞吐,降低两层聚合时由 Retraction 引起的数据抖动;聚合场景下数据倾斜处理和 Top-N 排序的优化原理。

## Flink SQL 架构 & Blink Planner(1.9+ )

1.1 Old Planner 的限制

要想了解 Flink SQL 在1.9 版本引入新架构的动机,我们首先看下 1.9 版本之前的架构设计。

1 old-arch.gif

从图中可以看出,虽然面向用户的 Table API & SQL 是统一的,但是流式和批式任务在翻译层分别对应了 DataStreamAPI 和 DataSetAPI,在 Runtime 层面也要根据不同的 API 获取执行计划,两层的设计使得整个架构能够复用的模块有限,不易扩展。

1.2 统一的 Blink Planner

Flink 在设计之初就遵循“批是流的特例”的理念,在架构上做到流批统一是大势所趋。在社区和阿里巴巴的共同努力下,1.9 版本引入了新的 Blink Planner,将批 SQL 处理作为流 SQL 处理的特例,尽量对通用的处理和优化逻辑进行抽象和复用,通过 Flink 内部的 Stream Transformation API 实现流 & 批的统一处理,替代原 Flink Planner 将流 & 批区分处理的方式。

此外,新架构通过灵活的插件化方式兼容老版本 Planner,用户可自行选择。不过在 1.11 版本 Blink Planner 会代替 Old Planner 成为默认的 Planner 来支持流 & 批进一步融合统一( Old Planner 将在之后逐步退出历史舞台)。

2 future-arch.gif

Flink SQL 工作流

Flink SQL 引擎的工作流总结如图所示。

3 sql-engine-internal.png

从图中可以看出,一段查询 SQL / 使用TableAPI 编写的程序(以下简称 TableAPI 代码)从输入到编译为可执行的 JobGraph 主要经历如下几个阶段

  1. 将 SQL文本 / TableAPI 代码转化为逻辑执行计划(Logical Plan)
  2. Logical Plan 通过优化器优化为物理执行计划(Physical Plan)
  3. 通过代码生成技术生成 Transformations 后进一步编译为可执行的 JobGraph 提交运行

本节将重点对 Flink SQL 优化器的常用优化方法和 CodeGen 生成 Transformations 进行介绍。

2.1 Logical Planning

Flink SQL 引擎使用 Apache Calcite SQL Parser 将 SQL 文本解析为词法树,SQL Validator 获取 Catalog 中元数据的信息进行语法分析和验证,转化为关系代数表达式(RelNode),再由 Optimizer 将关系代数表达式转换为初始状态的逻辑执行计划。

备注:TableAPI 代码使用 TableAPI Validator 对接 Catalog 后生成逻辑执行计划。

E.g.1 考虑如下表达 JOIN 操作的一段 SQL。

SELECT 
  t1.id, 1 + 2 + t1.value AS v 
FROM t1, t2 
WHERE 
  t1.id = t2.id AND 
  t2.id < 1000

经过上述操作后得到了一个树状结构的逻辑执行计划,根节点对应最上层的 Select 语句,叶子节点对应输入表 t1 和 t2 的 TableScan 操作,Join 和 Where 条件过滤 分别对应了 Join 和 Filter 节点。

LogicalProject(id=[$0], v=[+(+(1, 2), $1)])
+- LogicalFilter(condition=[AND(=($0, $3), <($3, 1000))])
   +- LogicalJoin(condition=[true], joinType=[inner])
      :- LogicalTableScan(table=[[default_catalog, default, t1]])
      +- LogicalTableScan(table=[[default_catalog, default, t2]])

可视化后如图所示,这是优化器开始工作的初始状态。

4 join-example.png

下面开始介绍 Flink SQL 优化器常见的几种优化方式。

■ 2.1.1 Expression Reduce

表达式(Expression) 是 SQL 中最常见的语法。比如 t1.id 是一个表达式, 1 + 2 + t1.value 也是一个表达式。优化器在优化过程中会递归遍历树上节点,尽可能预计算出每个表达式的值,这个过程就称为表达式折叠。这种转换在逻辑上等价,通过优化后,真正执行时不再需要为每一条记录都计算一遍 1 + 2。

5 expression-reduce.png

■ 2.1.2 PushDown Optimization

下推优化是指在保持关系代数语义不变的前提下将 SQL 语句中的变换操作尽可能下推到靠近数据源的位置以获得更优的性能,常见的下推优化有谓词下推(Predicate Pushdown),投影下推(Projection Pushdown,有时也译作列裁剪)等。

  • Predicate Pushdown

回顾 E.g.1,我们发现 WHERE 条件表达式中 t2.id < 1000 这个过滤条件描述的是对表 t2 的约束,跟表 t1 无关,完全可以下推到 JOIN 操作之前完成。假设表 t2 中有一百万行数据,但是满足 id < 1000 的数据只有 1,000 条,则通过谓词下推优化后到达 JOIN 节点的数据量降低了1,000 倍,极大地节省了 I / O 开销,提升了 JOIN 性能。

谓词下推(Predicate Pushdown)是优化 SQL 查询的一项基本技术,谓词一词来源于数学,指能推导出一个布尔返回值(TRUE / FALSE)的函数或表达式,通过判断布尔值可以进行数据过滤。谓词下推是指保持关系代数语义不变的前提下将 Filter 尽可能移至靠近数据源的位置(比如读取数据的 SCAN 阶段)来降低查询和传递的数据量(记录数)。

6 filter-push-down.png

  • Projection Pushdown

列裁剪是 Projection Pushdown 更直观的描述方式,指在优化过程中去掉没有使用的列来降低 I / O 开销,提升性能。但与谓词下推只移动节点位置不同,投影下推可能会增加节点个数。比如最后计算出的投影组合应该放在 TableScan 操作之上,而 TableScan 节点之上没有 Projection 节点,优化器就会显式地新增 Projection 节点来完成优化。另外如果输入表是基于列式存储的(如 Parquet 或 ORC 等),优化还会继续下推到 Scan 操作中进行。

回顾 E.g.1,我们发现整个查询中只用到了表 t1 的 id 和 value 字段,表 t2 的 id 字段,在 TableScan 节点之上分别增加 Projection 节点去掉多余字段,极大地节省了 I / O 开销。

7 projection-push-down.png

简要总结一下,谓词下推和投影下推分别通过避免处理不必要的记录数和字段数来降低 I / O 开销提升性能。

2.2 Physical Planning on Batch

通过上述一系列操作后,我们得到了优化后的逻辑执行计划。逻辑执行计划描述了执行步骤和每一步需要完成的操作,但没有描述操作的具体实现方式。而物理执行计划会考虑物理实现的特性,生成每一个操作的具体实现方式。比如 Join 是使用 SortMergeJoin、HashJoin 或 BroadcastHashJoin 等。优化器在生成逻辑执行计划时会计算整棵树上每一个节点的 Cost,对于有多种实现方式的节点(比如 Join 节点),优化器会展开所有可能的 Join 方式分别计算。最终整条路径上 Cost 最小的实现方式就被选中成为 Final Physical Plan。

回顾 E.g.1,当它以批模式执行,同时我们可以拿到输入表的 Statistics 信息。在经过前述优化后,表 t2 到达 Join 节点时只有 1,000 条数据,使用 BroadcastJoin 的开销相对最低,则最终的 Physical Plan 如下图所示。

8 batch-physical-plan.png

2.3 Translation & Code Generation

代码生成(Code Generation) 在计算机领域是一种广泛使用的技术。在 Physical Plan 到生成 Transformation Tree 过程中就使用了 Code Generation。

回顾 E.g.1,以 表 t2 之上的 Calc 节点 t2.id < 1000 表达式为例,通过 Code Generation 后生成了描述 Transformation Operator 的一段 Java 代码,将接收到的 Row 中 id < 1000 的 Row 发送到下一个 Operator。

9 code-gen.png

Flink SQL 引擎会将 Physical Plan 通过 Code Generation 翻译为 Transformations,再进一步编译为可执行的 JobGraph。

2.4 Physical Planning on Stream

以上介绍了 Flink SQL 引擎的整体工作流,上述例子是假定以批模式编译的,下面我们来介绍一下以流模式编译时,在生成 Physical Plan 过程中的一个重要机制:Retraction Mechanism (aka. Changelog Mechanism)。

10 physical-planning-stream.png

■ 2.4.1 Retraction Mechanism

Retraction 是流式数据处理中撤回过早下发(Early Firing)数据的一种机制,类似于传统数据库的 Update 操作。级联的聚合等复杂 SQL 中如果没有 Retraction 机制,就会导致最终的计算结果与批处理不同,这也是目前业界很多流计算引擎的缺陷。

E.g.2 考虑如下统计词频分布的 SQL。

SELECT cnt, COUNT(cnt) as freq
FROM (
  SELECT word, COUNT(*) as cnt
  FROM words
  GROUP BY word)
GROUP BY cnt

假设输入数据是:

SQL 表1.jpg

则经过上面的计算后,预期的输出结果应该是:

SQL 表2.jpg

但与批处理不同,流处理的数据是一条条到达的,理论上每一条数据都会触发一次计算,所以在处理了第一个 Hello 和第一个 World 之后,词频为 1 的单词数已经变成了 2,此时再处理第二个 Hello 时,如果不能修正之前的结果,Hello 就会在词频等于 1 和词频等于 2 这两个窗口下被同时统计,显然这个结果是错误的,这就是没有 Retraction 机制带来的问题。

11 physical-planning-retraction-0.gif

Flink SQL 在流计算领域中的一个重大贡献就是首次提出了这个机制的具体实现方案。Retraction 机制又名 Changelog 机制,因为某种程度上 Flink 将输入的流数据看作是数据库的 Changelog,每条输入数据都可以看作是对数据库的一次变更操作,比如 Insert,Delete 或者 Update。以 MySQL 数据库为例,其Binlog 信息以二进制形式存储,其中 Update_rows_log_event 会对应 2 条标记 Before Image (BI) 和 After Image (AI),分别表示某一行在更新前后的信息。

在 Flink SQL 优化器生成流作业的 Physical Plan 时会判断当前节点是否是更新操作,如果是则会同时发出 2 条消息 update_before 和 update_after 到下游节点,update_before 表示之前“错误”下发的数据,需要被撤回,update_after 表示当前下发的“正确”数据。下游收到后,会在结果上先减去 update_before,再加上 update_after。

回顾 E.g.2,下面的动图演示了加入 Retraction 机制后正确结果的计算过程。

12 physical-planning-retraction-1.gif

update_before 是一条非常关键的信息,相当于标记出了导致当前结果不正确的那个“元凶”。不过额外操作会带来额外的开销,有些情况下不需要发送 update_before 也可以获得正确的结果,比如下游节点接的是 UpsertSink(MySQL 或者 HBase的情况下,数据库可以按主键用 update_after 消息覆盖结果)。是否发送 update_before 由优化器决定,用户不需要关心。

■ 2.4.2 Update_before Decision

前面介绍了 Retraction 机制和 update_before,那优化器是怎样决定是否需要发送update_before 呢?本节将介绍这一部分的工作。

Step1:确定每个节点对应的 Changelog 变更类型

数据库中最常见的三种操作类型分别是 Insert (记为 [I]),Delete(记为 [D]),Update(记为 [U])。优化器首先会自底向上检查每个节点,判断它属于哪(几)种类型,分别打上对应标记。

回顾 E.g.2,第一个 Source 节点由于只产生新数据,所以属于 Insert,记为 [I];第二个节点计算内层的聚合,所以会发出更新的消息,记为 [I,U];第三个节点裁掉 word 字段,属于简单计算,传递了上游的变更类型,记为 [I,U];第四个节点是外层的聚合计算,由于它收到了来自上游的 Update 消息,所以额外需要 Delete 操作来保证更新成功,记为 [I,U,D]。

13 optimizer-step-0.gif

Step2:确定每个节点发送的消息类型

在介绍 Step2 之前,我们先介绍下 Flink 中 Update 消息类型的表示形式。在 Flink 中 Update 由两条 update_before(简称 UB)和 update_after (简称 UA)来表示,其中 UB 消息在某些情况下可以不发送,从而提高性能。

在 Step1 中优化器自底向上推导出了每个节点对应的 Changelog 变更操作,这一步里会先自顶向下推断当前节点需要父节点提供的消息类型,直到遇到第一个不需要父节点提供任何消息类型的节点,再往上回推每个节点最终的实现方式和需要的消息类型。

回顾 E.g.2,由于最上层节点是 UpsertSink 节点,只需要它的父节点提供 [UA] 即可。到了外层聚合的 Aggregate 节点,由于 Aggregate 节点的输入有 Update 操作,所以需要父节点需要提供 [UB,UA],这样才能正确更新自己的计算状态。

再往下到 Calc 节点,它需要传递 [UB,UA] 的需求给它的父节点,也就是内层的 Aggregate 节点。而到了内层 Aggregation 节点,它的父节点是 Source 节点,不会产生 Update 操作,所以它不需要 Source 节点额外发送任何 [UB / UA ]。当优化器遍历到 Source 节点,便开始进行回溯,如果当前节点能满足子节点的 requirement,则将对应的标签更新到节点上,否则便无法生成 plan。首先内层的 Aggregate 能产生 UB,所以能满足子节点的 requirement,所以优化器会给内层的 Aggregate 节点打上 [UB,UA] 的标签,然后向上传递到 Calc 节点,同样打上 [UB,UA] ,再到外层的 Aggregate 节点,由于它的下游只需要接受更新后的消息,所以打上 [UA] 标签,表示它只需要向下游发送 update_after 即可。

这些标签最终会影响算子的物理实现,比如外层的 Aggregate 节点,由于它会接收到来自上游的 [UB],所以物理实现会使用带 Retract 的 Count,同时它只会向 Sink 发送 update_after。而内层的 Aggregate 节点,由于上游发送过来的数据没有 [UB],所以可以采用不带 Retract 的 Count 实现,同时由于带有 [UB] 标签,所以需要往下游发送 update_before。

14 optimizer-step-1.gif

Flink SQL Internal Optimization

前面介绍了 Flink SQL 引擎的工作原理,接下来会简要概括一下 Flink SQL 内部的一些优化,更多资料可以在 Flink Forward Asia 2019 查看。

3.1 BinaryRow

在 Flink 1.9+ 前, Flink Runtime 层各算子间传递的数据结构是 Row,其内部实现是 Object[]。这种数据结构的问题在于不但需要额外开销存 Object Metadata,计算过程中还涉及到大量序列化 / 反序列 (特别是只需要处理某几个字段时需要反序列化整个 Row),primitive 类型的拆 / 装箱等,都会带来大量额外的性能开销。

15 row.png

Flink 1.9 开始引入了 Blink Planner,使用二进制数据结构的 BinaryRow 来表示 Record。BinaryRow 作用于默认大小为 32K 的 Memory Segment,直接映射到内存。BinaryRow 内部分为 Header,定长区和变长区。Header 用于存储 Retraction 消息的标识,定长区使用 8 个 bytes 来记录字段的 Nullable 信息及所有 primitive 和可以在 8 个 bytes 内表示的类型。其它类型会按照基于起始位置的 offset 存放在变长区。

BinaryRow 作为 Blink Planner 的基础数据结构,带来的好处是显而易见的:首先存储上更为紧凑,去掉了额外开销;其次在序列化和反序列化上带来的显著性能提升,可根据 offset 只反序列化需要的字段,在开启 Object Reuse 后,序列化可以直接通过内存拷贝完成。

16 binary-row.png

3.2 Mini-batch Processing

Flink 是纯流式处理框架,在理论上每一条新到的数据都会触发一次计算。然而在实现层面,这样做会导致聚合场景下每处理一条数据都需要读写 State 及序列化 / 反序列化。如果能够在内存中 buffer 一定量的数据,预先做一次聚合后再更新 State,则不但会降低操作 State 的开销,还会有效减少发送到下游的数据量,提升 throughput,降低两层聚合时由 Retraction 引起的数据抖动, 这就是 Mini-batch 攒批优化的核心思想。

17 mini-batch.gif

3.3 Skew Processing

对于数据倾斜的优化,主要分为是否带 DISTINCT 去重语义的两种方式。对于普通聚合的数据倾斜,Flink 引入了 Local-Global 两阶段优化,类似于 MapReduce 增加 Local Combiner 的处理模式。而对于带有去重的聚合,Flink 则会将用户的 SQL 按原有聚合的 key 组合再加上 DISTINCT key 做 Hash 取模后改写为两层聚合来进行打散。

18 skew-process.gif

3.4 Top-N Rewrite

全局排序在流式的场景是很难实现的,但如果只需要计算到目前的 Top-N 极值,问题就变得可解。不过传统数据库求排序的 SQL 语法是通过 ORDER BY 加 LIMIT 限制条数,背后实现的机制也是通过扫描全表排序后再返回 LIMIT 条数的记录。另外如果按照某些字段开窗排序,ORDER BY 也无法满足要求。Flink SQL 借鉴了批场景下开窗求 Top-N 的语法,使用 ROW_NUMBER 语法来做流场景下的 Top-N 排序。

E.g.3 下面这段 SQL 计算了每个类目下销量 Top3 的店铺

SELECT*
FROM(
  SELECT *, -- you can get like shopId or other information from this
    ROW_NUMBER() OVER (PARTITION BY category ORDER BY sales DESC) AS rowNum
  FROM shop_sales ) 
WHERE rowNum <= 3

在生成 Plan 方面,ROW_NUMBER 语义对应 OverAggregate 窗口节点和一个过滤行数的 Calc 节点,而这个窗口节点在实现层面需要为每一个到达的数据重新将 State 中的历史数据拿出来排序,这显然不是最优解。

我们知道流式场景求解极大 / 小值的最优操作是通过维护一个 size 为 N 的 minHeap / maxHeap。由实现反推出我们需要在优化器上新增一条规则,在遇到 ROW_NUMBER 生成的逻辑节点后,将其优化为一个特殊的 Rank 节点,对应上述的最优实现方式(当然这只是特殊 Rank 对应的其中一种实现)。这便是 Top-N Rewrite 的核心思想。

19 top-n.png

Summary & Futures

本文内容回顾

  1. 简要介绍 Flink 1.9 + 在 SQL & TableAPI 上引入新架构,统一技术栈,朝着流 & 批一体的方向迈进了一大步。
  2. 深入介绍 Flink SQL 引擎的内部运行机制,以及在对用户透明的同时,Flink SQL 在优化方面做的许多工作。

20 top-n-rewrite.gif

未来工作计划

  1. 在 Flink 1.11+ 后的版本,Blink Planner 将作为默认的 Planner 提供生产级别的支持。
  2. FLIP-95:重构 TableSource & TableSink 的接口设计,面向流批一体化,在 Source 端支持 changelog 消息流,从而支持 FLIP-105 的 CDC 数据源。
  3. FLIP-105:Flink TableAPI & SQL 对 CDC 的支持。
  4. FLIP-115:扩展目前只支持 CSV 的 FileSystem Connector,使其成为流批统一的 Generalized FileSystem Connector。
  5. FLIP-123:对 Hive DDL 和 DML 的兼容,支持用户在 Flink 中运行 Hive DDL。

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

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

相关文章

电商API商品数据采集接口||助力电商企业采集商品大数据提高开发效率

提高开发效率&#xff1a;电商API接口允许不同的应用程序之间高效地进行交互&#xff0c;节省了大量的人力物力成本&#xff0c;使得开发者可以将更多时间和精力集中于自身的核心业务。 增加数据安全性&#xff1a;通过对数据进行安全加密&#xff0c;API接口实现了对数据的保护…

java自学阶段二:JavaWeb开发45(git学习)

目录&#xff1a; 学习目标git的使用&#xff08;工作流程、常用命令、idea集成&#xff09; 一、学习目标&#xff1a; 了解Git基本概念能够了解git的工作流程能够使用Git常用命令熟悉Git代码托管服务能够使用idea操作git 二、git的使用 1&#xff09;git的概念&#xff1…

Oracle 19c OCM认证

Oracle OCM介绍 Oracle Certified Master (OCM) -Oracle认证大师&#xff0c;是Oracle认证的最高级别&#xff0c;是对数据库从业人员的技术、知识和操作技能的最高级别的认可&#xff0c;IT界顶级认证之一。Oracle OCM是解决最困难的技术难题和最复杂的系统故障的最佳Oracle专…

凡尔码搭建设备巡检系统数字化管理平台

一、搭建过程概述 利用凡尔码搭建设备巡检的数字化管理平台&#xff0c;首先需要对凡尔码平台有深入的了解&#xff0c;明确其提供的核心功能和特性&#xff0c;以及如何在设备巡检领域发挥其优势。接着&#xff0c;通过系统规划、组件配置、数据录入和表单创建等步骤&#xff…

短剧小程序App系统源码:打造个性化追剧体验

随着数字媒体的迅猛发展&#xff0c;短剧作为一种新兴的娱乐形式&#xff0c;越来越受到广大观众的喜爱。为了满足用户对短剧内容的个性化需求&#xff0c;短剧小程序App系统应运而生。本文将深入探讨短剧App源码的核心功能&#xff0c;以及如何通过多语言支持和国际支付等技术…

形如SyntaxError: EOL while scanning string literal,以红色波浪线形式在Pycharm下出现

背景&#xff1a; 新手在学习Python时可能会出现如下图所示的报错 下面分情况教大家如何解决 过程&#xff1a; 问题概述&#xff1a; 简单来说就是你单引号、双引号、三引号写的时候末尾注意要和前面写的匹配。 具体如下 """ 编辑器报错&#xff1a;Synt…

在Windows中使用svn的命令行

windows下使用svn命令行_svn命令行工具在哪里-CSDN博客 先下载命令行工具 再进行配置 set SVN_CMD_HOMEC:\Users\admin\Desktop\Apache-Subversion-1.14.0\bin(你的安装路径) set path%path%;%SVN_CMD_HOME% svn help查看svn版本 命令行查看svn版本--真实有效_svn 版本查看…

Java Web学习笔记4——HTML、CSS

HTML&#xff1a; HTML&#xff1a;超文本标记语言。 超文本&#xff1a;超越了文本的限制&#xff0c;比普通文本更强大。除了文字信息&#xff0c;还可以定义图片、音频、视频等内容。 标记语言&#xff1a;有标签构成的语言。 HTML标签都是预定义好的&#xff0c;例如&a…

【全开源】小区物业收费管理系统小程序(FastAdmin+UniApp)

便捷生活新选择 一款基于FastAdminUniApp开发的一款物业收费管理小程序。包含房产管理、收费标准、家属管理、抄表管理、在线缴费、业主公告、统计报表、业主投票、可视化大屏等功能。为物业量身打造的小区收费管理系统&#xff0c;贴合物业工作场景&#xff0c;轻松提高物业费…

Python实现PPT表格的编写包含新建修改插图(收藏备用)

自动创建一个ppt文件并创建好表格 代码要用到pptx库 pip install python-pptx 创建含有表格的ppt文件代码&#xff1a; from pptx import Presentation from pptx.util import Inches# 创建一个PPT对象 ppt Presentation()# 添加一个幻灯片 slide ppt.slides.add_slide(p…

【C++】优先级队列介绍与模拟实现

&#x1f49e;&#x1f49e; 前言 hello hello~ &#xff0c;这里是大耳朵土土垚~&#x1f496;&#x1f496; &#xff0c;欢迎大家点赞&#x1f973;&#x1f973;关注&#x1f4a5;&#x1f4a5;收藏&#x1f339;&#x1f339;&#x1f339; &#x1f4a5;个人主页&#x…

冒泡函数模拟qsort函数

有关于qsort函数和冒泡函数可以跳转CSDNhttps://mp.csdn.net/mp_blog/creation/editor/139388503 qsort的底层逻辑是这样的 我们冒泡排序模仿qsort必须要针对的是所以说我们要模仿qsort的底层逻辑来写 我们以整型数组来举例 #include <stdio.h> int cmp_int(const vo…

OpenAI的Sam Altman搞核聚变了?!究竟是创新还是疯狂?|TodayAI

据《华尔街日报》报道&#xff0c;西雅图地区的核聚变公司Helion Energy正在与人工智能公司OpenAI洽谈一项重要交易&#xff0c;OpenAI计划“购买大量电力为数据中心提供动力”。这一消息引起了广泛关注。 OpenAI的首席执行官兼联合创始人Sam Altman已向Helion投资了3.75亿美元…

【python】成功解决“TypeError: ‘method’ object is not subscriptable”错误的全面指南

成功解决“TypeError: ‘method’ object is not subscriptable”错误的全面指南 一、引言 在Python编程中&#xff0c;TypeError: method object is not subscriptable错误是一个常见的陷阱&#xff0c;特别是对于初学者来说。这个错误通常意味着你尝试像访问列表、元组、字典…

为何PHP使用率 大幅度下降!需求量几乎为零!

用PHP的人越来越少的主要原因包括&#xff1a;市场竞争加剧、新技术的出现、性能和安全问题、以及开发者社区的变化。市场竞争加剧是其中一个突出的因素。随着Python、Node.js等现代编程语言的崛起&#xff0c;它们提供了更好的性能、更简洁的语法和更丰富的框架&#xff0c;逐…

Android音频API介绍

Android系统提供了四个层面的音频API&#xff1a; Java层MediaRecorder&MediaPlayer系列&#xff1b;Java层AudioTrack&AudioRecorder系列&#xff1b;Jni层opensles&#xff1b;JNI层AAudio&#xff08;Android O引入&#xff09; 下面分别介绍这些API的使用及特点。…

学习Python我能做些什么了?你真的了解了嘛?

工欲善其事&#xff0c;必先利其器。学习不是盲目的&#xff0c;是有目标性的。所以&#xff0c;在学习之前充分了解自己所学技能的前景&#xff0c;学完能做什么&#xff0c;大概地薪资待遇是很有必要的。 Python作为人工智能的重要编程语言&#xff0c;无论发展前景还是就业…

Python接入淘宝API接口采集商品详情页到手价优惠券信息数据:智能化营销的加速器

在电子商务领域&#xff0c;智能化营销正在成为提高效率和竞争力的关键。淘宝API提供了一套完整的解决方案&#xff0c;帮助商家实现智能化营销&#xff0c;从而提升销售业绩和顾客满意度。 库存管理&#xff1a; 淘宝API使商家能够实时监控库存水平&#xff0c;自动补货&#…

如何使用vsCode打开intel D435i深度相机

一、下载并安装相机SDK文件 1.SDK下载地址&#xff1a; Release Intel RealSense™ SDK 2.0 (v2.54.2) IntelRealSense/librealsense GitHub 2.下载后&#xff0c;双击即可安装 3.环境配置 1&#xff09;window的开始菜单&#xff0c;搜索环境变量&#xff0c;选择编辑系…

WebGL开发时尚设计系统

开发一个基于WebGL的时尚设计系统可以为用户提供一个互动、实时的3D体验&#xff0c;允许他们设计和试穿虚拟服装。这个系统可以广泛应用于时尚设计、电子商务、虚拟试衣间等领域。以下是开发此系统的主要步骤和关键技术。北京木奇移动技术有限公司&#xff0c;专业的软件外包开…