flink的EventTime和Watermark

时间机制

Flink中的时间机制主要用在判断是否触发时间窗口window的计算。

在Flink中有三种时间概念:ProcessTime、IngestionTime、EventTime。

ProcessTime:是在数据抵达算子产生的时间(Flink默认使用ProcessTime)

IngestionTime:是在DataSource生成数据产生的时间

EventTime:是数据本身携带的时间,具有实际业务含义,不是Flink框架产生的时间

水位机制

由于网络原因、故障等原因,数据的EventTIme并不是单调递增的,是乱序的,有时与当前实际时间相差很大。

水位(watermark)用在EventTime语义的窗口计算,可以当作当前计算节点的时间。当水位超过窗口的endtime,表示事件时间t <= T的数据都**已经到达,**这个窗口就会触发WindowFunction计算。当水位超过窗口的endtime+允许迟到的时间,窗口就会消亡。本质是DataStream中的一种特殊元素,每个水印都携带有一个时间戳。

  1. 队列中是乱序的数据,流入长度3s的窗口。2s的数据进入[0,4)的窗口中
  2. 2s、3s、1s的数据进入[0,4)的窗口,7s的数据分配到[4,8)的窗口中
  3. 水印4s到达,代表4s以前的数据都已经到达。触发[0,4)的窗口计算,[4,8)的窗口等待数据
  4. 水印9s到达,[4,8)的窗口触发

多并行情况下,不同的watermark流到算子,取最小的wartermark当作当前算子的watermark。

如果所有流入水印中时间戳最小的那个都已经达到或超过了窗口的结束时间,那么所有流的数据肯定已经全部收齐,就可以安全地触发窗口计算了。

生成水位

首先设置env为事件时间

使用 DataStream API 实现 Flink 任务时,Watermark Assigner 能靠近 Source 节点就靠近 Source 节点,能前置尽量前置

StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime);//测试数据,间隔1s发送
DataStreamSource<Tuple2<String, Long>> source = env.addSource(new SourceFunction<Tuple2<String, Long>>() {@Overridepublic void run(SourceContext<Tuple2<String, Long>> ctx) throws Exception {ctx.collect(Tuple2.of("aa", 1681909200000L));//2023-04-19 21:00:00Thread.sleep(1000);ctx.collect(Tuple2.of("aa", 1681909500000L));//2023-04-19 21:05:00Thread.sleep(1000);ctx.collect(Tuple2.of("aa", 1681909800000L));//2023-04-19 21:10:00Thread.sleep(1000);ctx.collect(Tuple2.of("aa", 1681910100000L));//2023-04-19 21:15:00Thread.sleep(1000);ctx.collect(Tuple2.of("aa", 1681910400000L));//2023-04-19 21:20:00Thread.sleep(1000);ctx.collect(Tuple2.of("aa", 1681910700000L));//2023-04-19 21:25:00Thread.sleep(Long.MAX_VALUE);}@Overridepublic void cancel() {}});

抽取EventTime、生成Watermark

周期性水位–AssignerWithPeriodicWatermarks�(常用)

周期性生成水位。周期默认的时间是 200ms.

源码如下:

@PublicEvolving
public void setStreamTimeCharacteristic(TimeCharacteristic characteristic) {this.timeCharacteristic = Preconditions.checkNotNull(characteristic);if (characteristic == TimeCharacteristic.ProcessingTime) {getConfig().setAutoWatermarkInterval(0);} else {getConfig().setAutoWatermarkInterval(200);}

自定义实现AssignerWithPeriodicWatermarks,代码如下:

source.assignTimestampsAndWatermarks(new AssignerWithPeriodicWatermarks<Tuple2<String, Long>>() {private long currentTimestamp;@Nullable@Override// 生成watermarkpublic Watermark getCurrentWatermark() {return new Watermark(currentTimestamp);}@Override//获取事件时间public long extractTimestamp(Tuple2<String, Long> element, long previousElementTimestamp) {if (element.f1>=currentTimestamp){currentTimestamp = element.f1;}return element.f1;}}).keyBy(value -> value.f0).timeWindow(Time.minutes(10)).process(new ProcessWindowFunction<Tuple2<String, Long>, Object, String, TimeWindow>() {@Overridepublic void process(String key, ProcessWindowFunction<Tuple2<String, Long>, Object, String, TimeWindow>.Context context, Iterable<Tuple2<String, Long>> elements, Collector<Object> out) throws Exception {System.out.println("----------------");System.out.println("系统当前时间:"+ DateUtil.date());System.out.println("当前水位时间:"+DateUtil.date(context.currentWatermark()));System.out.println("窗口开始时间:"+DateUtil.date(context.window().getStart()));System.out.println("窗口结束时间:"+DateUtil.date(context.window().getEnd()));elements.forEach(element -> System.out.println("数据携带时间:"+DateUtil.date(element.f1)));}}).print();

运行结果如下:

水位时间到达2023-04-19 21:10:00触发窗口2023-04-19 21:00:00到2023-04-19 21:10:00,窗口中的数据为2023-04-19 21:00:00和2023-04-19 21:05:00

水位时间到达2023-04-19 21:20:00触发窗口2023-04-19 21:10:00到2023-04-19 21:20:00,窗口中的数据为2023-04-19 21:10:00和2023-04-19 21:15:00

长时间等待后,2023-04-19 21:20:00到2023-04-19 21:30:00是存在一个2023-04-19 21:25:00的数据,一直没有触发。这是因为没有新的数据进入,周期性生成的watermark一直是2023-04-19 21:20:00。所以后面窗口即使有数据也没有触发计算。

BoundedOutOfOrdernessTimestampExtractor�

BoundedOutOfOrdernessTimestampExtractor实现了AssignerWithPeriodicWatermarks接口,是flink内置的实现类。

主要源码如下:

public BoundedOutOfOrdernessTimestampExtractor(Time maxOutOfOrderness) {if (maxOutOfOrderness.toMilliseconds() < 0) {throw new RuntimeException("Tried to set the maximum allowed " +"lateness to " + maxOutOfOrderness + ". This parameter cannot be negative.");}this.maxOutOfOrderness = maxOutOfOrderness.toMilliseconds();this.currentMaxTimestamp = Long.MIN_VALUE + this.maxOutOfOrderness;}public abstract long extractTimestamp(T element);@Overridepublic final Watermark getCurrentWatermark() {long potentialWM = currentMaxTimestamp - maxOutOfOrderness;if (potentialWM >= lastEmittedWatermark) {lastEmittedWatermark = potentialWM;}return new Watermark(lastEmittedWatermark);}@Overridepublic final long extractTimestamp(T element, long previousElementTimestamp) {long timestamp = extractTimestamp(element);if (timestamp > currentMaxTimestamp) {currentMaxTimestamp = timestamp;}return timestamp;}

BoundedOutOfOrdernessTimestampExtractor产生的时间戳和水印是允许“有界乱序”的,构造它时传入的参数maxOutOfOrderness就是乱序区间的长度,而实际发射的水印为通过覆写extractTimestamp()方法提取出来的时间戳减去乱序区间,相当于让水印把步调“放慢一点”。这是Flink为迟到数据提供的第一重保障。

当然,乱序区间的长度要根据实际环境谨慎设定,设定得太短会丢较多的数据,设定得太长会导致窗口触发延迟,实时性减弱。

设置maxOutOfOrderness为5min,代码如下:

source.assignTimestampsAndWatermarks(new BoundedOutOfOrdernessTimestampExtractor<Tuple2<String, Long>>(Time.minutes(5)) {@Overridepublic long extractTimestamp(Tuple2<String, Long> element) {return element.f1;}}).keyBy(value -> value.f0).timeWindow(Time.minutes(10)).process(new ProcessWindowFunction<Tuple2<String, Long>, Object, String, TimeWindow>() {@Overridepublic void process(String key, ProcessWindowFunction<Tuple2<String, Long>, Object, String, TimeWindow>.Context context, Iterable<Tuple2<String, Long>> elements, Collector<Object> out) throws Exception {System.out.println("----------------");System.out.println("系统当前时间:"+ DateUtil.date());System.out.println("当前水位时间:"+DateUtil.date(context.currentWatermark()));System.out.println("窗口开始时间:"+DateUtil.date(context.window().getStart()));System.out.println("窗口结束时间:"+DateUtil.date(context.window().getEnd()));elements.forEach(element -> System.out.println("数据携带时间:"+DateUtil.date(element.f1)));}}).print();

运行结果如下:

看起来和我们自定义实现结果一样。但是10min的水位时间是来自数据15min减去延迟时间5min得来的。

同理20min的水位时间是来自数据25min减去延迟时间5min得来的。

我们可以设置延迟时间为10min,看一下结果。最后一条数据是25min,那么最后的水位线就是25min-10min=15min。只会触发00-10的窗口。

同样的,由于没有后续数据导致后面的窗口没有触发。

AscendingTimestampExtractor

AscendingTimestampExtractor要求生成的时间戳和水印都是单调递增的。用户实现从数据中获取自增的时间戳extractAscendingTimestamp与上一次时间戳比较。如果出现减少,则打印warn日志。

源码如下:

    public abstract long extractAscendingTimestamp(T element);@Overridepublic final long extractTimestamp(T element, long elementPrevTimestamp) {final long newTimestamp = extractAscendingTimestamp(element);if (newTimestamp >= this.currentTimestamp) {this.currentTimestamp = newTimestamp;return newTimestamp;} else {violationHandler.handleViolation(newTimestamp, this.currentTimestamp);return newTimestamp;}}@Overridepublic final Watermark getCurrentWatermark() {return new Watermark(currentTimestamp == Long.MIN_VALUE ? Long.MIN_VALUE : currentTimestamp - 1);}

间断性水位线

适用于根据接收到的消息判断是否需要产生水位线的情况,用这种水印生成的方式并不多见。

举例如下,数据为15min的时候生成水位。

source.assignTimestampsAndWatermarks(new AssignerWithPunctuatedWatermarks<Tuple2<String, Long>>() {@Nullable@Overridepublic Watermark checkAndGetNextWatermark(Tuple2<String, Long> lastElement, long extractedTimestamp) {DateTime date = DateUtil.date(lastElement.f1);return date.minute()==15?new Watermark(lastElement.f1):null;}@Overridepublic long extractTimestamp(Tuple2<String, Long> element, long previousElementTimestamp) {return element.f1;}}).keyBy(value -> value.f0).timeWindow(Time.minutes(10)).process(new ProcessWindowFunction<Tuple2<String, Long>, Object, String, TimeWindow>() {@Overridepublic void process(String key, ProcessWindowFunction<Tuple2<String, Long>, Object, String, TimeWindow>.Context context, Iterable<Tuple2<String, Long>> elements, Collector<Object> out) throws Exception {System.out.println("----------------");System.out.println("系统当前时间:"+ DateUtil.date());System.out.println("当前水位时间:"+DateUtil.date(context.currentWatermark()));System.out.println("窗口开始时间:"+DateUtil.date(context.window().getStart()));System.out.println("窗口结束时间:"+DateUtil.date(context.window().getEnd()));elements.forEach(element -> System.out.println("数据携带时间:"+DateUtil.date(element.f1)));}}).print();

结果如下:

15min的数据生成了15min的水位,只触发了00-10的窗口。

窗口处理迟到的数据

allowedLateness�

Flink提供了WindowedStream.allowedLateness()方法来设定窗口的允许延迟。也就是说,正常情况下窗口触发计算完成之后就会被销毁,但是设定了允许延迟之后,窗口会等待allowedLateness的时长后再销毁。在该区间内的迟到数据仍然可以进入窗口中,并触发新的计算。当然,窗口也是吃资源大户,所以allowedLateness的值要适当。给个完整的代码示例如下。

source.assignTimestampsAndWatermarks(new BoundedOutOfOrdernessTimestampExtractor<Tuple2<String, Long>>(Time.minutes(5)) {@Overridepublic long extractTimestamp(Tuple2<String, Long> element) {return element.f1;}}).keyBy(value -> value.f0).timeWindow(Time.minutes(10)).allowedLateness(Time.minutes(5)).process(new ProcessWindowFunction<Tuple2<String, Long>, Object, String, TimeWindow>() {@Overridepublic void process(String key, ProcessWindowFunction<Tuple2<String, Long>, Object, String, TimeWindow>.Context context, Iterable<Tuple2<String, Long>> elements, Collector<Object> out) throws Exception {System.out.println("----------------");System.out.println("系统当前时间:"+ DateUtil.date());System.out.println("当前水位时间:"+DateUtil.date(context.currentWatermark()));System.out.println("窗口开始时间:"+DateUtil.date(context.window().getStart()));System.out.println("窗口结束时间:"+DateUtil.date(context.window().getEnd()));elements.forEach(element -> System.out.println("数据携带时间:"+DateUtil.date(element.f1)));}}).print();

side output

侧输出(side output)是Flink的分流机制。迟到数据本身可以当做特殊的流,我们通过调用WindowedStream.sideOutputLateData()方法将迟到数据发送到指定OutputTag的侧输出流里去,再进行下一步处理(比如存到外部存储或消息队列)。代码如下。

// 侧输出的OutputTagOutputTag<Tuple2<String, Long>> lateOutputTag = new OutputTag<>("late_data_output_tag");SingleOutputStreamOperator<Object> process = source.assignTimestampsAndWatermarks(new BoundedOutOfOrdernessTimestampExtractor<Tuple2<String, Long>>(Time.minutes(5)) {@Overridepublic long extractTimestamp(Tuple2<String, Long> element) {return element.f1;}}).keyBy(value -> value.f0).timeWindow(Time.minutes(10)).allowedLateness(Time.minutes(5)).sideOutputLateData(lateOutputTag).process(new ProcessWindowFunction<Tuple2<String, Long>, Object, String, TimeWindow>() {@Overridepublic void process(String key, ProcessWindowFunction<Tuple2<String, Long>, Object, String, TimeWindow>.Context context, Iterable<Tuple2<String, Long>> elements, Collector<Object> out) throws Exception {System.out.println("----------------");System.out.println("系统当前时间:" + DateUtil.date());System.out.println("当前水位时间:" + DateUtil.date(context.currentWatermark()));System.out.println("窗口开始时间:" + DateUtil.date(context.window().getStart()));System.out.println("窗口结束时间:" + DateUtil.date(context.window().getEnd()));elements.forEach(element -> System.out.println("数据携带时间:" + DateUtil.date(element.f1)));}});//处理侧输出数据
//        process.getSideOutput(lateOutputTag).addSink()

最后的window不触发解决方法

自定义自增水位

周期性获取watermark时,自定义增加水位

source.assignTimestampsAndWatermarks(new AssignerWithPeriodicWatermarks<Tuple2<String, Long>>() {private long currentTimestamp;@Nullable@Override// 生成watermarkpublic Watermark getCurrentWatermark() {currentTimestamp+=60000;return new Watermark(currentTimestamp);}@Override//获取事件时间public long extractTimestamp(Tuple2<String, Long> element, long previousElementTimestamp) {if (element.f1>=currentTimestamp){currentTimestamp = element.f1;}return element.f1;}})

结果如下:

自定义trigger

当watermark不能满足关窗条件时,我们给注册一个晚于事件时间的处理时间定时器使它一定能达到关窗条件。

import org.apache.flink.streaming.api.windowing.triggers.Trigger;
import org.apache.flink.streaming.api.windowing.triggers.TriggerResult;
import org.apache.flink.streaming.api.windowing.windows.TimeWindow;public class MyTrigger extends Trigger<Object, TimeWindow> {@Overridepublic TriggerResult onElement(Object element, long timestamp, TimeWindow window, TriggerContext ctx) throws Exception {if (window.maxTimestamp() <= ctx.getCurrentWatermark()) {// if the watermark is already past the window fire immediatelyreturn TriggerResult.FIRE;} else {ctx.registerEventTimeTimer(window.maxTimestamp());ctx.registerProcessingTimeTimer(window.maxTimestamp() + 30000L);return TriggerResult.CONTINUE;}}@Overridepublic TriggerResult onProcessingTime(long time, TimeWindow window, TriggerContext ctx) throws Exception {ctx.deleteEventTimeTimer(window.maxTimestamp());return TriggerResult.FIRE;}@Overridepublic TriggerResult onEventTime(long time, TimeWindow window, TriggerContext ctx) throws Exception {if (time == window.maxTimestamp()) {ctx.deleteProcessingTimeTimer(window.maxTimestamp() + 30000L);return TriggerResult.FIRE;} else {return TriggerResult.CONTINUE;}}@Overridepublic void clear(TimeWindow window, TriggerContext ctx) throws Exception {ctx.deleteEventTimeTimer(window.maxTimestamp());ctx.deleteProcessingTimeTimer(window.maxTimestamp() + 30000L);}
}

Test.java

参考链接:

https://www.jianshu.com/p/c612e95a5028

https://blog.csdn.net/lixinkuan328/article/details/104129671

https://nightlies.apache.org/flink/flink-docs-release-1.17/docs/dev/datastream/event-time/generating_watermarks/

https://cloud.tencent.com/developer/article/1573079

https://blog.csdn.net/m0_73707775/article/details/129560540?spm=1001.2101.3001.6650.5&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7ERate-5-129560540-blog-118368717.235%5Ev29%5Epc_relevant_default_base3&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7ERate-5-129560540-blog-118368717.235%5Ev29%5Epc_relevant_default_base3&utm_relevant_index=10

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

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

相关文章

Ubuntu 下测试 NVME SSD 的读写速度

在 Ubuntu 系统下&#xff0c;测试 NVME SSD 的读写速度&#xff0c;有好多种方法&#xff0c;常用的有如下几种&#xff1a; 1. Gnome-disks Gnome-disks&#xff08;也称为“Disks”&#xff09;是 GNOME 桌面环境中的磁盘管理工具&#xff0c;有图形界面&#xff0c;是测试…

SpringBoot之核心配置

学习目标&#xff1a; 1.熟悉Spring Boot全局配置文件的使用 2.掌握Spring Boot配置文件属性值注入 3.熟悉Spring Boot自定义配置 4.掌握Profile多环境配置 5.了解随机值设置以及参数间引用 1.全局配置文件 Spring Boot使用 application.properties 或者application.yaml 的文…

后端服务集成ElasticSearch搜索功能技术方案

文章目录 一、为什么选用ElasticSearch二、ElasticSearch基本概念1、文档和字段2、索引和映射3、倒排索引、文档和词条4、分词器 三、ElasticSearch工作原理1、Term Dictionary、Term index2、Stored Fields3、Docs Values4、Segment5、Lucene6、高性能、高扩展性、高可用①高性…

【JavaEE进阶】获取Cookie/Session

&#x1f340;Cookie简介 HTTP协议自身是属于 "⽆状态"协议. "⽆状态"的含义指的是: 默认情况下 HTTP 协议的客⼾端和服务器之间的这次通信,和下次通信之间没有直接的联系.但是实际开发中,我们很多时候是需要知道请求之间的关联关系的. 例如登陆⽹站成…

《Spring Framework实战》3:概览

欢迎观看《Spring Framework实战》视频教程 Spring Framework 为基于现代 Java 的企业应用程序提供了全面的编程和配置模型 - 在任何类型的部署平台上。 Spring 的一个关键要素是应用程序级别的基础设施支持&#xff1a;Spring 专注于企业应用程序的 “管道”&#xff0c;以便…

借助免费GIS工具箱轻松实现las点云格式到3dtiles格式的转换

在当今数字化浪潮下&#xff0c;地理信息系统&#xff08;GIS&#xff09;技术日新月异&#xff0c;广泛渗透到城市规划、地质勘探、文化遗产保护等诸多领域。而 GISBox 作为一款功能强大且易用的 GIS 工具箱&#xff0c;以轻量级、免费使用、操作便捷等诸多优势&#xff0c;为…

高等数学学习笔记 ☞ 一元函数微分的基础知识

1. 微分的定义 &#xff08;1&#xff09;定义&#xff1a;设函数在点的某领域内有定义&#xff0c;取附近的点&#xff0c;对应的函数值分别为和&#xff0c; 令&#xff0c;若可以表示成&#xff0c;则称函数在点是可微的。 【 若函数在点是可微的&#xff0c;则可以表达为】…

linux之自动挂载

如果想要实现自动挂载&#xff0c;应该挂在客户端&#xff01;&#xff01;&#xff01;&#xff01;&#xff01; 客户端&#xff1a; [rootlocalhost ~]# yum install nfs-utils -y &#xff08;下载软件&#xff09; [rootlocalhost ~]# systemctl start nfs-utils.servic…

用户界面软件01

Jens Coldewey 著&#xff0c;Tom.X 译 本文中的模式语言逐步深入地探讨用户界面架构的设计&#xff0c;它基于人机工程学&#xff0c;足以形成一套完整的体系。如果你对这方面有兴趣&#xff0c;请参考[Tog92]&#xff0c;[Coo95]和[Col95]。 本文不讨论用户界面的布局&…

2025.1.8(c++对c语言的扩充——堆区空间,引用,函数)

笔记 上一笔记接续&#xff08;练习2的答案&#xff09; 练习&#xff1a;要求在堆区连续申请5个int的大小空间用于存储5名学生的成绩&#xff0c;分别完成空间的申请、成绩的录入、升序排序、成绩输出函数以及空间释放函数&#xff0c;并在主程序中完成测试 要求使用new和d…

(长期更新)《零基础入门 ArcGIS(ArcScene) 》实验七----城市三维建模与分析(超超超详细!!!)

城市三维建模与分析 三维城市模型已经成为一种非常普遍的地理空间数据资源,成为城市的必需品,对城市能化管理至关重要。语义信息丰富的三维城市模型可以有效实现不同领域数据与IS相信息的高层次集成及互操作,从而在城市规划、环境模拟、应急响应和辅助决策等众多领域公挥作用、…

Harmony开发【笔记1】报错解决(字段名写错了。。)

在利用axios从网络接收请求时&#xff0c;发现返回obj的code为“-1”&#xff0c;非常不解&#xff0c;利用console.log测试&#xff0c;更加不解&#xff0c;可知抛出错误是 “ E 其他错误: userName required”。但是我在测试时&#xff0c;它并没有体现为空&#xff0c;…

(2023|NIPS,LLaVA-Med,生物医学 VLM,GPT-4 生成自指导指令跟随数据集,数据对齐,指令调优)

LLaVA-Med: Training a Large Language-and-Vision Assistant for Biomedicine in One Day 目录 LLaVA-Med: Training a Large Language-and-Vision Assistant for Biomedicine in One Day 0. 摘要 1. 简介 2. 相关工作 3. 生物医学视觉指令数据 4. 将多模态对话模型适配…

(概率论)无偏估计

参考文章&#xff1a;(15 封私信 / 51 条消息) 什么是无偏估计&#xff1f; - 知乎 (zhihu.com) 首先&#xff0c;第一个回答中&#xff0c;马同学图解数学讲解得很形象&#xff0c; 我的概括是&#xff1a;“注意&#xff0c;有一个总体的均值u。然后&#xff0c;如果抽样n个&…

国产游戏崛起,燕云十六移动端1.9上线,ToDesk云电脑先开玩

游戏爱好者的利好消息出新了&#xff01;网易大型武侠仙游《燕云十六声》正式官宣&#xff0c;移动端要在1月9日正式上线了&#xff01;你期待手游版的燕云吗&#xff1f;不妨评论区留言说说你的看法。小编分别花了几个小时在台式机电脑和手机上都试了下&#xff0c;欣赏画面还…

VS2022引入sqlite数据库交互

法一&#xff1a;用官网编译好的动态库(推荐) 下载所需文件 sqlite官网地址&#xff1a;https://www.sqlite.org/howtocompile.html 下载以下的2个压缩包 第一个压缩包 sqlite-amalgamation-xxxx.zip&#xff0c;xxxx是版本号,保持一致即可&#xff0c;这里面有sqite3.h 第…

Java后端常用的4种请求方式(通俗易懂)

文章目录 前言通用接口类(ControllerDemo)通用实体类(UserEntity)通用响应类(HttpClientResult)成功截图(先启动项目,然后右键执行main方法) HttpClientHttpClient 的主要类代码案例导入依赖工具类(HttpClientUtil)测试类 HttpURLConnection简介调用步骤代码案例导入依赖工具类…

STM32烧写失败之Contents mismatch at: 0800005CH (Flash=FFH Required=29H) !

一&#xff09;问题&#xff1a;用ULINK2给STM32F103C8T6下载程序&#xff0c;下载方式设置如下&#xff1a; 出现下面两个问题&#xff1a; 1&#xff09;下载问题界面如下&#xff1a; 这个错误的信息大概可以理解为&#xff0c;在0x08000063地址上读取到flash存储为FF&am…

Spring Boot教程之五十二:CrudRepository 和 JpaRepository 之间的区别

Spring Boot – CrudRepository 和 JpaRepository 之间的区别 Spring Boot建立在 Spring 之上&#xff0c;包含 Spring 的所有功能。由于其快速的生产就绪环境&#xff0c;使开发人员能够直接专注于逻辑&#xff0c;而不必费力配置和设置&#xff0c;因此如今它正成为开发人员…

【SQL】Delete使用

目录 语法 需求 示例 分析 代码 语法 DELETE删除表中所需内容 删除表中满足特点条件的行&#xff1a;DELETE FROM 表名 WHERE 条件; 删除表中所有行&#xff1a;DELETE FROM 表名; WHERE子句 WHERE子句用于指定从表中选取记录的条件。允许筛选数据&#xff0c;只返回满足…