尧图网站建设 尧图网络
  • 首页
  • 关于我们
  • 服务项目
  • 案例展示
  • 建站流程
  • 资讯中心
  • 联系我们
首页/资讯中心/详情

Hudi文件布局深度解析:从磁盘结构理解COW与MOR底层机制

Hudi文件布局深度解析:从磁盘结构理解COW与MOR底层机制
📅 发布时间:2026/7/10 4:02:58

1. 项目概述:为什么“Hudi文件布局”值得花一整篇深度拆解?

如果你正在用 Hudi 构建实时数仓、做 CDC 数据入湖、或者天天和 Flink + Hudi 搭配写作业,却还只停留在INSERT INTO和MERGE INTO的表层语法——那恭喜你,已经踩进了性能瓶颈的隐形陷阱。我带过三个从 Hive 迁移到 Hudi 的中型数据平台项目,其中两个上线后查询变慢、小文件爆炸、压缩任务卡死,排查三天才发现问题根子不在 SQL 写法,也不在集群资源,而是在对Hudi 文件布局(Storage Layouts)的理解几乎为零。这不是概念题,是实打实影响吞吐、延迟、存储成本、运维稳定性的底层骨架。

Hudi 的文件布局,不是目录结构的“样子”,而是它如何组织数据生命周期的“逻辑”。它决定了:一条更新记录是立刻落盘成 Parquet 还是先攒进 Log;一个分区里到底有多少个文件组、多少个切片、哪些能被快照读到、哪些必须等压缩合并;甚至决定了 Flink Checkpoint 失败时,你能不能安全回滚而不丢数据。它把时间线(Timeline)、表类型(COW/MOR)、索引机制、压缩策略、清理策略全部串成一根链条——而文件布局,就是这根链条的物理载体。

这篇文章不讲官网复述,不列配置参数堆砌,而是以一个真实生产环境中的 MOR 表为例,从hdfs dfs -ls /path/to/table看到的第一行输出开始,一层层剥开.hoodie/目录下的元数据、每个fileid-xxx文件组内部的.parquet和.log.如何协同、为什么同一个 fileid 下会有20240901082345.log.1和20240901082345.log.2并存、压缩操作究竟在磁盘上做了什么原子动作、以及当你执行hoodie.cleaner.policy=KEEP_LATEST_FILE_VERSIONS时,Hudi 是怎么算出“最新 3 个版本”并精准删掉旧切片的。所有解释都带现场命令、真实路径片段、字节级结构图示(文字描述),目标就一个:让你下次看到Failed to read log file: xxx.log.3报错时,能直接定位到是日志块校验失败还是时间线状态不一致,而不是先去翻 Apache JIRA。

关键词“Hudi”和“文件布局”不是标签,是锚点——全文所有技术细节都紧扣这两个词展开,不发散、不炫技、不讲 Spark 源码编译,只聚焦“磁盘上发生了什么”。

2. 文件布局整体设计与思路拆解:为什么 Hudi 不直接用 Hive 那套目录结构?

2.1 Hive 的简单分区 vs Hudi 的多维分层:根本矛盾在哪?

Hive 表的存储结构非常直白:/db/tbl/dt=2024-09-01/hour=08/000001_0。它本质是静态快照——每次 INSERT OVERWRITE 就是全量替换整个分区目录。这种设计在 T+1 场景下足够健壮,但一旦要支持秒级更新、增量消费、事务一致性,立刻崩盘。原因有三:

  • 无事务语义:Hive 的ALTER TABLE ... ADD PARTITION只是元数据操作,底层文件写入没有原子性保证。如果写入一半失败,分区就处于半残缺状态;
  • 无版本控制:无法回答“某条记录在 10:00 和 10:05 分别是什么值”,因为旧文件已被覆盖或删除;
  • 无增量标识:下游想消费“过去 5 分钟新增数据”,只能靠时间戳字段过滤,但该字段可能被业务误写、缺失、或存在时区歧义。

