Flink的Table API 与SQL的流处理

1 流处理与SQL的区别

  Table API和SQL,本质上还是基于关系型表的操作方式;而关系型表、SQL本身,一般是有界的,更适合批处理的场景。所以在流处理的过程中,有一些特殊概念。

SQL流处理
处理对象字段元组的有界集合字段元组的无限序列
查询对数据的访问可以访问完整的数据输入无法访问所有数据,必须持续等待流式输入
查询终止条件生成固定大小的结果集后终止永不停止,根据持续收到的数据不断更新查询结果

  尽管存在这些差异,使用关系查询和SQL处理流并不是不可能的。高级关系数据库系统提供了一种称为物化视图的特性。物化视图被定义为SQL查询,就像常规的虚拟视图一样。与虚拟视图相反,物化视图缓存查询的结果,以便在访问视图时不需要计算查询。缓存的一个常见挑战是防止缓存提供过时的结果。当物化视图的定义查询的基表被修改时,物化视图就会过时。即时视图维护是一种技术,用于在基本表更新后立即更新物化视图。

2 动态表

  在Flink Table API概念里有个动态表(Dynamic Tables),动态表随着新数据的到来不停的在之前的基础上更新结果。这与传统的关系型数据库中的表示是截然不同的,因为流处理的数据是源源不断的,将流数据转换成Table,然后进行操作结果也就不是一成不变,而是随着新数据不断更新。

  动态表是Flink对流数据的Table API和SQL支持的核心概念。动态表可以像传统关系型数据库中的表一样查询,查询一个动态表会产生持续查询,持续查询永远不会终止,还会生成另一个动态表,查询操作会不断更新动态结果表,反应动态数据表的更改。

  本质上,动态表上的连续查询与定义物化视图的查询非常相似,注意:连续查询的结果在语义上总是等价于在输入表的快照上以批处理模式执行相同查询的结果。

  下图可视化了流、动态表和连续查询之间的关系:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bcLFveIX-1596812198844)(C:\资料\flink\笔记\8 Table API与SQL\assets\1596357675358.png)]

  流式持续查询的过程为:①流被转换为动态表②对动态表计算连续查询,生成新的动态表③ 生成的动态表被转换回流。

  注意:动态表首先是一个逻辑概念。动态表不一定在查询执行期间物化

3 将流转换成表

  为了处理带有关系查询的流,必须先将其转换为表。从概念上讲,流的每个数据记录,都被解释为对结果表的插入(Insert)修改因为流式持续不断的,而且之前的输出结果无法改变。本质上,我们其实是从一个、只有插入操作的changelog(更新日志)流,来构建一个表

  使用具有以下模式的单击事件流解释动态表和连续查询的概念:

[user:  VARCHAR,   // the name of the usercTime: TIMESTAMP, // the time when the URL was accessedurl:   VARCHAR    // the URL that was accessed by the user
]

  下图可视化了单击事件流(左侧)如何转换为表(右侧)。随着插入更多单击流的记录,结果表不断增长。

在这里插入图片描述

  注意:定义在流上的表在内部不是物化的

4 持续查询

  对动态表进行持续查询,并生成新的动态表作为结果。与批处理查询不同,持续查询从不根据输入表上的更新终止而更新其结果表。在任何时间点,持续查询的结果在语义上等同于输入表快照上以批处理模式执行的同一查询的结果。

  在下面的示例中,我们将在clicks表,该表是在单击事件流上定义的。第一个查询是一个简单的GROUP BY COUNT聚合查询,它将clicks表上user字段,并计算访问的URL数。下面的图显示了查询是如何在一段时间内作为clicks表使用其他行更新。

