弊端:数据重复 -> 优势:Paimon 主键表原生去重

原方案弊端 (Kafka)

  • 问题: 消息队列(Kafka)是仅支持追加(Append-Only)的日志流。当 Flink 作业发生故障恢复(Failover)或业务逻辑迭代重跑数据时,同样的数据会被再次写入消息队列,形成重复数据。
  • 影响: 下游应用(如DWS层、ADS层或直接对接的BI报表)必须自己实现复杂的去重逻辑,这不仅消耗大量计算资源(“资源消耗至少增加一倍”),而且可能导致作业不稳定。

Paimon 的优势

  • 解决方案: Paimon 提供了主键表(Primary Key Table)。这种表类型基于 LSM 树(Log-structured merge-tree)结构,原生支持 UPSERT (更新或插入) 和 DELETE 操作。
  • 工作原理: 当 Flink 作业将数据写入 Paimon 的主键表时:
    • 如果数据的主键已存在,Paimon 会更新该行记录。
    • 如果主键不存在,Paimon 会插入新行。
    • 因此,即使上游作业重发了数据,Paimon 也能通过主键自动去重,确保表里存储的始终是每个主键对应的最新状态。下游消费者不再需要关心去重问题。

正如文档中所描述的,主键表是 Paimon 的核心功能之一,用于支持大规模的实时更新。

# OverviewIf you define a table with primary key, you can insert, update or delete records in the table.Primary keys consist of a set of columns that contain unique values for each record. Paimon enforces data ordering by
sorting the primary key within each bucket, allowing users to achieve high performance by applying filtering conditions
on the primary key. See [CREATE TABLE]({{< ref "flink/sql-ddl#create-table" >}}).

在 Flink SQL 中创建这样的主键表非常简单:

// ... existing code ...
CREATE TABLE my_table (user_id BIGINT,item_id BIGINT,behavior STRING,dt STRING,hh STRING,PRIMARY KEY (dt, hh, user_id) NOT ENFORCED
);
// ... existing code ...

弊端:DWS 层缺失 -> 优势:Paimon 支持聚合与更新,构建 DWS 层

原方案弊端 (Kafka)

  • 问题: 由于 Kafka 不支持 UPSERT,无法对聚合结果进行原地更新。例如,一个按用户ID统计5分钟窗口消费额的 DWS 层,10:00-10:05 的聚合结果和 10:05-10:10 的聚合结果会作为两条独立的消息存在于 Kafka 中。
  • 影响: 无法构建一个持续更新状态的 DWS 层。下游要么消费这个中间结果流自己再做一次聚合,代价极高;要么跳过 DWS 层,直接将 DWD 数据写入在线存储(如 ClickHouse),把聚合压力和复杂性推给了下游系统。

Paimon 的优势

  • 解决方案: Paimon 的主键表能力完美解决了这个问题。我们可以轻松地在 Paimon 中构建 DWS 层。
  • 工作原理: Flink 聚合任务(例如,每5分钟统计一次用户消费总额)可以将聚合结果 UPSERT 到 Paimon 的 DWS 表中。这张表以用户ID为主键。每次新的聚合结果到来时,它会直接更新对应用户的消费总额,而不是插入一条新纪录。
  • Changelog 支持: 更重要的是,Paimon 可以为下游生成 changelog(变更日志)。下游系统(如 ClickHouse)可以直接消费这个 changelog 流,轻松地同步最新的聚合结果,而无需处理复杂的合并逻辑。

Paimon 的 merge-engine 机制甚至允许自定义更新逻辑,例如 partial-update(部分更新)或 aggregation(聚合),这为构建 DWS 层提供了极大的灵活性。

// ... existing code ...
- Realtime updates:- Primary key table supports writing of large-scale updates, has very high update performance, typically through Flink Streaming.- Support defining Merge Engines, update records however you like. Deduplicate to keep last row, or partial-update, or aggregate records, or first-row, you decide.
// ... existing code ...

弊端:No Schema -> 优势:Paimon 提供统一的、可查询的 Schema

