news 2026/10/21 15:25:27

Flink本地文件状态持久化:RocksDBStateBackend实战指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Flink本地文件状态持久化:RocksDBStateBackend实战指南

1. 为什么非得用本地文件存Flink状态?——从“重启就丢数”说起

我第一次在某跨平台实时日志分析项目里遇到这个问题,是在凌晨两点。当时系统刚上线第三天,一个上游Kafka集群突发网络抖动,Flink作业自动触发了Checkpoint失败重试机制。我们按常规操作做了手动重启——结果发现,所有基于窗口的累计PV/UV指标全归零了,下游告警电话直接打爆。排查半小时后才确认:不是代码逻辑错,也不是Checkpoint配置漏,而是整个作业的状态后端(State Backend)压根没配对。

Flink的状态管理机制,本质上是一套“内存+持久化”的双层设计。算子运行时把最新状态存在TaskManager的JVM堆内(快),但必须定期把快照刷到外部存储(稳)。这个“外部存储”就是State Backend。很多人默认以为只要开了Checkpoint,数据就“自动落盘”,其实完全不是——Flink默认使用的是MemoryStateBackend,它只把状态快照序列化后存在JobManager的内存里,一旦JobManager进程挂掉或重启,快照立刻消失。这就像你写文档只开自动保存,但没连U盘,电脑一断电,最后十分钟全没了。

而标题里说的“通过本地文件持久化算子状态”,核心要解决的,正是这个“稳”字:不依赖HDFS、S3或远程数据库,只用TaskManager本机磁盘,实现状态可落地、可恢复、可验证。它特别适合三类场景:一是离线开发与单机调试阶段,没有分布式存储环境;二是边缘计算节点,网络带宽受限或无外网连接;三是安全合规要求严格、禁止状态出域的封闭系统。它不是生产环境首选,但却是理解Flink状态生命周期最干净的“解剖样本”。