Hudi 的文件布局,就是为彻底解决这三个问题而生的。它不追求“目录好看”,而追求“行为可溯、状态可控、演进可逆”。其核心设计哲学是:用文件系统原语模拟数据库的 WAL(Write-Ahead Log)+ MVCC(Multi-Version Concurrency Control)机制。这个目标直接决定了它的四层物理结构:

  1. 基础路径(Base Path):表的根目录,如/data/warehouse/sales_orders;
  2. 分区路径(Partition Path):按用户定义的分区列(如dt,region)生成,与 Hive 兼容,但仅作逻辑隔离;
  3. 文件组(File Group):每个分区下,按file_id(UUID)划分的逻辑单元,是 Hudi 管理数据更新的最小粒度;
  4. 文件切片(File Slice):每个文件组内,由一次提交(Commit)生成的“基础文件 + 对应日志文件集合”,代表该 file_id 在某一时刻的数据快照。

提示:很多人误以为file_id是随机生成的,其实它是根据记录的主键(record key)和分区路径(partition path)通过 Murmur3 哈希计算得出的。这意味着:相同主键的记录,无论何时写入,永远落在同一个 file_id 下——这是实现高效 Upsert 的基石。我们后面会用hudi-cli实际验证这个哈希过程。

2.2 COW 与 MOR 的布局差异:不是“选一个”,而是“选一套行为契约”

Hudi 提供 COW(Copy on Write)和 MOR(Merge on Read)两种表类型,它们的文件布局差异绝非只是“多几个 .log 文件”那么简单,而是代表了两种完全不同的数据一致性模型和读写权衡。

维度COW 表MOR 表
基础文件生成时机每次写入(INSERT/UPDATE/DELETE)都立即触发 Parquet 文件重写写入只生成 .log 文件,Parquet 文件仅在压缩(Compaction)时生成
读取时数据可见性快照查询(Snapshot Query)直接读最新 Parquet,强一致性快照查询需实时合并 .log + .parquet,最终一致性(默认)
写入吞吐低:每次 UPDATE 都要重写整个 Parquet 文件(即使只改一行)高:Log 文件追加写,I/O 成本极低
读取延迟低:Parquet 列式扫描快中高:需运行时合并,尤其当 Log 文件过多时
存储放大小:无冗余 Log 文件大:Log 文件长期存在,直到压缩或清理

关键洞察在于:MOR 表的文件布局天然支持“写优化”和“读优化”的分离。你可以用 Flink SQL 以 MOR 模式持续写入(高吞吐),同时用 Presto 或 Trino 以 RO(Read Optimized)视图查询(低延迟),两者互不干扰。而 COW 表的“读写一体”特性,在高频小批量更新场景下,极易因频繁重写 Parquet 导致小文件泛滥。

注意:MOR 表的“最终一致性”不是缺陷,而是设计选择。Hudi 通过时间线(Timeline)上的 Instant 状态(REQUESTED/INFLIGHT/COMPLETED)严格保证:只要一个 Commit 标记为 COMPLETED,其对应的所有 .log 文件就已持久化且可被后续 Compaction 安全读取。不存在“部分写入成功”的中间态。

2.3 时间线(Timeline)如何成为文件布局的“总调度室”?

文件布局的物理结构,必须由一个逻辑控制器来协调,否则就会变成一堆无法关联的碎片文件。这个控制器就是 Hudi 的时间线(Timeline)。它不是一个抽象概念,而是实实在在存储在.hoodie/目录下的 JSON 文件集合:

# 进入表根目录 $ hdfs dfs -ls /data/warehouse/sales_orders/.hoodie/ Found 3 items drwxr-xr-x - hive hive 0 2024-09-01 08:23 /data/warehouse/sales_orders/.hoodie/archived -rw-r--r-- 1 hive hive 1024 2024-09-01 08:23 /data/warehouse/sales_orders/.hoodie/hoodie.properties drwxr-xr-x - hive hive 0 2024-09-01 08:23 /data/warehouse/sales_orders/.hoodie/metadata