原方案弊端 (Kafka)

  • 问题: 存储在Kafka 中的数据(通常是 JSON 字符串)是半结构化的。虽然 Flink 作业在流处理时会解析它,但数据本身在消息队列中没有一个强定义的、可供外部查询的 Schema。
  • 影响: 其他数据消费者(如算法、BI 团队)无法直接查询 DWD 层数据。他们需要自己写程序消费 Kafka,然后重复解析和计算,造成了巨大的资源浪费和协作成本。

Paimon 的优势

  • 解决方案: Paimon 是一个湖仓格式,它将数据以结构化的、带 Schema 的表形式存储。
  • 工作原理: 数据一旦写入 Paimon 表,就拥有了明确的表结构(列名、数据类型等)。这个 Schema 信息会和数据一起被管理。
  • 生态兼容: Paimon 提供了强大的生态兼容性,支持 Flink, Spark, Hive, Trino, Doris 等多种查询引擎。这意味着 BI、算法等团队可以直接使用他们熟悉的 SQL 工具,像查询普通数据库一样查询 Paimon 中的 DWD 和 DWS 表,极大地提升了数据利用效率。

Paimon 的兼容性矩阵展示了其强大的生态整合能力:

// ... existing code ...
| Engine | Version | Batch Read | Batch Write | Create Table | Alter Table | Streaming Write | Streaming Read | Batch Overwrite | DELETE & UPDATE | MERGE INTO | Time Travel |
| :-------------------------------------------------------------------------------: | :-------------: | :-----------: | :-----------: | :-------------: | :-------------: | :----------------: | :----------------: | :---------------: | :---------------: | :----------: | :-----------: |
| Flink | 1.15 - 1.20 | ✅ | ✅ | ✅ | ✅(1.17+) | ✅ | ✅ | ✅ | ✅(1.17+) | ❌ | ✅ |
| Spark | 3.2 - 3.5 | ✅ | ✅ | ✅ | ✅ | ✅(3.3+) | ✅(3.3+) | ✅ | ✅ | ✅ | ✅(3.3+) |
| Hive | 2.1 - 3.1 | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ |
| Trino | 420 - 440 | ✅ | ✅(427+) | ✅(427+) | ✅(427+) | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ |
// ... existing code ...

弊端:资源与效率 -> 优势:Paimon 统一流批,简化架构

原方案弊端 (Lambda 架构)

  • 问题: 为了保证数据的最终准确性(例如,通过离线反作弊修正数据),需要维护实时(Flink on Kafka)和离线(ETL on MaxCompute)两套独立的数据链路和代码。
  • 影响: 开发和运维成本翻倍,架构复杂,数据同步和一致性保障困难。

Paimon 的优势

  • 解决方案: Paimon 作为统一存储层,天然支持流式读写和批式读写,是构建流批一体(Unified Batch and Streaming)架构的理想选择。
  • 工作原理:
    • 流式写入: Flink 实时作业持续将数据写入 Paimon。
    • 批式读/写: 离线作业(如 Spark 或 Flink Batch)可以读取 Paimon 表的最新快照进行批量处理(如反作弊计算),并将修正后的数据写回到同一张 Paimon 表中。
    • 统一视图: 无论是实时查询还是离线查询,都访问的是同一份存储,同一张表。这彻底消除了两套系统、两份数据的问题。

在ODS层完成数据解析后,可以将数据反向写入到Paimon中,这正是 Paimon 流批一体能力的完美体现。

Paimon 的架构设计就是为了解决这个问题,提供一个统一的存储底座。

// ... existing code ...
Paimon provides table abstraction. It is used in a way that
does not differ from the traditional database:
- In `batch` execution mode, it acts like a Hive table andsupports various operations of Batch SQL. Query it to see thelatest snapshot.
- In `streaming` execution mode, it acts like a message queue.Query it acts like querying a stream changelog from a message queuewhere historical data never expires.
// ... existing code ...

总结

痛点Kafka 方案Paimon 解决方案
数据重复Append-only,导致Failover后数据重复主键表,通过 UPSERT 自动去重
DWS层构建无 UPSERT,无法原地更新聚合结果主键表,支持聚合结果的持续更新,可构建DWS层
数据共享No Schema,下游需重复解析统一Schema,多引擎可直接用SQL查询,提升协作效率
架构冗余Lambda架构,流批两套代码和存储流批一体,统一存储,简化架构,降低开发运维成本

常见优化策略