在这里插入图片描述

  当查询开始时,clicks表(左侧)示空的,这个查询开始计算结果表,当第一行[Mary, ./home]插入到clicks表,结果表(右侧)由一行组成[Mary, 1];当第二行[Bob, ./cart]插入到clicks表,结果表(右侧)插入一个新的行[Bob, 1];当第三行[Mary, ./prod?id=1]`插入到clicks表,生成已计算结果行的更新[Mary, 1]更新成[Mary, 2];最后[Liz, 1]插入到结果表,当第四行追加到clicks表中。

  第二个查询类似于第一个查询,但将clicks表之外的user属性的每小时滚动在计算URL数量之前(基于时间的计算(如windows)是基于特殊的)。同样,图显示了不同时间点的输入和输出,以可视化动态表的变化性质。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5xsFLiKu-1596812198855)(C:\资料\flink\笔记\8 Table API与SQL\assets\1596359394654.png)]

  和前面一样,输入表clicks显示在左边。查询每小时连续计算结果并更新结果表。在12:00:00和12:59:59clicks表包含带有时间戳的四行(cTime)。该查询从此输入计算两个结果行(每个输入一行),并将它们附加到结果表中。之间的下一个窗口13:00:0013:59:59,clicks表包含三行,这将导致将另两行附加到结果表。随着时间的推移,结果表将被更新,因为会追加更多的行。

  虽然上面两个示例查询看起来非常相似(两者都计算分组计数聚合),但它们在一个重要方面有所不同:

  ①第一个查询更新先前发出的结果,即定义结果表的Changelog流包含INSERT和UPDATE改变;

  ②第二个查询仅附加到结果表,即结果表的Changelog流仅由INSERT改变

  查询是生成仅附加的表还是更新的表有一些含义:

  ①产生更新更改的查询通常需要维护更多的状态

  ②将仅追加的表转换为流与对更新的表的转换不同

5 将动态表转换成流

  与常规的数据库表一样,动态表可以通过插入(Insert)、更新(Update)和删除(Delete)更改,进行持续的修改。将动态表转换为流或将其写入外部系统时,需要对这些更改进行编码。Flink的Table API和SQL支持三种方式对动态表的更改进行编码:

5.1 仅追加(Append-only)

  仅通过插入(Insert)更改,来修改的动态表,可以直接转换为“仅追加”流。这个流中发出的数据,就是动态表中新增的每一行。

5.2 撤回(Retract)

  Retract流是包含两类消息的流,添加(Add)消息和撤回(Retract)消息。

  动态表通过将INSERT 编码为add消息、DELETE 编码为retract消息、UPDATE编码为被更改行(前一行)的retract消息和更新后行(新行)的add消息,转换为retract流。

下图显示了将动态表转换为Retract流的过程

在这里插入图片描述

5.3 Upsert(更新插入)

  Upsert流包含两种类型的消息:Upsert消息和delete消息。转换为upsert流的动态表,**需要有唯一的键(key)。**通过将INSERT和UPDATE更改编码为upsert消息,将DELETE更改编码为DELETE消息,就可以将具有唯一键(Unique Key)的动态表转换为流。

  下图显示了将动态表转换为upsert流的过程:

在这里插入图片描述

  注意:在代码里将动态表转换为DataStream时,仅支持Append和Retract流。而向外部系统输出动态表的TableSink接口,则可以有不同的实现。

6 时间特性

  基于时间的操作(如Table API和SQL中窗口操作),需要定义相关的时间语义和时间数据来源的信息。所以,Table可以提供一个逻辑上的时间字段,用于在表处理程序中,指示时间和访问相应的时间戳

  时间属性,可以是每个表schema的一部分。一旦定义了时间属性,它就可以作为一个字段引用,并且可以在基于时间的操作中使用。**只要时间属性没有被修改,只是从查询的一个部分转发到另一个部分,它仍然是一个有效的时间属性。**时间属性的行为类似于常规时间戳,可以访问,并且进行计算。如果在计算中使用时间属性,则它将被物化并成为常规时间戳。常规时间戳不配合Flink的时间和水印系统,因此不能再用于基于时间的操作。

val env = StreamExecutionEnvironment.getExecutionEnvironmentenv.setStreamTimeCharacteristic(TimeCharacteristic.ProcessingTime) // default// alternatively:
// env.setStreamTimeCharacteristic(TimeCharacteristic.IngestionTime)
// env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime)

6.1 处理时间(Processing Time)

  处理时间语义下,允许表处理程序根据机器的本地时间生成结果。它是时间的最简单概念。它既不需要提取时间戳,也不需要生成watermark。

  定义处理时间属性有三种方法:在DataStream转化时直接指定;在定义Table Schema时指定;在创建表的DDL中指定。

6.1.1 创建表的DDL中指定

  在创建表的DDL中,增加一个字段并指定成proctime,也可以指定当前的时间字段。代码如下:

CREATE TABLE user_actions (user_name STRING,data STRING,user_action_time AS PROCTIME() -- declare an additional field as a processing time attribute
) WITH ('connector.type' = 'filesystem','connector.path' = 'file:///D:\\..\\sensor.txt','format.type' = 'csv'
);SELECT TUMBLE_START(user_action_time, INTERVAL '10' MINUTE), COUNT(DISTINCT user_name)
FROM user_actions
GROUP BY TUMBLE(user_action_time, INTERVAL '10' MINUTE);

  注意:运行这段DDL,必须使用Blink Planner

6.1.2 DataStream转化成Table时指定

  由DataStream转换成表时,可以在后面指定字段名来定义Schema。在定义Schema期间,可以使用.proctime,定义处理时间字段。

注意,这个proctime属性只能通过附加逻辑字段,来扩展物理schema。因此,只能在schema定义的末尾定义它。

val stream: DataStream[(String, String)] = ...// declare an additional logical field as a processing time attribute
val table = tEnv.fromDataStream(stream, 'UserActionTimestamp, 'user_name, 'data, 'user_action_time.proctime)val windowedTable = table.window(Tumble over 10.minutes on 'user_action_time as 'userActionWindow)

6.1.3 定义Table Schema时指定

  只要在定义Schema的时候,加上一个新的字段,并指定成proctime就可以了。

tableEnv.connect(new FileSystem().path("file_path")).withFormat(new Csv()).withSchema(new Schema().field("user_name", DataTypes.STRING()).field("UserActionTimestamp", DataTypes.BIGINT()).field("data", DataTypes.STRING()).field("pt", DataTypes.TIMESTAMP(3)).proctime()    // 指定 pt字段为处理时间) // 定义表结构.createTemporaryTable("inputTable") // 创建临时表

6.2 事件时间(Event Time)

  事件时间语义,允许表处理程序根据每个记录中包含的时间生成结果。这样即使在有乱序事件或者延迟事件时,也可以获得正确的结果。

  为了处理无序事件,并区分流中的准时和迟到事件;Flink需要从事件数据中,提取时间戳,并用来推进事件时间的进展(watermark)。事件时间属性可以在CREATE TABLE DDL中定义,也可以在Datastream到表转换期间或者通过使用TableSource来定义。

6.2.1 创建表的DDL中指定

  事件时间属性,是使用CREATE TABLE DDL中的WARDMARK语句定义的。watermark语句,定义现有事件时间字段上的watermark生成表达式,该表达式将事件时间字段标记为事件时间属性。

CREATE TABLE user_actions (user_name STRING,data STRING,user_action_time TIMESTAMP(3),-- declare user_action_time as event time attribute and use 5 seconds delayed watermark strategyWATERMARK FOR user_action_time AS user_action_time - INTERVAL '5' SECOND
) WITH (...
);SELECT TUMBLE_START(user_action_time, INTERVAL '10' MINUTE), COUNT(DISTINCT user_name)
FROM user_actions
GROUP BY TUMBLE(user_action_time, INTERVAL '10' MINUTE);

6.2.2 DataStream转化成Table时指定

  在DataStream转换成Table,schema的定义期间,使用.rowtime可以定义事件时间属性。必须在转换的数据流中分配时间戳和watermark。

  在将数据流转换为表时,有两种定义时间属性的方法。根据指定的.rowtime字段名是否存在于数据流的架构中,timestamp字段可以作为新字段追加到schema替换现有字段

  在这两种情况下,定义的事件时间戳字段,都将保存DataStream中事件时间戳的值。

// Option 1:// extract timestamp and assign watermarks based on knowledge of the stream
val stream: DataStream[(String, String)] = inputStream.assignTimestampsAndWatermarks(...)// declare an additional logical field as an event time attribute
val table = tEnv.fromDataStream(stream, 'user_name, 'data, 'user_action_time.rowtime)// Option 2:// extract timestamp from first field, and assign watermarks based on knowledge of the stream
val stream: DataStream[(Long, String, String)] = inputStream.assignTimestampsAndWatermarks(...)// the first field has been used for timestamp extraction, and is no longer necessary
// replace first field with a logical event time attribute
val table = tEnv.fromDataStream(stream, 'user_action_time.rowtime, 'user_name, 'data)// Usage:val windowedTable = table.window(Tumble over 10.minutes on 'user_action_time as 'userActionWindow)

6.2.3 定义Table Schema时指定

 这种方法只要在定义Schema的时候,将事件时间字段,并指定成rowtime就可以了。

tableEnv.connect(new FileSystem().path("flie_path")).withFormat(new Csv()).withSchema(new Schema().field("user_name", DataTypes.STRING()).field("timestamp", DataTypes.BIGINT()).rowtime(new Rowtime().timestampsFromField("timestamp")    // 从字段中提取时间戳.watermarksPeriodicBounded(1000)    // watermark延迟1秒).field("data", DataTypes.STRING())) // 定义表结构.createTemporaryTable("inputTable") // 创建临时表

7 窗口

  时间语义要配合窗口操作,根据时间段做计算了才能发挥作用。在Table API和SQL中,主要有两种窗口:Group Windows(分组窗口)和Over Windows。

7.1 Table中的Group Windows

  Group Windows会根据时间或行计数间隔,将行聚合到有限的组(Group)中,并对每个组的数据执行一次聚合函数。

  Table API中的Group Windows都是使用.window(w:GroupWindow)子句定义的,必须由as子句指定一个别名为了按窗口对表进行分组,窗口的别名必须在group by子句中,像常规的分组字段一样引用。

val table = input.window([w: GroupWindow] as 'w) // 定义窗口,别名 w.groupBy('w, 'a)  // 以属性a和窗口w作为分组的key .select('a, 'b.sum)  // 聚合字段b的值,求和

  也可以把窗口的相关信息,作为字段添加到结果表中:

val table = input.window([w: GroupWindow] as 'w) .groupBy('w, 'a) .select('a, 'w.start, 'w.end, 'w.rowtime, 'b.count)

  Table API提供了一组具有特定语义的预定义Window类,这些类会被转换为底层DataStream或DataSet的窗口操作。Table API支持的窗口定义,主要也是三种:滚动(Tumbling)、滑动(Sliding)和会话(Session)。

7.1.1 滚动窗口

滚动窗口(Tumbling windows)要用Tumble类来定义,另外还有三个方法:①over:定义窗口长度;②on:用来分组(按时间间隔)或者排序(按行数)的时间字段;③as:别名,必须出现在后面的groupBy中

// Tumbling Event-time Window(事件时间字段rowtime)
.window(Tumble over 10.minutes on 'rowtime as 'w)// Tumbling Processing-time Window(处理时间字段proctime)
.window(Tumble over 10.minutes on 'proctime as 'w)// Tumbling Row-count Window (类似于计数窗口,按处理时间排序,10行一组)
.window(Tumble over 10.rows on 'proctime as 'w)

7.1.2 滑动窗口

  滑动窗口(Sliding windows)要用Slide类来定义,另外还有四个方法:①over:定义窗口长度②every:定义滑动步长③on:用来分组(按时间间隔)或者排序(按行数)的时间字段④as:别名,必须出现在后面的groupBy中

// Sliding Event-time Window
.window(Slide over 10.minutes every 5.minutes on 'rowtime as 'w)// Sliding Processing-time window 
.window(Slide over 10.minutes every 5.minutes on 'proctime as 'w)// Sliding Row-count window
.window(Slide over 10.rows every 5.rows on 'proctime as 'w)

7.1.3 会话窗口

  会话窗口(Session windows)要用Session类来定义,另外还有三个方法:①withGap:会话时间间隔②on:用来分组(按时间间隔)或者排序(按行数)的时间字段③as:别名,必须出现在后面的groupBy中

// Session Event-time Window
.window(Session withGap 10.minutes on 'rowtime as 'w)// Session Processing-time Window 
.window(Session withGap 10.minutes on 'proctime as 'w)

7.2 Table中的Over Windows

  Over window聚合是标准SQL中已有的(Over子句),可以在查询的SELECT子句中定义。Over window 聚合,会针对每个输入行,计算相邻行范围内的聚合。Over windows使用.window(w:overwindows*)子句定义,并在select()方法中通过别名来引用。

val table = input.window([w: OverWindow] as 'w).select('a, 'b.sum over 'w, 'c.min over 'w)

  Table API提供了Over类,来配置Over窗口的属性。可以在事件时间或处理时间,以及指定为时间间隔、或行计数的范围内,定义Over windows。无界的over window是使用常量指定的。也就是说,时间间隔要指定UNBOUNDED_RANGE,或者行计数间隔要指定UNBOUNDED_ROW。而有界的over window是用间隔的大小指定的。

  (1) 无界的 over window

// 无界的事件时间over window (时间字段 "rowtime")
.window(Over partitionBy 'a orderBy 'rowtime preceding UNBOUNDED_RANGE as 'w)//无界的处理时间over window (时间字段"proctime")
.window(Over partitionBy 'a orderBy 'proctime preceding UNBOUNDED_RANGE as 'w)// 无界的事件时间Row-count over window (时间字段 "rowtime")
.window(Over partitionBy 'a orderBy 'rowtime preceding UNBOUNDED_ROW as 'w)//无界的处理时间Row-count over window (时间字段 "rowtime")
.window(Over partitionBy 'a orderBy 'proctime preceding UNBOUNDED_ROW as 'w)

  (2)有界的 over window

// 有界的事件时间over window (时间字段 "rowtime",之前1分钟)
.window(Over partitionBy 'a orderBy 'rowtime preceding 1.minutes as 'w)// 有界的处理时间over window (时间字段 "rowtime",之前1分钟)
.window(Over partitionBy 'a orderBy 'proctime preceding 1.minutes as 'w)// 有界的事件时间Row-count over window (时间字段 "rowtime",之前10行)
.window(Over partitionBy 'a orderBy 'rowtime preceding 10.rows as 'w)// 有界的处理时间Row-count over window (时间字段 "rowtime",之前10行)
.window(Over partitionBy 'a orderBy 'proctime preceding 10.rows as 'w)

7.3 SQL中的Group Windows

  Group Windows在SQL查询的Group BY子句中定义。与使用常规GROUP BY子句的查询一样,使用GROUP BY子句的查询会计算每个组的单个结果行。

  SQL支持以下Group窗口函数:

TUMBLE(time_attr, interval)

  定义一个滚动窗口,第一个参数是时间字段,第二个参数是窗口长度。

HOP(time_attr, interval, interval)

  定义一个滑动窗口,第一个参数是时间字段,第二个参数是窗口滑动步长,第三个是窗口长度。

SESSION(time_attr, interval)

  另外还有一些辅助函数,可以用来选择Group Window的开始和结束时间戳,以及时间属性。

TUMBLE_START(time_attr, interval)TUMBLE_END(time_attr, interval)TUMBLE_ROWTIME(time_attr, interval)TUMBLE_PROCTIME(time_attr, interval)

7.4 SQL中的Over Windows

  由于Over本来就是SQL内置支持的语法,所以这在SQL中属于基本的聚合操作。所有聚合必须在同一窗口上定义,也就是说,必须是相同的分区、排序和范围。目前仅支持在当前行范围之前的窗口(无边界和有边界)。

  注意,ORDER BY必须在单一的时间属性上指定。

SELECT COUNT(amount) OVER (PARTITION BY userORDER BY proctimeROWS BETWEEN 2 PRECEDING AND CURRENT ROW)
FROM Orders// 也可以做多个聚合
SELECT COUNT(amount) OVER w, SUM(amount) OVER w
FROM Orders
WINDOW w AS (PARTITION BY userORDER BY proctimeROWS BETWEEN 2 PRECEDING AND CURRENT ROW)

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

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

相关文章

LeetCode 833. 字符串中的查找与替换(排序,replace)

文章目录1. 题目2. 解题1. 题目 某个字符串 S 需要执行一些替换操作,用新的字母组替换原有的字母组(不一定大小相同)。 每个替换操作具有 3 个参数:起始索引 i,源字 x 和目标字 y。 规则是:如果 x 从原始…

Json.NET

我前面的一篇博客 Metro应用Json数据处理 介绍了如何使用 DataContractJsonSerializer 类将对象的实例序列化为JSON字符串以及将JSON字符串反序列化为对象的实例的处理方式。而此种处理方式的一个很大的缺点就是要求JSON字符串格式是约定好的,而在很多情况下我们无法…

天池 在线编程 最大得分(DP)

文章目录1. 题目2. 解题1. 题目 来源&#xff1a;https://tianchi.aliyun.com/oj/164423301311799378/184808348725744275 2. 解题 class Solution { public:/*** param matrix: the matrix* return: the maximum score you can get*/int maximumScore(vector<vector<i…

天池 在线编程 LR String

文章目录1. 题目2. 解题1. 题目 来源&#xff1a;https://tianchi.aliyun.com/oj/164423301311799378/184808348725744276 2. 解题 class Solution { public:/*** param s: a string* param t: a string* param n: max times to swap a l and a r.* return: return if s can …

天池 在线编程 音乐组合

文章目录1. 题目2. 解题1. 题目 来源&#xff1a;https://tianchi.aliyun.com/oj/164423301311799378/184808348725744274 2. 解题 对60求余后&#xff0c;0, 30的为 Cn2C_n^2Cn2​&#xff0c;其余的相加等于60的&#xff0c;种类相乘 class Solution { public:/*** param …

java之NIO(Channel,Buffer,Selector)

java之NIO 1 什么是NIO Java NIO (New IO&#xff0c;Non-Blocking IO)是从Java 1.4版本开始引入的一套新的IO API。NIO支持面向缓冲区的、基于通道的IO操作。NIO的三大核心部分&#xff1a;通道(Channel)&#xff0c;缓冲区(Buffer), 选择器(Selector)&#xff0c;数据总是从…

LeetCode 1652. 拆炸弹(前缀和)

文章目录1. 题目2. 解题1. 题目 你有一个炸弹需要拆除&#xff0c;时间紧迫&#xff01;你的情报员会给你一个长度为 n 的 循环 数组 code 以及一个密钥 k 。 为了获得正确的密码&#xff0c;你需要替换掉每一个数字。所有数字会 同时 被替换。 如果 k > 0 &#xff0c;将…

MYSQL从入门到精通

SQL是数据库的查询语言&#xff0c;语法结构简单&#xff0c;相信本文会让你从入门到熟练。 掌握SQL后&#xff0c;不论你是产品经理、运营人员或者数据分析师&#xff0c;都会让你分析的能力边界无限拓展。别犹豫了&#xff0c;赶快上车吧&#xff01; SQL最小化的查询结构如下…

LeetCode 1653. 使字符串平衡的最少删除次数(DP)

文章目录1. 题目2. 解题1. 题目 给你一个字符串 s &#xff0c;它仅包含字符 a 和 b​​​​ 。 你可以删除 s 中任意数目的字符&#xff0c;使得 s 平衡 。 我们称 s 平衡的 当不存在下标对 (i,j) 满足 i < j 且 s[i] b 同时 s[j] a 。 请你返回使 s 平衡 的 最少 删除…

LeetCode 1654. 到家的最少跳跃次数(BFS)

文章目录1. 题目2. 解题1. 题目 有一只跳蚤的家在数轴上的位置 x 处。请你帮助它从位置 0 出发&#xff0c;到达它的家。 跳蚤跳跃的规则如下&#xff1a; 它可以 往前 跳恰好 a 个位置&#xff08;即往右跳&#xff09;。它可以 往后 跳恰好 b 个位置&#xff08;即往左跳&…

Maven详解及实例

1 什么是Maven Maven对项目进行模型抽象&#xff0c;充分运用的面向对象的思想&#xff0c;Maven可以通过一小段描述信息来管理项目的构建&#xff0c;报告和文档的软件项目管理工具。Maven 除了以程序构建能力为特色之外&#xff0c;还提供高级项目管理工具。简单的来说Maven是…

Python多线程详解

1、多线程的理解 多进程和多线程都可以执行多个任务&#xff0c;线程是进程的一部分。线程的特点是线程之间可以共享内存和变量&#xff0c;资源消耗少&#xff08;不过在Unix环境中&#xff0c;多进程和多线程资源调度消耗差距不明显&#xff0c;Unix调度较快&#xff09;&…

LeetCode 1655. 分配重复整数(回溯)

文章目录1. 题目2. 解题2.1 回溯1. 题目 给你一个长度为 n 的整数数组 nums &#xff0c;这个数组中至多有 50 个不同的值。 同时你有 m 个顾客的订单 quantity &#xff0c;其中&#xff0c;整数 quantity[i] 是第 i 位顾客订单的数目。请你判断是否能将 nums 中的整数分配给…

Flink的异步I/O及Future和CompletableFuture

1 概述 Flink在做流数据计算时&#xff0c;经常要外部系统进行交互&#xff0c;如Redis、Hive、HBase等等存储系统。系统间通信延迟是否会拖慢整个Flink作业&#xff0c;影响整体吞吐量和实时性。 如需要查询外部数据库以关联上用户的额外信息&#xff0c;通常的实现方式是向数…

LeetCode 1656. 设计有序流(数组)

文章目录1. 题目2. 解题1. 题目 有 n 个 (id, value) 对&#xff0c;其中 id 是 1 到 n 之间的一个整数&#xff0c;value 是一个字符串。不存在 id 相同的两个 (id, value) 对。 设计一个流&#xff0c;以 任意 顺序获取 n 个 (id, value) 对&#xff0c;并在多次调用时 按 …

flask框架+mysql数据库并与前台数据交互

在Flask使用数据库 我们将使用Flask-SQLAlchemy 的扩展来管理数据库。由SQLAlchemy项目提供的&#xff0c;已封装了关系对象映射&#xff08;ORM&#xff09;的一个插件。 ORMs允许数据库程序用对象的方式替代表和SQL语句。面向对象的操作被ORM转化为数据库命令。这样就意味着&…

Canal原理及其使用

1 什么是canal canal是用java开发的基于数据库增量日志解析&#xff0c;提供增量数据订阅&消费的中间件。目前&#xff0c;canal主要支持了MySQL的binlog解析&#xff0c;解析完成后才利用canal client 用来处理获得的相关数据。&#xff08;数据库同步需要阿里的otter中间…

LeetCode 1657. 确定两个字符串是否接近

文章目录1. 题目2. 解题1. 题目 如果可以使用以下操作从一个字符串得到另一个字符串&#xff0c;则认为两个字符串 接近 &#xff1a; 操作 1&#xff1a;交换任意两个 现有 字符。 例如&#xff0c;abcde -> aecdb操作 2&#xff1a;将一个 现有 字符的每次出现转换为另一…

机器学习简介及学习思维导图

什么是机器学习机器学习是人工智能的一个分支。人工智能的研究是从以“推理”为重点到以“知识”为重点&#xff0c;再到以“学习”为重点&#xff0c;一条自然、清晰的脉络。机器学习是实现人工智能的一个途径&#xff0c;即以机器学习为手段解决人工智能中的问题。机器学习算…

LeetCode 1658. 将 x 减到 0 的最小操作数(哈希)

文章目录1. 题目2. 解题1. 题目 给你一个整数数组 nums 和一个整数 x 。每一次操作时&#xff0c;你应当移除数组 nums 最左边或最右边的元素&#xff0c;然后从 x 中减去该元素的值。请注意&#xff0c;需要 修改 数组以供接下来的操作使用。 如果可以将 x 恰好 减到 0 &…