其中:

  • hoodie.properties:表的全局配置,定义了hoodie.table.type=MOR、hoodie.table.base.file.format=PARQUET等核心元信息;
  • archived/:归档的历史 Instant 文件,用于审计和恢复;
  • metadata/:Hudi 1.0+ 引入的元数据表,将分区列表、文件列表、列统计等信息以 Hudi 表形式存储,极大加速元数据操作(避免listStatus()全量扫描);
  • 最核心的是未列出的*.commit、*.clean、*.compaction、*.deltacommit文件:它们就是时间线的实体,每个文件名形如20240901082345.commit,代表一次原子操作。

每一个.commit文件,都精确记录了本次写入所影响的所有文件组(file_id)及其新生成的文件切片(File Slice)路径。例如,一个20240901082345.deltacommit(MOR 表的增量提交)内容节选:

{ "instantTime": "20240901082345", "status": "COMPLETED", "operationType": "UPSERT", "affectedPartitions": ["dt=2024-09-01"], "fileIdAndRelativePaths": { "f8a7b2c1-4e56-4f78-9a1b-2c3d4e5f6a7b": [ "dt=2024-09-01/f8a7b2c1-4e56-4f78-9a1b-2c3d4e5f6a7b-0_20240901082345.log.1" ] } }

这意味着:Hudi 的文件布局不是静态树状结构,而是一个由时间线驱动的、动态演化的图谱。file_id是节点,.commit文件是边,记录着“谁在什么时候给谁增加了什么”。没有时间线,文件布局就是一堆孤儿文件;没有文件布局,时间线就是一纸空文。二者共生,缺一不可。

3. 核心细节解析与实操要点:从磁盘路径读懂 Hudi 的“身体语言”

3.1 解剖一个真实的文件组(File Group):file_id不是 UUID,是“数据身份证”

我们进入一个典型的 MOR 表分区目录:

$ hdfs dfs -ls /data/warehouse/sales_orders/dt=2024-09-01/ Found 5 items drwxr-xr-x - hive hive 0 2024-09-01 08:20 /data/warehouse/sales_orders/dt=2024-09-01/000000_0 drwxr-xr-x - hive hive 0 2024-09-01 08:21 /data/warehouse/sales_orders/dt=2024-09-01/000001_0 drwxr-xr-x - hive hive 0 2024-09-01 08:22 /data/warehouse/sales_orders/dt=2024-09-01/000002_0 drwxr-xr-x - hive hive 0 2024-09-01 08:23 /data/warehouse/sales_orders/dt=2024-09-01/000003_0 drwxr-xr-x - hive hive 0 2024-09-01 08:23 /data/warehouse/sales_orders/dt=2024-09-01/000004_0

等等——这看起来和 Hive 一模一样?别急,这只是 Hudi 兼容 Hive 的“表象”。真正的文件组藏在这些目录内部。执行:

$ hdfs dfs -ls /data/warehouse/sales_orders/dt=2024-09-01/000000_0/ Found 3 items -rw-r--r-- 1 hive hive 12345678 2024-09-01 08:20 /data/warehouse/sales_orders/dt=2024-09-01/000000_0/123e4567-e89b-12d3-a456-426614174000-0_20240901082012.parquet -rw-r--r-- 1 hive hive 2345678 2024-09-01 08:21 /data/warehouse/sales_orders/dt=2024-09-01/000000_0/123e4567-e89b-12d3-a456-426614174000-0_20240901082012.log.1 -rw-r--r-- 1 hive hive 1234567 2024-09-01 08:22 /data/warehouse/sales_orders/dt=2024-09-01/000000_0/123e4567-e89b-12d3-a456-426614174000-0_20240901082012.log.2