优化策略主要围绕性能存储稳定性三个方面展开,这是构建和维护高性能湖仓系统的核心。


一、 性能优化

性能优化的核心目标是提升数据写入和处理的吞杜量,同时降低延迟

1. 启用异步 Compaction
  • 分析:

    • Paimon 基于 LSM 树结构,写入时会产生多个有序的小文件(Sorted Runs)。查询时需要合并这些文件,文件过多会严重影响查询性能。
    • Compaction 就是将这些小文件合并成大文件的过程。默认情况下,Compaction 可能和数据写入操作在同一个 Flink Task 中同步或半同步执行,这会占用写入链路的资源,尤其是在写入高峰期,可能导致反压和延迟增加。
    • 异步 Compaction (Asynchronous Compaction) 将合并操作与写入操作解耦。写入操作可以快速完成,将合并任务交给独立的机制或在系统负载较低时执行。这极大地提升了写入的吞吐量和稳定性,正如您提到的“节点切换的平均耗时超过 50 秒,而开启后则缩短至 20 秒”。
  • 实现与扩展:

    • 完全异步化: 通过调整特定参数,可以让 Compaction 完全不阻塞写入,最大化写入吞吐量。这适用于对写入性能要求极高,但对数据查询的即时性要求稍低的场景。
      // ... existing code ...
      num-sorted-run.stop-trigger = 2147483647
      sort-spill-threshold = 10
      lookup-wait = false
      // ... existing code ...
      
    • 专用 Compaction 作业: 这是更彻底的解耦方式。可以将主写入作业的 Compaction 完全关闭 ('write-only' = 'true'),然后启动一个独立的、专用的 Flink 作业来负责 Compaction。这样可以为写入和 Compaction 分配独立的资源,互不干扰,实现更精细的资源管理和调优。
      // ... existing code ...public static final ConfigOption<Boolean> WRITE_ONLY =key("write-only").booleanType().defaultValue(false).withFallbackKeys("write.compaction-skip").withDescription("If set to true, compactions and snapshot expiration will be skipped. "+ "This option is used along with dedicated compact jobs.");
      // ... existing code ...
      
      然后通过 Flink Action 启动专用作业:
      // ... existing code ...
      <FLINK_HOME>/bin/flink run \/path/to/paimon-flink-action-{{< version >}}.jar \compact \
      // ... existing code ...
      

2. 调整 Checkpoint Interval
  • 分析:

    • 在 Flink 流式写入 Paimon 的场景中,Checkpoint 是触发数据从内存刷写到文件系统并生成 Paimon Snapshot 的关键
    • 过于频繁的 Checkpoint (短间隔) 会导致生成大量的小文件,增加文件系统压力和后续 Compaction 的负担。同时,频繁的 Barrier 对齐和状态快照也会消耗大量网络和 CPU 资源,在高吞吐场景下极易引起反压。
    • 增大 Checkpoint Interval 可以让数据在内存的 write-buffer 中累积更多,单次刷写生成的文件更大,从而减少小文件数量和 Checkpoint 开销。
  • 实现与扩展:

    • 除了增大 execution.checkpointing.interval,还可以调整 execution.checkpointing.max-concurrent-checkpoints 来允许更多的 Checkpoint 并行,提高容错效率。
    • Buffer 优化: 配合增大 Checkpoint 间隔,应适当增大 Paimon 的写缓冲 write-buffer-size,并可以开启 write-buffer-spillable,当内存 Buffer 写满时,会先溢写到本地磁盘,而不是直接刷到远程存储,这样可以平滑 Checkpoint 峰值压力,生成更大的最终文件。
    // ... existing code ...
    1. Flink Configuration (`'flink-conf.yaml'/'config.yaml'` or `SET` in SQL): Increase the checkpoint interval(`'execution.checkpointing.interval'`), increase max concurrent checkpoints to 3(`'execution.checkpointing.max-concurrent-checkpoints'`), or just use batch mode.
    2. Increase `write-buffer-size`.
    3. Enable `write-buffer-spillable`.
    // ... existing code ...
    