关键词里虽然空着,但根据标题和实际工程语境,我们必须锚定四个不可绕过的术语:RocksDBStateBackend(唯一支持本地文件持久化的后端)、local filesystem(非hdfs://或s3a://,而是file:///路径)、state.checkpoints.dir(Checkpoint快照落盘位置)、state.savepoints.dir(Savepoint手动触发快照位置)。这四个词,就是整套方案的四根承重柱。少一根,状态就悬在半空。

提示:别被“本地文件”四个字骗了——它不等于“随便往/tmp写个txt”。Flink对本地文件系统的读写有强一致性要求:必须支持原子性rename、硬链接、fsync刷盘。Linux ext4、XFS没问题;macOS默认APFS在某些内核版本下会因硬链接行为异常导致Checkpoint失败;Windows NTFS虽支持,但需关闭防病毒软件实时扫描,否则I/O阻塞超时。这些细节,文档里不会明说,但实测踩过坑的人一眼就懂。

2. RocksDBStateBackend不是“开关”,而是一套精密装配流程

很多人以为切换State Backend就是改一行配置:“state.backend: rocksdb”。错了。RocksDBStateBackend是Flink中唯一能将状态以本地SST文件形式持久化的后端,但它本身不负责“存到哪”,只负责“怎么存”。它的落盘路径、目录结构、线程模型、内存分配,全部需要显式声明和协同配置。把它当成一个插件,而不是一个开关,才能真正掌控状态生命周期。

2.1 路径配置的双重陷阱:checkpoints.dir 与 savepoints.dir 必须分离

Flink要求明确指定两个独立路径:

  • state.checkpoints.dir:用于自动触发的Checkpoint快照,路径格式必须为file:///absolute/path/to/checkpoints
  • state.savepoints.dir:用于手动触发的Savepoint快照,路径格式同样必须为file:///absolute/path/to/savepoints

注意三个强制约束:

  1. 绝对路径是铁律:./checkpoints或checkpoints/这类相对路径会被Flink静默忽略,作业启动时看似正常,但Checkpoint永远不生成——因为Flink内部解析时发现协议头缺失,直接跳过。
  2. 两个目录绝不能相同:如果都指向/data/flink/checkpoints,Flink会在首次Savepoint时抛出IllegalStateException: Checkpoint directory and savepoint directory must be different。这不是bug,是设计使然——Checkpoint是周期性覆盖写入,Savepoint是用户主动创建的“快照快照”,必须物理隔离避免误删。
  3. 父目录权限必须由Flink进程用户完全控制:假设Flink以flink-user用户启动,那么/data/flink/checkpoints的owner必须是flink-user,且至少具备rwx权限。实测中曾遇到SELinux启用的CentOS系统,即使chmod 755,也会因策略限制导致RocksDB打开WAL日志文件失败,报错IO error: While lock file: /data/flink/checkpoints/xxx/LOCK: Permission denied。解决方案不是关SELinux,而是执行chcon -t svirt_sandbox_file_t /data/flink/checkpoints。

我们来模拟一个真实配置片段(flink-conf.yaml):

# 状态后端类型 state.backend: rocksdb # Checkpoint自动快照路径(必须file://协议+绝对路径) state.checkpoints.dir: file:///data/flink/checkpoints # Savepoint手动快照路径(同上,且必须与checkpoints.dir不同) state.savepoints.dir: file:///data/flink/savepoints # 启用增量Checkpoint(强烈推荐,大幅降低本地磁盘I/O压力) state.checkpoints.incremental: true # 检查点间隔:30秒一次,平衡恢复速度与性能开销 execution.checkpointing.interval: 30s # 检查点超时:60秒,防止大状态卡住整个流水线 execution.checkpointing.timeout: 60s # 最小暂停时间:两次Checkpoint之间至少间隔10秒,避免连续I/O风暴 execution.checkpointing.min-pause: 10s

这段配置里,state.checkpoints.incremental: true是关键中的关键。RocksDB的增量Checkpoint原理是:每次只上传自上次Checkpoint以来发生变更的SST文件(Sorted String Table),而非全量复制整个RocksDB目录。在单机调试场景下,一个含10GB状态的WordCount作业,全量Checkpoint可能耗时45秒、写入10GB磁盘;而增量模式下,通常只需上传几十MB新SST文件,耗时压到8秒内。这不是参数优化,而是架构级降压。

2.2 RocksDB本地存储的目录结构解密:每个文件都在讲一个故事

当你在/data/flink/checkpoints下看到类似这样的目录树:

checkpoints/ ├── 9a2b3c4d-5e6f-7g8h-9i0j-1k2l3m4n5o6p/ # Checkpoint ID(UUID) │ ├── _metadata # 元数据文件:记录本次Checkpoint包含哪些算子、状态大小、时间戳 │ ├── shared/ # 增量模式下:共享的SST文件(未被任何Checkpoint独占) │ │ ├── 000010.sst │ │ └── 000011.sst │ └── taskowned/ # 算子独占的SST文件(仅被当前Checkpoint引用) │ └── 000012.sst └── 9a2b3c4d-5e6f-7g8h-9i0j-1k2l3m4n5o6q/ ├── _metadata ├── shared/ │ └── 000010.sst # 注意:此文件在上一个Checkpoint中已存在,本次复用 └── taskowned/ └── 000013.sst

这个结构揭示了Flink状态恢复的本质:恢复不是“加载整个目录”,而是“按元数据索引,精准定位所需SST文件”。_metadata文件是字典,shared/是公共资源池,taskowned/是私有资产。当作业重启时,Flink读取最新_metadata,发现需要000010.sst和000013.sst,就去shared/和taskowned/中分别加载——哪怕000010.sst是三天前生成的,只要没被GC回收,就能复用。

这就引出了本地文件模式下最易被忽视的运维点:SST文件的GC(垃圾回收)策略。RocksDB默认启用ttl(Time-To-Live)机制,但Flink并未暴露该参数给用户。实际生效的是Flink自身的Checkpoint保留策略:

# 保留最近3个完整的Checkpoint(含增量链) state.checkpoints.num-retained: 3 # 过期Checkpoint自动清理(单位:小时) state.checkpoints.cleanup: true

这意味着:如果每30秒一个Checkpoint,Flink只会保留最近3个Checkpoint目录及其引用的所有SST文件(包括shared/中被多个Checkpoint共用的文件),其余SST文件会被标记为“可删除”。但注意:shared/中的文件只有在所有引用它的Checkpoint都被清理后,才会真正删除。所以你可能看到/data/flink/checkpoints/shared/下堆积了上百个SST文件,但这不是泄漏,是正常缓存。

注意:不要手动rm -rf某个Checkpoint目录!Flink的GC是原子操作,手动删除会导致_metadata与实际文件不一致,下次重启时抛出FileNotFoundException。必须通过Flink Web UI的“Cancel Job with Savepoint”或命令行./flink cancel -s file:///path/to/savepoint <jobId>触发受控清理。

3. 从零写一个可验证的WordCount Demo:状态恢复的每一步都看得见

光说不练假把式。下面我带你手写一个极简但完整的Flink流处理作业,它用file://后端,跑通“启动→注入数据→Checkpoint→杀进程→重启→状态续算”全流程。所有代码可直接粘贴运行,无需修改路径(默认使用/tmp/flink-local-state)。

3.1 Maven依赖:精简到只剩骨架

<dependencies> <!-- Flink核心 --> <dependency> <groupId>org.apache.flink</groupId> <artifactId>flink-streaming-java</artifactId> <version>1.17.1</version> </dependency> <!-- RocksDB状态后端(必须显式引入) --> <dependency> <groupId>org.apache.flink</groupId> <artifactId>flink-statebackend-rocksdb</artifactId> <version>1.17.1</version> </dependency> <!-- 日志框架 --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-simple</artifactId> <version>1.7.36</version> </dependency> </dependencies>

关键点:flink-statebackend-rocksdb依赖必须显式声明。Flink 1.15+ 版本已将RocksDB后端从flink-dist中剥离,若遗漏此依赖,作业启动时会报ClassNotFoundException: org.apache.flink.contrib.streaming.state.RocksDBStateBackend,错误信息极其隐蔽。

3.2 Java主程序:埋点、打印、可调试

import org.apache.flink.api.common.eventtime.WatermarkStrategy; import org.apache.flink.api.common.restartstrategy.RestartStrategies; import org.apache.flink.api.common.state.ValueState; import org.apache.flink.api.common.state.ValueStateDescriptor; import org.apache.flink.api.common.time.Time; import org.apache.flink.api.common.typeinfo.Types; import org.apache.flink.api.java.tuple.Tuple2; import org.apache.flink.configuration.Configuration; import org.apache.flink.core.fs.FileSystem; import org.apache.flink.core.fs.Path; import org.apache.flink.runtime.state.filesystem.FsStateBackend; import org.apache.flink.runtime.state.hashmap.HashMapStateBackend; import org.apache.flink.runtime.state.storage.FileSystemCheckpointStorage; import org.apache.flink.streaming.api.CheckpointingMode; import org.apache.flink.streaming.api.datastream.DataStream; import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment; import org.apache.flink.streaming.api.functions.KeyedProcessFunction; import org.apache.flink.util.Collector; import java.time.Duration; import java.util.UUID; public class LocalFileStateDemo { public static void main(String[] args) throws Exception { final StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment(); // ====== 关键1:配置本地文件State Backend ====== // 创建FileSystemCheckpointStorage,指向本地file://路径 FileSystemCheckpointStorage storage = new FileSystemCheckpointStorage( "file:///tmp/flink-local-state/checkpoints" ); // 设置Savepoint路径(必须单独指定) env.getCheckpointConfig().setSavepointDirectory("file:///tmp/flink-local-state/savepoints"); // 构建RocksDBStateBackend(注意:必须传入storage) FsStateBackend fsBackend = new FsStateBackend( "file:///tmp/flink-local-state/checkpoints", true // enable incremental checkpointing ); // ⚠️ 核心:设置State Backend为RocksDB(非FsStateBackend!) // RocksDBStateBackend是包装类,内部持有fsBackend env.setStateBackend(new org.apache.flink.contrib.streaming.state.RocksDBStateBackend( fsBackend, true // enable incremental )); // ====== 关键2:启用Checkpoint ====== env.enableCheckpointing(30_000, CheckpointingMode.EXACTLY_ONCE); env.getCheckpointConfig().setMinPauseBetweenCheckpoints(10_000); env.getCheckpointConfig().setCheckpointTimeout(60_000); env.getCheckpointConfig().setMaxConcurrentCheckpoints(1); env.getCheckpointConfig().enableExternalizedCheckpoints( CheckpointingConfig.ExternalizedCheckpointCleanup.RETAIN_ON_CANCELLATION ); // ====== 关键3:设置重启策略(本地调试必备)====== env.setRestartStrategy(RestartStrategies.fixedDelayRestart( 3, // 尝试重启3次 Time.seconds(10) // 每次间隔10秒 )); // ====== 数据源:从Socket读取(方便手动注入)====== DataStream<String> socketStream = env.socketTextStream("localhost", 9999); // ====== 核心业务:带状态的WordCount(KeyedProcessFunction)====== DataStream<Tuple2<String, Long>> result = socketStream .filter(line -> !line.trim().isEmpty()) .flatMap((String line, Collector<String> out) -> { for (String word : line.split("\\s+")) { out.collect(word.toLowerCase()); } }).returns(Types.STRING) .keyBy(word -> word) // 按单词分组 .process(new CountWithStateFunction()); result.print("WordCount-Result"); env.execute("LocalFileStateDemo"); } // 自定义函数:维护每个单词的计数状态 public static class CountWithStateFunction extends KeyedProcessFunction<String, String, Tuple2<String, Long>> { // 声明ValueState:存储当前单词的计数 private ValueState<Long> countState; @Override public void open(Configuration parameters) throws Exception { ValueStateDescriptor<Long> descriptor = new ValueStateDescriptor<>( "word-count", // state name Types.LONG // type info ); countState = getRuntimeContext().getState(descriptor); } @Override public void processElement(String value, Context ctx, Collector<Tuple2<String, Long>> out) throws Exception { Long currentCount = countState.value(); if (currentCount == null) { currentCount = 0L; } currentCount += 1; countState.update(currentCount); // 每处理一个单词,打印当前状态(用于验证恢复) System.out.printf("[State] Word '%s' count = %d%n", value, currentCount); out.collect(Tuple2.of(value, currentCount)); } } }

这段代码有三个“可验证设计”:

  • 打印状态:System.out.printf直接输出每个单词的实时计数,重启前后对比一目了然;
  • Socket数据源:用nc -lk 9999即可手动发数据,无需Kafka集群;
  • Externalized Checkpoint:RETAIN_ON_CANCELLATION确保作业取消后Checkpoint不被自动删除,为重启留档。

3.3 完整验证流程:像拆解钟表一样看状态如何复活

Step 1:准备环境

# 创建本地状态目录(确保权限正确) mkdir -p /tmp/flink-local-state/{checkpoints,savepoints} # 启动Socket服务(另开终端) nc -lk 9999

Step 2:首次启动并注入数据

# 编译并运行(假设打包为flink-demo.jar) java -jar flink-demo.jar

在nc终端输入:

hello world hello flink world flink

观察控制台输出:

[State] Word 'hello' count = 1 [State] Word 'world' count = 1 [State] Word 'hello' count = 2 [State] Word 'flink' count = 1 [State] Word 'world' count = 2 [State] Word 'flink' count = 2

此时,/tmp/flink-local-state/checkpoints/下应已生成至少1个UUID命名的目录(如a1b2c3d4-...),内含_metadata和shared/。

Step 3:手动触发Savepoint(强制落盘)

# 查找正在运行的Job ID(从Web UI或日志中复制) # 执行Savepoint命令 ./flink savepoint <job-id> file:///tmp/flink-local-state/savepoints

成功后,/tmp/flink-local-state/savepoints/下会出现savepoint-xxxxxx/目录。

Step 4:暴力杀进程 + 重启

# Ctrl+C终止Java进程(模拟崩溃) # 等待几秒,确保进程完全退出 # 重新运行同一Jar java -jar flink-demo.jar

关键观察点:重启后,控制台第一行应立即打印:

[State] Word 'hello' count = 2 [State] Word 'world' count = 2 [State] Word 'flink' count = 2

这证明:Flink在open()阶段已从/tmp/flink-local-state/checkpoints/中加载了之前的状态,countState.value()直接返回了2,而非从0开始累加。

实操心得:如果重启后计数从0开始,90%概率是路径配置错误(比如用了相对路径)或RocksDB依赖缺失;如果报FileNotFoundException,则是手动删过Checkpoint目录。记住:Flink的恢复日志在log/flink-*-standalonesession-*.log中,搜索Restoring state from即可定位加载源。

4. 生产级避坑指南:本地文件模式的七宗罪与解法

用本地文件做状态后端,绝非“开发爽一时,上线火葬场”。它是一把双刃剑,用得好是调试利器,用不好是线上事故温床。以下是我在多个项目中总结的七条血泪教训,每一条都对应一个真实故障场景。

4.1 罪状一:磁盘空间告急 → 状态写满根分区

现象:作业运行2小时后突然停止Checkpoint,日志报IOException: No space left on device,但df -h显示/分区还有20%剩余。

根因:RocksDB的Write-Ahead Log(WAL)默认写入/tmp,而/tmp通常是/分区下的子目录。一个高吞吐作业的WAL文件可达GB级,瞬间撑爆根分区。

解法:显式配置WAL路径到大容量磁盘

// 在env.setStateBackend()之前添加 Configuration config = new Configuration(); config.setString("state.backend.rocksdb.localdir", "/data/rocksdb-wal"); // 独立大磁盘 env.configure(config, LocalFileStateDemo.class.getClassLoader());

同时,在flink-conf.yaml中补充:

state.backend.rocksdb.localdir: /data/rocksdb-wal

注意:localdir路径必须存在且Flink用户有写权限,否则启动失败。建议提前mkdir -p /data/rocksdb-wal && chown flink-user:flink-user /data/rocksdb-wal。

4.2 罪状二:Checkpoint超时 → 状态太大,本地I/O扛不住

现象:execution.checkpointing.timeout: 60s配置下,Checkpoint频繁超时,state.checkpoints.dir下生成大量.inprogress临时文件。

根因:单机磁盘IOPS有限。RocksDB在Checkpoint时需将内存中所有MemTable flush到SST,并同步WAL。一个100GB状态的作业,在普通SATA盘上可能需200秒才能完成。

解法:三管齐下

  1. 升硬件:强制使用SSD,NVMe最佳;
  2. 调参数:降低state.backend.rocksdb.options中的max_background_jobs(默认2),设为1,减少并发写入竞争;
  3. 换策略:关闭state.checkpoints.incremental: false,改用全量Checkpoint(牺牲磁盘空间换时间)。

4.3 罪状三:状态不一致 → 多TaskManager共享同一本地路径

现象:在YARN或Kubernetes上部署时,多个TaskManager容器挂载了同一个NFS目录作为state.checkpoints.dir,作业重启后状态混乱,部分单词计数重复。

根因:RocksDB的SST文件名由进程ID和时间戳生成,多进程写入同一目录必然冲突。NFS的缓存一致性模型无法保证Flink所需的原子rename语义。

解法:绝对禁止共享本地路径!每个TaskManager必须有独占的本地路径。在K8s中,通过emptyDir卷实现:

volumeMounts: - name: flink-state mountPath: /data/flink/state volumes: - name: flink-state emptyDir: {}

然后在Flink配置中动态注入:

state.checkpoints.dir: file:///data/flink/state/checkpoints

4.4 罪状四:恢复失败 → Savepoint路径协议错误

现象:./flink run -s file:///path/to/savepoint ...报Scheme 'file' not supported。

根因:Flink CLI的-s参数只支持hdfs://或s3a://等远程协议,不支持file://!这是官方文档的隐藏雷区。

解法:必须用file:(无斜杠)协议:

./flink run -s file:/tmp/flink-local-state/savepoints/savepoint-abc123 ./flink-demo.jar

或者,更稳妥的方式:先将Savepoint拷贝到HDFS/S3,再用标准协议恢复。

4.5 罪状五:状态泄漏 → 重启后旧状态残留

现象:作业重启后,countState.value()返回一个极大随机数(如9223372036854775807),后续计算全错。

根因:ValueState未初始化,countState.value()返回null,但代码中未判空直接参与运算,JVM将null自动拆箱为0L,再经多次+=1后溢出。

解法:永远检查state.value() == null

Long currentCount = countState.value(); if (currentCount == null) { currentCount = 0L; // 显式初始化 } currentCount += 1; countState.update(currentCount);

4.6 罪状六:元数据损坏 → _metadata文件被意外修改

现象:重启时报Corruption: checksum mismatch,指向_metadata文件。

根因:运维人员用vim编辑了_metadata(二进制文件!),或磁盘坏道导致文件损坏。

解法:_metadata是Flink自动生成的Protobuf序列化文件,绝对禁止人工编辑。修复方法只有两个:1)从上一个完好的Checkpoint目录复制_metadata;2)放弃恢复,从Source重新消费。

4.7 罪状七:版本不兼容 → 升级Flink后状态无法加载

现象:从Flink 1.15升级到1.17,重启作业报Unknown version: 123。

根因:RocksDB的SST文件格式和_metadata结构随Flink版本演进。Flink 1.16+ 引入了新的状态序列化协议(StateSerializerProvider),旧版本生成的SST无法被新版本解析。

解法:升级前必须做状态兼容性验证:

  1. 在旧版本Flink上触发Savepoint;
  2. 将Savepoint拷贝到新版本环境;
  3. 用./flink savepoint -d <savepoint-path>命令校验(-d表示dry-run);
  4. 仅当校验通过,才执行正式升级。

最后分享一个小技巧:在flink-conf.yaml中加入state.backend.rocksdb.predefined-options: DEFAULT,可强制使用Flink内置的、经过充分测试的RocksDB参数组合,避免因手动调优引发的未知问题。这个选项在1.16+版本中默认开启,但显式声明更安心。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/21 15:25:13

IDA Pro逆向分析提速:xAnalyzer插件自动识别函数结构与调用约定

逆向分析这个领域&#xff0c;花时间最多的往往不是“看不懂指令”&#xff0c;而是“看不透函数”。尤其是那些没有符号的老代码&#xff0c;对着反汇编窗口里一片push、mov、sub esp&#xff0c;你盯着看半天&#xff0c;还是分不清哪个是参数、哪个是局部变量&#xff0c;调…

作者头像 李华
网站建设 2026/10/21 15:24:31

统一大模型调用网关litellm:多模型接入与成本管控实践

在接大模型应用的时候&#xff0c;最让人头疼的往往不是模型效果&#xff0c;而是工程接入那一堆琐碎事。今天聊的这个项目“litellm”&#xff0c;如果你在多模型接入、API网关、成本管控上有过切肤之痛&#xff0c;那它大概率能帮你省下不少加班时间。简单来说&#xff0c;li…

作者头像 李华
网站建设 2026/10/21 15:24:29

知识工作插件方案:从零搭建高效知识库的完整指南

开头部分先用一个知识工作者的真实困境切入&#xff0c;解释这个标题指的是什么、解决什么问题、适合谁读。然后主体展开&#xff1a;先是插件方案的整体设计思路&#xff0c;接着是核心插件拆解&#xff0c;再到完整搭建实操&#xff0c;然后是问题排查&#xff0c;最后补一段…

作者头像 李华
网站建设 2026/10/21 15:23:42

Linux下MySQL自动备份实战:脚本设计、定时任务与排错指南

说实话&#xff0c;Linux服务器的MySQL数据库备份这件事&#xff0c;属于那种“平时没人提&#xff0c;出事全怪你”的活儿。我自己管理过好几台业务服务器&#xff0c;最清楚数据库裸奔的风险&#xff1a;磁盘坏了、误删表、升级翻车、被入侵清库&#xff0c;任何一件事发生&a…

作者头像 李华
网站建设 2026/10/21 15:22:53

爬虫练手到数据分析:Scrapy+Pandas+PyECharts漫画数据全链路实战

1. 从爬虫练手到数据分析&#xff1a;这个漫画项目到底在做什么先说个大多数人的通病&#xff1a;一提起爬虫练手项目&#xff0c;第一反应就是去爬新闻、爬电商商品、爬招聘职位&#xff0c;最后爬下来的数据除了证明“我能爬到”之外&#xff0c;几乎没有任何后续。我自己早年…

作者头像 李华
网站建设 2026/10/21 15:22:41

四类垃圾3000张数据集:从数据体检到模型训练全流程

简介&#xff1a;这份资源是专门面向生活垃圾智能分类赛项与目标检测训练整理的标注数据集&#xff0c;包含约三千张四类实拍生活垃圾图像&#xff0c;覆盖有害垃圾&#xff08;电池、过期药品&#xff09;、可回收垃圾&#xff08;易拉罐、小号矿泉水瓶&#xff09;、厨余垃圾…

作者头像 李华