看懂这个路径命名规则,就等于拿到了 Hudi 的“解码器”:

  • 123e4567-e89b-12d3-a456-426614174000:这就是file_id,一个标准 UUID。但它不是随机生成的,而是由record_key(如订单IDORD-123456)和partition_path(dt=2024-09-01)经哈希计算得出。你可以用 Hudi CLI 验证:
    $ hudi-cli --table-path /data/warehouse/sales_orders --command "get-file-id --record-key ORD-123456 --partition-path dt=2024-09-01" # 输出:123e4567-e89b-12d3-a456-426614174000
  • -0_:表示这是该 file_id 的第一个文件切片(slice number),从 0 开始递增。当发生压缩(Compaction)生成新 Parquet 时,会变成-1_、-2_;
  • 20240901082012:这是该切片的instant_time(毫秒级时间戳,格式yyyyMMddHHmmss),精确到秒,代表生成此切片的那次 Commit 的时间。它和.hoodie/20240901082012.commit文件一一对应;
  • .parquet:基础文件,存储该切片的完整快照(Full Snapshot);
  • .log.1,.log.2:日志文件,存储自20240901082012之后对该 file_id 的所有增量变更(Upsert/Delete)。

实操心得:file_id的稳定性是 Hudi 性能的命脉。如果你发现同一主键的记录被分散到多个 file_id,一定是hoodie.datasource.write.recordkey.field配置错了,或者业务侧传入的 record key 格式不统一(比如有时带前缀ORD-,有时不带)。务必在建表时用hudi-cli抽样验证。

3.2 日志文件(.log)的内部结构:不是纯文本,是带 Schema 的二进制块

.log文件常被误解为“简单的追加日志”,这是巨大误区。它是一个高度结构化的二进制容器,内部由多种类型的 Block 组成,每个 Block 都有明确的 Header 和 Payload:

Block 类型作用是否可选典型大小
Data Block存储实际的更新/插入记录(Avro 格式)必选几 KB ~ 几 MB
Command Block存储元数据指令,如ROLLBACK、COMPACT触发标记可选< 1KB
Delete Block存储逻辑删除标记(record key + partition path),用于软删除可选< 1KB

一个.log.1文件的典型结构如下(文字描述):

[Header: Magic Number 'HUDILOG' + Version 1] [Block 1: Data Block Header (size=1234, schemaId=5)] [Block 1: Data Block Payload (Avro binary, 1234 bytes)] [Block 2: Command Block Header (size=45, type=ROLLBACK)] [Block 2: Command Block Payload (JSON string "rollback_to_instant:20240901081900")] [Block 3: Delete Block Header (size=23, type=DELETE)] [Block 3: Delete Block Payload (record key list, 23 bytes)]

关键点在于:Hudi 的日志合并(Log Merging)不是字符串拼接,而是 Block 级别的解析与重组。当 Compaction 执行时,它会:

  1. 读取20240901082012.parquet的所有 record key;
  2. 逐个解析20240901082012.log.1和20240901082012.log.2中的 Data Block,提取所有 record key;
  3. 对每个 key,应用最新的变更(最后出现的 Update 覆盖之前的,Delete 排除);
  4. 将最终结果写入新的123e4567-...-1_20240901082345.parquet。

注意:.log文件的schemaId必须与基础 Parquet 文件的 Schema 兼容。Hudi 通过hoodie.table.schema.on.read.enable=true(默认)在读取时自动进行 Schema 演化兼容(如新增 nullable 字段)。但如果新增了非 nullable 字段且无默认值,合并会失败。这是生产环境中.log文件读取失败的第二大原因(第一是磁盘损坏导致 Block Header 校验失败)。

3.3 基础文件(.parquet)的“隐藏属性”:不只是数据,更是索引锚点

Parquet 文件在 Hudi 中扮演双重角色:既是数据载体,也是高效查询的索引基座。它的特殊之处在于:

  • 内置 Bloom Filter:Hudi 在写入 Parquet 时,会自动为record_key字段构建 Bloom Filter,并作为 Parquet 的key_value_metadata写入。这使得在 MOR 表的快照查询中,引擎可以先用 Bloom Filter 快速判断“某条 record key 是否可能存在于该 Parquet 文件中”,如果返回false,则完全跳过该文件的读取,极大减少 I/O。
  • Column Statistics:Hudi 强制收集record_key和partition_path字段的 min/max 值,并写入 Parquet 的页脚(Footer)。这支持谓词下推(Predicate Pushdown),例如WHERE dt='2024-09-01' AND order_id='ORD-123456',引擎可直接跳过其他分区和其他 Parquet 文件。
  • Row Group 粒度对齐:Hudi 的写入器会尽量让每个 Row Group 的记录数接近hoodie.parquet.max.file.size(默认 128MB),并确保同一file_id的不同切片(如-0_和-1_)的 Row Group 边界对齐。这使得 Compaction 合并时,可以按 Row Group 粒度进行,避免全文件解压重写。