3. 调整 Writer 节点资源与并行度
  • 分析:

    • Writer 节点的并行度直接决定了数据写入的能力。这个并行度通常应该与 Paimon 表的 bucket 数量相匹配或成倍数关系,以确保数据均匀分布到各个 bucket,避免数据倾斜。
    • 在数据回刷或追赶历史数据时,上游数据源的读取速度会很快,此时必须相应地调大 Writer 的并行度来匹配处理能力。
    • Writer 节点的内存 (write-buffer-size) 也至关重要,它直接影响单次刷盘的文件大小和 Compaction 的效率。
  • 实现与扩展:

    • 动态调整: 虽然分区和 Bucket 数不建议频繁调整,但 Flink 作业的并行度是可以在停机重启时调整的。在数据回刷场景,可以临时调高并行度,完成后再恢复正常值。
    • 使用 Flink 托管内存: 为了避免手动管理内存导致 OOM,可以启用 'sink.use-managed-memory-allocator' = 'true'。这样 Paimon Writer 会使用 Flink 的托管内存,由 Flink TaskManager 统一管理和分配,可以提高资源利用率和稳定性。
    // ... existing code ...
    INSERT INTO paimon_table /*+ OPTIONS('sink.use-managed-memory-allocator'='true', 'sink.managed.writer-buffer-memory'='256M') */
    SELECT * FROM ....;
    
4. 合理设置分区与 Bucket
  • 分析:

    • 分区 (Partition) 是数据物理隔离的第一层,通常基于低基数的列(如日期 dt),用于数据管理和查询过滤。好的分区设计可以极大地提升查询性能,因为查询引擎可以直接跳过不相关的分区目录。
    • 桶 (Bucket) 是在分区内对数据进行哈希分桶,是数据读写并行的基本单位。bucket 的数量决定了写入的最大并行度和数据在分区内的分布。
    • Rescale 的代价: 修改分区键或 bucket 数需要 ALTER TABLE 后通过 INSERT OVERWRITE 重写数据,这是一个成本很高的操作。因此,在表设计之初就必须对未来的数据量和并发量有充分预估。
  • 实现与扩展:

    • 分区策略: 选择更新频率低、且常作为查询条件的列做分区键。例如,按天分区 dt 是最常见的策略。
    • Bucket 数估算: Bucket 数应根据分区内的数据量峰值来设定。一个经验法则是,保证每个 Bucket 内单个文件的大小在合理范围(例如 128MB ~ 1GB)。您的例子中“每个分区的bucket num为512”就是一个很好的实践,它为每小时高达 700GB 的数据量提供了足够的写入并行度和数据分布。

二、 存储优化

存储优化的核心是控制文件数量,回收无效数据,降低存储成本

1. 文件生命周期管理 (TTL)
  • 分析:

    • Paimon 的每一次提交都会生成一个新的快照 (Snapshot)。为了支持时间旅行 (Time Travel),旧的快照和对应的数据文件会保留一段时间。
    • snapshot.time-retained 和 snapshot.num-retained.min 控制了快照的保留策略。过长的保留时间会导致大量元数据和数据文件堆积。
    • changelog-producer 产生的 Changelog 文件也有独立的生命周期管理。
  • 实现与扩展:

    • 需要根据业务对数据回溯的需求来设定合理的 TTL。例如,如果业务只需要回溯 3 天的数据,那么 snapshot.time-retained 就不应设置得过长。
    • 定期检查和调整 TTL 策略,以平衡数据可恢复性和存储成本。
2. 小文件合并
  • 分析:

    • 流式写入不可避免地会产生小文件。除了前面提到的异步 Compaction 和专用 Compaction 作业,Paimon 还提供了其他机制。
    • precommit-compact: 在文件提交到快照之前进行一次合并,可以有效减少最终生成的 changelog 文件数量。
    • full-compaction: 全量合并,可以将一个分区/桶内的所有文件合并成一个或少数几个文件,对查询性能提升最大。可以通过 'full-compaction.delta-commits' 定期触发。
  • 实现与扩展:

    • Sort Compact: 在执行 Compaction 时,可以指定按某些列 (Z-Order 或普通排序) 对数据进行排序。这可以极大地优化基于这些列的范围查询或点查性能,因为数据在物理上是连续存储的,可以最大化数据跳过的效果。
      // ... existing code ...
      CALL sys.compact(`table` => 'database_name.table_name', partitions => 'partition_name', order_strategy => 'z-order',order_by => 'col1,col2'
      );
      // ... existing code ...
      
    • 外部治理服务: 对于大型湖仓平台,可以引入如 Apache Amoro 这样的外部治理服务,它能提供更智能、自动化的表维护(Self-Optimizing),包括小文件合并、数据过期等。