你可以用parquet-tools查看一个 Hudi Parquet 文件的元数据:

$ parquet-tools meta hdfs://namenode:8020/data/warehouse/sales_orders/dt=2024-09-01/000000_0/123e4567-...-0_20240901082012.parquet file: hdfs://namenode:8020/data/warehouse/sales_orders/dt=2024-09-01/000000_0/123e4567-...-0_20240901082012.parquet creator: parquet-mr version 1.12.3 (build 0b0b0b0b) ... key_value_metadata: { "hoodie.bloom.filter.type": "DYNAMIC_V0", "hoodie.bloom.filter.num.entries": "1000000", "hoodie.bloom.filter.fpp": "0.01", "hoodie.minRecordKey": "ORD-000001", "hoodie.maxRecordKey": "ORD-999999" }

看到hoodie.minRecordKey和hoodie.maxRecordKey了吗?这就是 Hudi 为快速范围查找埋下的伏笔。它比 Hive 的PARTITION级别过滤细一个数量级。

4. 实操过程与核心环节实现:手把手搭建一个可验证的文件布局沙盒

4.1 本地快速启动:用 Spark Shell 搭建最小 Hudi 环境(5分钟)

无需 Hadoop 集群,用本地文件系统即可验证所有文件布局行为。以下步骤在 macOS/Linux 上实测通过:

  1. 下载 Hudi 二进制包(推荐 1.4.0,稳定且文档全):

    wget https://downloads.apache.org/hudi/1.4.0/hudi-1.4.0-bin.tgz tar -xzf hudi-1.4.0-bin.tgz cd hudi-1.4.0
  2. 启动 Spark Shell 并加载 Hudi 包:

    # 使用本地模式,内存调大避免 OOM ./spark-shell \ --jars packaging/hudi-spark-bundle/target/hudi-spark-bundle_2.12-1.4.0.jar \ --conf 'spark.serializer=org.apache.spark.serializer.KryoSerializer' \ --master local[2] \ --driver-memory 4g \ --executor-memory 4g
  3. 在 Spark Shell 中创建一个 MOR 表:

    import org.apache.hudi.QuickstartUtils._ import scala.collection.JavaConversions._ import org.apache.spark.sql.SaveMode._ import org.apache.hudi.DataSourceWriteOptions._ import org.apache.hudi.config.HoodieWriteConfig._ // 1. 生成测试数据(模拟订单流) val inserts = Seq( ("ORD-001", "2024-09-01", "Shanghai", 100.0), ("ORD-002", "2024-09-01", "Beijing", 200.0), ("ORD-003", "2024-09-01", "Guangzhou", 150.0) ).toDF("order_id", "dt", "city", "amount") // 2. 第一次写入(初始化表) val basePath = "/tmp/hudi_mor_test" inserts.write.format("hudi") .option(PRECOMBINE_FIELD_OPT_KEY, "ts") // 用 ts 字段预合并 .option(RECORDKEY_FIELD_OPT_KEY, "order_id") .option(PARTITIONPATH_FIELD_OPT_KEY, "dt") .option(TABLE_NAME, "mor_orders") .option(TABLE_TYPE_OPT_KEY, "MERGE_ON_READ") // 关键:指定 MOR .mode(Overwrite) .save(basePath) // 3. 查看生成的文件布局 :sh ls -l /tmp/hudi_mor_test/dt=2024-09-01/ // 输出类似:123e4567-...-0_20240901082012.parquet

此时,你已经在/tmp/hudi_mor_test/下拥有了一个真实的 Hudi MOR 表。接下来,我们用一系列命令,亲手“触摸”它的文件布局。

4.2 深度探查:用 Hudi CLI 解析时间线与文件切片关系

Hudi 自带的 CLI 工具是理解文件布局的瑞士军刀。进入 Hudi 目录后:

# 启动 CLI,连接到你的表 ./hudi-cli.sh --connect --url "jdbc:hudi:/tmp/hudi_mor_test" # 1. 查看时间线(Timeline)上有哪些 Instant hudi:/> timeline show # 输出:20240901082012 [COMPLETED] [COMMIT] [UPSERT] # 2. 查看该 Instant 影响了哪些文件组 hudi:/> commit showfiles --commit 20240901082012 # 输出:fileId: 123e4567-...-0, partition: dt=2024-09-01, fileSlice: 123e4567-...-0_20240901082012.parquet # 3. 查看该文件组当前的所有切片(验证是否只有 1 个) hudi:/> filegroup list --base-path /tmp/hudi_mor_test --file-id 123e4567-...-0 # 输出:Slice: 123e4567-...-0_20240901082012.parquet (baseInstantTime: 20240901082012)

现在,我们模拟一次更新,观察文件布局如何变化:

// 在 Spark Shell 中执行第二次写入(更新 ORD-001 的金额) val updates = Seq( ("ORD-001", "2024-09-01", "Shanghai", 120.0), // 更新 ("ORD-004", "2024-09-01", "Shenzhen", 300.0) // 新增 ).toDF("order_id", "dt", "city", "amount") updates.write.format("hudi") .option(PRECOMBINE_FIELD_OPT_KEY, "ts") .option(RECORDKEY_FIELD_OPT_KEY, "order_id") .option(PARTITIONPATH_FIELD_OPT_KEY, "dt") .option(TABLE_NAME, "mor_orders") .option(TABLE_TYPE_OPT_KEY, "MERGE_ON_READ") .mode(Append) // 注意:这里是 Append,不是 Overwrite .save(basePath)

再次用 CLI 查看:

hudi:/> timeline show # 输出新增一行:20240901082533 [COMPLETED] [DELTA_COMMIT] [UPSERT] hudi:/> commit showfiles --commit 20240901082533 # 输出:fileId: 123e4567-...-0, partition: dt=2024-09-01, fileSlice: 123e4567-...-0_20240901082533.log.1 hudi:/> filegroup list --base-path /tmp/hudi_mor_test --file-id 123e4567-...-0 # 输出: # Slice: 123e4567-...-0_20240901082012.parquet (baseInstantTime: 20240901082012) # Slice: 123e4567-...-0_20240901082533.log.1 (baseInstantTime: 20240901082012)

看!file_id相同的文件组下,现在同时存在一个.parquet(来自第一次 Commit)和一个.log.1(来自第二次 Delta Commit)。这正是 MOR 表“写时只追加日志”的直观体现。.log.1的baseInstantTime是20240901082012,说明它是基于那个 Parquet 文件的增量。

4.3 手动触发压缩(Compaction):见证文件布局的“新陈代谢”

MOR 表的.log文件不会自动消失,必须显式触发 Compaction。这步操作会彻底改变文件布局:

# 在 CLI 中触发 Compaction hudi:/> compaction schedule # 输出:Scheduling compaction for instant 20240901083000 hudi:/> compaction run --compaction-instant 20240901083000 # 输出:Compaction completed successfully for instant 20240901083000

现在,再次查看文件组:

hudi:/> filegroup list --base-path /tmp/hudi_mor_test --file-id 123e4567-...-0 # 输出: # Slice: 123e4567-...-0_20240901082012.parquet (baseInstantTime: 20240901082012) # Slice: 123e4567-...-0_20240901082533.log.1 (baseInstantTime: 20240901082012) # Slice: 123e4567-...-0_20240901083000.parquet (baseInstantTime: 20240901083000)