3. 清理废弃/孤立文件
  • 分析:
    • 作业异常终止或旧版本 Paimon 的 Bug 可能会导致产生一些不被任何快照引用的孤立文件。这些文件占用了存储空间且不会被自动清理。
  • 实现与扩展:
    • Paimon 提供了 expire_snapshots 和 drop_partition 等 Action,可以用来清理快照和分区。
    • 社区也提供了相应的工具或讨论来识别和清理孤立文件。编写定时脚本,定期执行 Paimon 提供的清理命令,是一种有效的运维实践。

三、 稳定性优化

稳定性优化的核心是保障作业在各种异常情况下(尤其是高负载时)的健壮性和可恢复性

1. 启用 Consumer
  • 分析:

    • 当 Flink 作业从 Paimon 消费数据时,它会从某个快照开始读取。如果这个快照因为 TTL 过期而被删除了,作业就无法从该点恢复。
    • Consumer 机制允许为某个消费作业(由 consumer-id 标识)“锁定”一个快照。这个被锁定的快照及其之后的所有快照都不会被 TTL 机制自动删除,直到 Consumer 前进到新的快照。
    • 这极大地增强了下游消费作业的可恢复性,但代价是可能会保留更多的快照和数据文件,增加了存储成本。
  • 实现与扩展:

    • 这是一个典型的恢复能力与存储成本之间的权衡。对于关键的下游应用,启用 Consumer 是必要的。对于非核心应用,可以不启用,或定期手动重置消费位点。
2. 调整 TM (Task Manager) 和 Committer 资源
  • 分析:

    • TM 资源: Paimon Writer 的内存需求与数据记录大小、更新频率、Bucket 数量等密切相关。内存不足是导致 OOM 和作业不稳定的主要原因。
    • Committer 节点: 这是 Flink 写入 Paimon 的最后一步,负责将所有 Task 生成的 manifest 文件合并,并生成最终的 snapshot 文件。当一次 Checkpoint 写入的分区和文件非常多时,Committer 会成为瓶颈,需要大量的内存来持有这些元数据信息,也需要足够的 CPU 来完成合并。
  • 实现与扩展:

    • 精细化资源管理: Flink 1.18 之后默认开启了细粒度资源管理。可以利用这个特性为 Paimon 的 Committer 算子单独配置更高的内存和 CPU,而无需增加整个 TaskManager 的资源,从而实现更高效的资源利用。
      // ... existing code ...
      You can use fine-grained-resource-management of Flink to increase committer heap memory only:
      1. Configure Flink Configuration `cluster.fine-grained-resource-management.enabled: true`. (This is default after Flink 1.18)
      2. Configure Paimon Table Options: `sink.committer-memory`, for example 300 MB, depends on your `TaskManager`.(`sink.committer-cpu` is also supported)
      // ... existing code ...
      
    • 经验公式与监控: 结合 Paimon 社区提供的经验公式和实际的监控数据(如内存使用率、GC 时间、反压情况),持续迭代和优化资源配置。

总结

结合 Paimon 的文档和特性,我们可以看到这些策略背后都有其深刻的技术原理支撑。核心思想可以归纳为:

  1. 解耦与异步: 将耗时的 Compaction 操作与主写入链路解耦,是提升写入性能和稳定性的关键。
  2. 批处理思想: 在流处理中引入批处理的思想,通过增大 Checkpoint 间隔和 Buffer,将多次小操作合并为一次大操作,以摊销固定开销。
  3. 预估与规划: 在表设计阶段充分预估未来数据量,合理规划分区和 Bucket,避免后期高昂的调整成本。
  4. 权衡与取舍: 在性能、成本、稳定性、数据时效性之间做出权衡。例如,Consumer 提升了恢复能力但增加了存储成本;高压缩率降低了存储但增加了 CPU 开销。
  5. 精细化运维: 利用专用作业、细粒度资源管理等高级特性,对不同组件进行针对性优化,实现对整个系统的精细化控制。