新生成的20240901083000.parquet,就是 Compaction 的产物。它包含了20240901082012.parquet的全量数据,加上20240901082533.log.1的所有更新。此时,20240901082533.log.1依然存在,但已“过期”,因为它的变更已被合并进新 Parquet。Hudi 的清理(Clean)服务会在后续自动删除它。

实操心得:Compaction 的baseInstantTime(如20240901083000)不是随机的,它等于 Compaction 操作本身的 Instant Time。而新 Parquet 的baseInstantTime也等于此值,这保证了时间线的严格单调性。你可以用hudi-cli的timeline get-commits-between命令,精确找出哪些.log文件参与了这次 Compaction。

4.4 清理(Clean)策略详解:KEEP_LATEST_FILE_VERSIONS的数学计算

Hudi 的清理策略决定了旧文件切片的“寿命”。最常用的是KEEP_LATEST_FILE_VERSIONS,它保留每个file_id的最新 N 个切片。但“最新”是如何定义的?不是按文件名时间戳,而是按该切片所对应的 Instant 在时间线上的完成顺序。

假设一个file_id的历史切片如下(按时间线完成顺序排列):

Instant TimeFile SliceTypeState
20240901082012...-0_20240901082012.parquetBaseCOMPLETED
20240901082533...-0_20240901082533.log.1LogCOMPLETED
20240901083000...-0_20240901083000.parquetBaseCOMPLETED
20240901083510...-0_20240901083510.log.1LogCOMPLETED
20240901084020...-0_20240901084020.parquetBaseCOMPLETED

如果设置hoodie.cleaner.policy=KEEP_LATEST_FILE_VERSIONS且hoodie.cleaner.fileversions.retained=2,Hudi 会:

  1. 对每个file_id,收集其所有切片;
  2. 按切片的 `baseInstantTime

相关新闻

  • 2026年7月最新亨得利官方名表服务中心|网点地址与24小时热线权威信息通告 - 亨得利官方博客
  • TLA2518与STM32F413RH的ADC接口设计与优化实践
  • 原来新疆特产砍价还有这些门道?

最新新闻

  • ADP5350与STM32的嵌入式电源管理系统设计实践
  • SolidWorks 2026安装部署全链路指南:许可、驱动与系统兼容性深度解析
  • 中小企业做本地SEO全案优化需注意什么?
  • LangGraph不是画图工具,而是可执行状态图引擎
  • 低成本启动招聘业务?2026最新招聘小程序SaaS模板选型对比清单
  • 从CivitAI提示词复制到自动化工作流集成的实战指南

日新闻

  • OpenClaw本地化部署:xParse文档解析引擎实战指南
  • 蓝牙 5.4 协议栈深度解析:从 HCI 到 L2CAP 的 7 层数据流
  • PyTorch nn.CrossEntropyLoss 实战:3种权重设置与标签平滑对比(附代码)

周新闻

  • 基于YOLOv12的番茄成熟度智能检测系统开发
  • 终极RimWorld模组管理指南:用RimSort告别模组冲突烦恼
  • AI Agent框架开发:从理论到实践的完整指南

月新闻

  • 2026年6月公司网站搭建最新热门渠道测评:四大低成本/零代码平台对比+避坑
  • 【Linux】Linux arm 编译QT程序,出现expected “}“报错
  • 【MATLAB例程】四基站二维AOA定位与距离辅助增强对比仿真。基于角度观测和测距修正的固定目标平面定位精度分析

关于尧图

  • 公司简介
  • 团队介绍
  • 企业文化
  • 荣誉资质

服务项目

  • 定制开发
  • 电商建站
  • UI 设计
  • 运维服务

快速链接

  • 案例展示
  • 建站流程
  • 常见问题
  • 资讯中心

联系方式

  • 📍北京市朝阳区互联网产业园 A 座 10 层
  • 📞400-888-8888
  • ✉️contact@rkmt.cn
  • 🕐周一至周日 9:00-21:00

© 2024 北京尧图网络科技有限公司 版权所有 | 京 ICP 备 XXXXXXXX 号