这些策略共同构成了一套行之有效的 Paimon 湖仓优化方法论。

补充Paimon快照存储占用

快照(Snapshot)的本质 是一个元数据文件。

Paimon 的数据组织是一个清晰的层级结构,正如文档中图示的那样: Snapshot -> Manifest List -> Manifest -> Data File

  • Snapshot 文件: 是表的某个时间点版本的入口。它本身很小,是一个 JSON 文件,记录了这个版本包含哪些 Manifest List 文件,以及其他元数据。您正在查看的 Snapshot.java 文件就定义了它的结构。
  • Manifest List / Manifest 文件: 也是元数据文件,它们像目录一样,逐层记录了哪些数据文件(Data File)属于这个快照版本,以及这些数据文件的状态(是新增的还是被删除的)。
  • Data File: 这才是真正存储着表数据的物理文件(例如 Parquet 文件)。

所以,一个快照通过层层指向,最终“引用”了一批数据文件。

逻辑删除 vs 物理删除

当对表进行更新、删除或执行 Compaction(合并)操作时,Paimon 并不会立即去物理删除旧的数据文件。它会执行一个逻辑删除

  1. 生成新的数据文件。
  2. 创建一个新的 Snapshot
  3. 在这个新 Snapshot 的 Manifest 文件中,将旧的数据文件标记为 DELETE,将新的数据文件标记为 ADD

此时,旧的 Snapshot 依然存在,并且它仍然指向那些被“逻辑删除”的旧数据文件。这就是 Paimon 实现时间旅行(Time Travel) 的基础——只要旧快照还在,就可以随时回到过去的数据版本。

文档 docs/content/learn-paimon/understand-files.md 中对此有清晰的描述:

Paimon maintains multiple versions of files, compaction and deletion of files are logical and do not actually delete files. Files are only really deleted when Snapshot is expired.

简单来说:Compaction 等操作只做标记,不做真删除。真正的删除由快照过期来触发。

禁止删除有快照指向的文件

只要有一个活跃的、未过期的快照还在引用某个数据文件,这个数据文件就是安全的,绝对不会被删除。物理删除操作只会发生在那些“无主”的文件上——即所有引用它的快照都已经过期并被清除了。

这个机制确保了数据安全性和时间旅行能力,同时通过 TTL 自动回收不再需要的历史数据,从而控制存储成本。

这个过程的实现主要在 SnapshotDeletion.java 这个类中,它负责具体的清理逻辑。

// ... existing code ...
public class SnapshotDeletion extends FileDeletionBase<Snapshot> {// ... existing code ...@Overridepublic void cleanUnusedDataFiles(Snapshot snapshot, Predicate<ExpireFileEntry> skipper) {if (changelogDecoupled && !produceChangelog) {// Skip clean the 'APPEND' data files.If we do not have the file source information// eg: the old version table file, we just skip clean this here, let it done by// ExpireChangelogImplPredicate<ExpireFileEntry> enriched =manifestEntry ->skipper.test(manifestEntry)|| (manifestEntry.fileSource().orElse(FileSource.APPEND)== FileSource.APPEND);cleanUnusedDataFiles(snapshot.deltaManifestList(), enriched);} else {cleanUnusedDataFiles(snapshot.deltaManifestList(), skipper);}cleanUnusedDataFiles(snapshot.baseManifestList(), skipper);}
// ... existing code ...

这个类中的方法会遍历过期快照的 deltaManifestList 和 baseManifestList,收集文件列表,然后执行清理。

快照 TTL 如何触发物理删除

快照 TTL(Time-To-Live,生命周期)是如何处理多版本数据删除的?

  1. 定义过期策略: 可以配置快照的保留策略,比如 snapshot.time-retained (保留时长) 和 snapshot.num-retained.min (最小保留数量)。
  2. 识别过期快照: 当一个快照的存活时间超过了您设定的 TTL,它就会被 Paimon 的过期机制(Expire)识别为“已过期”。
  3. 清理过程:
    • 过期程序(ExpireSnapshots)会启动,它首先会删除这些过期的 snapshot JSON 文件本身。
    • 接着,它会读取这些过期快照所引用的 Manifest 文件,列出所有被这些过期快照“逻辑删除”的数据文件。
    • 最关键的一步:程序会检查这个列表中的每一个数据文件,确认它是否还被任何一个“未过期”的(即活跃的)快照所引用
    • 只有当一个数据文件不再被任何活跃快照引用时,它才会被物理删除

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

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

相关文章

Linux Shell 命令 + 项目场景

shell 命令1. 基础文件操作命令1.1 ls - 列出目录内容1.2 find - 文件搜索2. 版本控制命令2.1 git - 版本控制系统2.2 高级 Git 操作3. 文本搜索命令3.1 grep - 文本搜索3.2 高级搜索技巧4. Android 构建系统命令4.1 source - 加载环境变量4.2 lunch - 选择构建目标4.3 m - And…

A316-Mini-V1:超小尺寸USB高清音频解码器模组技术探析

引言 随着便携式音频设备的普及&#xff0c;对小型化、高性能音频解决方案的需求日益增长。本文将介绍一款极致小型化的高性能USB音频解码器模组——A316-Mini-V1&#xff0c;这是一款基于XMOS XU316芯片的微型音频处理模组。产品概述 A316-Mini-V1是一款专为小尺寸产品设计的M…

低代码平台买saas好还是私有化好

选择低代码平台采用SaaS还是私有化部署&#xff0c;应根据企业具体情况考虑安全性、成本控制、维护难度、扩展需求等因素。 其中&#xff0c;安全性是决定企业选择的重要因素之一。私有化部署意味着企业能够完全掌控数据和系统的安全管理&#xff0c;更适合对数据安全要求极高的…

基于SkyWalking的微服务APM监控实战指南

基于SkyWalking的微服务APM监控实战指南 1. 业务场景描述 随着微服务在生产环境中大规模应用&#xff0c;系统链路复杂、实例弹性伸缩、灰度发布等特点都给性能监控和问题诊断带来了新的挑战。传统的单机或轻量级监控方案已无法满足微服务环境下的全链路、分布式追踪和实时告警…

Python 进阶(五): Excel 基本操作

目录 1. 概述2. 写入 2.1 使用 xlwt2.2 使用 XlsxWriter 3. 读取4. 修改 1. 概述 在现实中&#xff0c;很多工作都需要与数据打交道&#xff0c;Excel 作为常用的数据处理工具&#xff0c;一直备受人们的青睐&#xff0c;而大部分人都是手动操作 Excel&#xff0c;如果数据量…

32、鸿蒙Harmony Next开发:使用动画-动画概述

​​​属性动画转场动画粒子动画组件动画动画曲线动画衔接动画效果帧动画&#xff08;ohos.animator&#xff09; UI&#xff08;用户界面&#xff09;中包含开发者与设备进行交互时所看到的各种组件&#xff08;如时间、壁纸等&#xff09;。属性作为接口&#xff0c;用于控制…

【STM32】485接口原理

485 通信实验 这篇文章是对 RS485通信 的原理、硬件连接、接口芯片&#xff08;SP3485&#xff09;、总线结构等都有详尽的说明。我们在此处进行清晰有条理的讲解整理&#xff0c;便于学习和实验操作。 在了解485接口通信原理之前&#xff0c;我们先复习一下串口&#xff1a;串…

亚马逊二审攻防全攻略:预防、应对与长效合规之道

当店铺收到二审通知&#xff0c;不少卖家会陷入焦虑与慌乱&#xff0c;只要掌握科学的预防策略与应对方法&#xff0c;不仅能降低二审风险&#xff0c;即便遭遇审核也能顺利突围。一、未雨绸缪&#xff1a;预防二审的四大核心策略夯实资料真实性根基资料的真实性与一致性是亚马…

添加状态信息

1首先在数据字典里加入可借阅和不可借阅状态2导入数据字典export default {name: "Book",dicts: [book_borrow_status],//导入数据字典data() {return {formData: {name: null,author: null,num: null,price: null,typeId: null,status:null//新加状态属性},3设置状态…

234、回文链表

题目&#xff1a;解答&#xff1a;对143稍作修改即可&#xff0c;判断两个指针指向的是否一直相等。终止条件为不等或者head2nullptrclass Solution { public:ListNode *rev(ListNode *head){ListNode *cur head;ListNode *pre nullptr;while(cur){ListNode * nxt cur->n…

第15次:商品搜索

实现用户在页面可自由搜索某个商品的功能。 第1步&#xff1a;准备搜索功能用到的库 pip install whoosh pip install jieba pip install django-haystackwhoosh是搜索引擎&#xff0c;对英文支持较好&#xff0c;但对中文效果不佳。jieba为中文分词库&#xff0c;弥补whoosh…

《使用Qt Quick从零构建AI螺丝瑕疵检测系统》——0. 博客系列大纲

目录【《使用Qt Quick从零构建AI螺丝瑕疵检测系统》系列简介】第一部分&#xff1a;基础入门与项目启航第二部分&#xff1a;核心视觉算法开发第三部分&#xff1a;模拟完整工业流程第四部分&#xff1a;软件打包与高级特性【《使用Qt Quick从零构建AI螺丝瑕疵检测系统》系列简…

【Python】Python中的循环语句

循环语句导读一、基本概念1.1 循环语句的执行流程1.2 循环语句的分类二、while语句三、for语句四、break与continue五、死循环六、循环中的else语句七、range()函数结语导读 大家好&#xff0c;很高兴又和大家见面啦&#xff01;&#xff01;&#xff01; 在上一篇内容中我们…

docker|Linux|以centos基础镜像为基础制作nmap专用镜像(镜像瘦身计划)

一、 最近由于某些场景下需要使用nmap&#xff0c;而nmap的rpm安装包在源目标机器上使用有软件冲突&#xff0c;因此&#xff0c;计划使用docker部署nmap 具体计划为 1、使用centos的基础镜像&#xff0c;在有网环境下&#xff0c;通过配置阿里云的yum仓库&#xff0c;在cen…

基于单片机公交车报站系统/报站器

传送门 &#x1f449;&#x1f449;&#x1f449;&#x1f449;其他作品题目速选一览表 &#x1f449;&#x1f449;&#x1f449;&#x1f449;其他作品题目功能速览​​​​​​​ 概述 公交车自动报站系统利用单片机作为核心控制器&#xff0c;结合GPS/北斗定位模块、语音存…

Oracle 体系结构学习

1 认识Oracle后台进程Oracle数据库后台进程是Oracle数据库管理系统&#xff08;DBMS&#xff09;的核心组件&#xff0c;它们在后台运行&#xff0c;负责数据库的各种管理和维护任务。主要包括以下几种&#xff1a;SMON (System Monitor)SMON负责数据库的恢复操作&#xff0c;如…

构建一种安全的老式测试仪,用于具有限流灯泡,模拟仪表和可变输出的交流设备

这个复古电路和电源测试仪的想法来自我需要一个简单&#xff0c;安全&#xff0c;时尚的工具来测试和控制工作台上的线路供电设备。商业解决方案要么太笨重&#xff0c;太昂贵&#xff0c;要么缺乏我喜欢的触觉和模拟魅力。所以我决定自己造一个。这个测试仪的核心是一个老式的…

Redis5:Redis的Java客户端——Jedis与SpringDataRedis详解

目录 1、Jedis客户端 1.1使用过程 2、SpringDataRedis 2.1 SpingDataRedis介绍 2.2SpringDataRedis快速入门 2.3RedisTemplate的RedisSerializer 2.3.1RedisTemplate中JDK序列化局限性 2.3.2方式一&#xff1a;改变RedisTemplate的序列化方式 2.3.3RedisTemplate存储一…

零基础 “入坑” Java--- 十三、再谈类和接口

文章目录一、Object类1.获取对象信息2.对象比较&#xff1a;equals方法二、再谈接口1.比较相关接口2.Cloneable接口和深拷贝三、内部类1.匿名内部类2.实例内部类3.静态内部类4.局部内部类在之前的学习中&#xff0c;我们已经了解了有关类以及接口的知识&#xff0c;在本章节中&…

Spring Boot 一个注解搞定「加密 + 解密 + 签名 + 验签」

Spring Boot 一个注解搞定「加密 解密 签名 验签」本文基于 Spring Boot 3.x&#xff0c;通过一个自定义注解 AOP&#xff0c;一行注解即可给任何 Controller 方法加上 请求解密 → 验签 → 响应加密 → 加签 的完整链路&#xff0c;并可直接拷贝到生产环境使用。一、最终效…