当前位置: 首页 > news >正文

InnoDB的redo log和 undo log

核心目的对比:

  • Redo Log (重做日志): 保证事务的 持久性 (Durability)。确保即使发生系统崩溃,已提交事务所做的修改也不会丢失。它记录的是物理层面的操作(物理逻辑日志),描述的是“在某个数据页上做了什么修改”。
  • Undo Log (回滚日志): 保证事务的 原子性 (Atomicity)隔离性 (Isolation) 的一部分(通过 MVCC 实现一致性读)。
    • 原子性: 用于在事务失败或用户执行 ROLLBACK 时,撤销未提交事务所做的修改。
    • MVCC: 用于为其他并发事务构建行记录的历史版本,实现非锁定读(快照读)。

🧱 一、Redo Log (重做日志) 实现详解

  1. 物理逻辑日志 (Physiological Logging):

    • Redo log 不是简单的 SQL 语句记录,也不是纯物理的字节变化记录。
    • 它记录的是在特定数据页(通过表空间ID+页号标识) 上发生的逻辑操作(如 MLOG_1BYTE, MLOG_2BYTES, MLOG_4BYTES, MLOG_8BYTES, MLOG_WRITE_STRING, MLOG_COMP_REC_INSERT, MLOG_COMP_REC_DELETE, MLOG_COMP_REC_UPDATE_IN_PLACE 等)。这些操作描述了页内某个偏移量处写入的具体内容。
    • 优点: 比纯物理日志更紧凑(只记录变化部分),比纯逻辑日志更易于高效恢复(操作直接作用于页)。
  2. 日志缓冲区 (Log Buffer):

    • 为了避免每次修改都直接写磁盘造成的巨大 I/O 开销,InnoDB 设置了内存中的 Log Buffer
    • 当事务修改数据页时,首先在 Buffer Pool 中修改页(产生脏页),同时生成对应的 redo log record 并写入 Log Buffer
    • 关键点: 先写日志 (Write-Ahead Logging, WAL)。在脏页刷回磁盘之前,必须保证其对应的 redo log 记录至少已经写入 Log Buffer。这是崩溃恢复能重做已提交事务的关键保障。
  3. 日志文件组 (Redo Log Files):

    • Redo log 物理上存储在磁盘的一组文件中(通常是 ib_logfile0, ib_logfile1)。这些文件是循环使用的。
    • 大小固定: 由参数 innodb_log_file_sizeinnodb_log_files_in_group 决定总大小(例如 innodb_log_file_size=1G, innodb_log_files_in_group=2 表示总共 2GB)。
    • 循环写入:
      • 文件被逻辑上划分为若干块(如 512 字节)。
      • 日志写入位置由 Log Sequence Number (LSN) 标识。LSN 是一个全局单调递增的整数,代表自日志系统初始化以来写入的日志总量(字节)。
      • 当写满最后一个文件时,会回头覆盖第一个文件的起始位置(前提是该位置之前的脏页已经被刷新到磁盘)。这个可覆盖的位置由 checkpoint 决定。
  4. 日志刷盘 (Flushing to Disk):

    • Log Buffer 中的内容需要定期或按策略刷写到磁盘的 redo log files 中,以确保内存中的日志记录在崩溃时不会丢失。
    • 触发时机 (由 innodb_flush_log_at_trx_commit 控制):
      • 1 (默认且安全): 每次事务提交时,将 Log Buffer 中的所有内容 write() 到操作系统的文件系统缓存 并立即 调用 fsync() 强制刷到磁盘。最大程度保证持久性
      • 0 (不安全): 每秒将 Log Buffer 内容 write() 到文件系统缓存并 fsync() 一次。事务提交不触发写盘。崩溃可能丢失约 1 秒的数据。
      • 2 (折中): 每次事务提交时,将 Log Buffer 内容 write() 到文件系统缓存,但立即 fsync()。每秒 fsync() 一次。如果操作系统不崩溃,事务提交不会丢;操作系统崩溃可能丢失约 1 秒的数据。
    • 后台线程: 即使 innodb_flush_log_at_trx_commit=02,也有一个后台线程 (log_writer / log_flusher) 负责每秒将 Log Buffer 刷盘,防止积累过多。
  5. 检查点 (Checkpoint):

    • 目的: 标记一个位置 (checkpoint LSN),表明在该 LSN 之前产生的 redo log 所对应的所有脏页已经被刷新到磁盘数据文件中。
    • 意义:
      • 崩溃恢复时,只需要从 checkpoint LSN 开始应用 redo log 即可,之前的修改已经落盘。
      • 确定了 redo log 文件中可以被安全覆盖的起始位置(checkpoint LSN 之前的空间可复用)。
    • 触发时机:
      • 日志文件空间即将用完(需要推进 checkpoint 来释放旧空间)。
      • 后台线程定期刷新脏页。
      • 脏页比例过高 (innodb_max_dirty_pages_pct)。
      • 系统相对空闲时。
    • 类型:
      • Fuzzy Checkpoint (模糊检查点): InnoDB 主要使用模糊检查点。它只记录一个 LSN 点,并不保证此刻所有小于该 LSN 的脏页都刷完了,而是通过后台线程持续刷新,并不断更新这个 LSN 点。更高效,对系统影响小。
      • Sharp Checkpoint (尖锐检查点):在关闭数据库等特殊时刻使用,会强制将所有脏页刷盘。
  6. 日志序列号 (Log Sequence Number - LSN):

    • LSN 是贯穿整个 redo log 系统的核心概念。
    • 全局递增: 每个写入 Log Buffer 的 redo log record 都会分配一个唯一的、更大的 LSN。
    • 用途广泛:
      • 标识 redo log record 在日志序列中的位置。
      • 记录每个数据页最后一次被修改时对应的 LSN (Page LSN)。
      • 记录 checkpoint LSN
      • 记录当前写入日志文件的 LSN (flushed_to_disk_lsn)。
      • 记录当前 Log Buffer 中最后一条日志的 LSN (log_buffer_lsn)。
    • 崩溃恢复原理:
      • 启动时,找到最近一次成功的 checkpoint LSN (存储在日志文件头或特定的 checkpoint 页)。
      • checkpoint LSN 开始扫描 redo log 文件。
      • 对于每条 redo log record:
        • 读取它要修改的页号。
        • 将该页从磁盘(或 Buffer Pool)读入内存(如果需要)。
        • 比较 redo record 的 LSN (Redo LSN) 和该页的 Page LSN
          • 如果 Redo LSN > Page LSN:说明该修改尚未应用到页上,应用这条 redo log。
          • 如果 Redo LSN <= Page LSN:说明该修改可能已经应用过(或者页是更新的),跳过。
      • 一直处理到 redo log 的末尾。
      • 这样,所有已提交事务在崩溃前做的修改就被重做 (Redo) 了。
  7. 组提交 (Group Commit):

    • 问题: 在高并发场景下,如果每个事务提交都要独立进行一次 fsync()(当 innodb_flush_log_at_trx_commit=1),磁盘 I/O 会成为巨大瓶颈。
    • 解决方案: 组提交。
      • 当第一个事务 T1 发起提交请求时,它成为该批次的“领导者 (leader)”。
      • 在 T1 将它的 redo log 写入 Log Buffer 并准备调用 fsync() 的短暂窗口期内,其他并发提交的事务 (T2, T3…) 可以快速地将它们的 redo log 也追加到 Log Buffer 中。
      • 关键: 领导者 T1 负责调用一次 fsync(),将 Log Buffer当前累积的所有 T1, T2, T3… 的 redo log 一次性刷到磁盘。
      • 效果: 多个事务的日志刷盘 I/O 被合并成一次 fsync,极大提升了高并发下的提交吞吐量。

🔄 二、Undo Log (回滚日志) 实现详解

  1. 逻辑日志:

    • Undo log 记录的是逻辑操作的逆操作。
    • INSERT 操作: Undo log 记录该行的主键信息,用于在回滚时执行 DELETE
    • DELETE 操作: Undo log 记录被删除行的所有列完整内容(在删除前保存),用于回滚时执行 INSERT
    • UPDATE 操作: Undo log 记录被修改列的旧值(以及 WHERE 条件所需信息,通常是主键),用于回滚时执行 UPDATE ... SET col = old_value ...
    • 关键: Undo log 使得回滚操作可以“撤销”之前的修改,而不仅仅是物理上的反转。
  2. 存储位置 - Undo Tablespaces:

    • 历史: MySQL 5.6 及之前,undo log 存储在共享的 ibdata1 系统表空间中。
    • 现状 (推荐): MySQL 5.7+ 引入了独立的 Undo Tablespaces (默认文件如 undo_001, undo_002),由 innodb_undo_directory 指定目录,innodb_undo_tablespaces 控制数量。这提高了管理灵活性(可独立设置大小、自动扩展)和 I/O 性能(分离存储)。
    • 内部结构: Undo Tablespace 内部被划分为多个 Undo Segments。每个 Undo Segment 包含多个 Undo Slots (通常 1024 个),每个 Slot 用于存储一个事务的 undo log 链(一个事务可能有多个操作,产生多条 undo log,形成链)。
  3. 事务关联与链式结构:

    • 每个事务开始时,会被分配一个唯一的 Transaction ID (trx_id)
    • 事务会被分配到一个 Undo Segment 和其中的一个 Undo Slot。
    • 事务对数据的每次修改(INSERT/DELETE/UPDATE)都会产生一条 undo log 记录。
    • 链式结构: 同一个事务产生的多条 undo log 记录,会通过指针连接成一个链表。链表的头信息存储在事务对象中。这使得回滚时可以按操作逆序执行。
    • 数据页关联: 每个数据行记录(在聚集索引的叶子节点)的隐藏列 DB_ROLL_PTR (Rollback Pointer) 指向修改该行的最新事务所对应的 undo log record。如果该行被多次修改,则通过 undo log record 中的指针可以回溯到更早的版本,形成一条版本链。这是 MVCC 读取历史版本的基础。
  4. 回滚段 (Rollback Segments):

    • 概念: 回滚段是 Undo Tablespace 中 Undo Segments 的管理单元。它是一个内存数据结构(trx_rseg_t),负责管理一组 Undo Segments。
    • 数量:innodb_rollback_segments 配置(默认 128)。每个回滚段管理固定数量的 Undo Segments。
    • 分配: 事务启动时,会根据策略(如 round-robin)选择一个回滚段,然后从该回滚段管理的 Undo Segments 中分配一个空闲的 Undo Slot 来存储其 undo log。
  5. Purge 机制:

    • 问题: 当事务提交后,它产生的 undo log 在理论上就可以删除了(因为不再需要用于回滚该事务)。但为了支持 MVCC,不能立即删除!因为可能还有更早启动的事务(Read View)需要依赖这些 undo log 来访问行记录的历史版本。
    • 解决: Purge 线程。
      • InnoDB 维护一个称为 History List 的链表,链接着所有已提交事务产生的、但还不能被物理删除的 undo log 链。
      • Purge 线程(后台线程)持续扫描 History List。
      • 对于每条 undo log record,Purge 线程检查:系统中是否还存在任何活跃的 Read View 需要访问这条 undo log 所代表的旧版本数据? 判断依据是比较 undo log record 关联的事务 ID (trx_id) 与当前系统中最老的 Read View (up_limit_id)。
      • 条件: 如果 trx_id < up_limit_id (即产生该 undo log 的事务在所有现存 Read View 开启之前就已提交),那么这条 undo log record 以及它所代表的整个旧版本行记录就不再被任何事务所需要了。
      • 操作: Purge 线程会:
        • 物理删除该 undo log record。
        • 如果该 undo log 对应的是一个被 DELETE 操作标记删除的行(在 InnoDB 中,DELETE 操作只是标记删除,称为 delete-marked),并且该行所有旧版本都已不再需要,Purge 线程会真正物理删除该行记录(清理聚集索引和二级索引),回收空间。这个过程称为 Purge
    • 重要性: Purge 是防止 undo log 空间无限增长和清除垃圾数据的关键。如果 Purge 跟不上修改速度(长事务会阻塞 Purge),可能导致 undo tablespace 膨胀甚至耗尽空间。
  6. Undo Log 与 MVCC:

    • 当一个事务启动并执行一个 SELECT 语句时:
      • 它会生成一个 Read View。Read View 包含几个关键信息:
        • m_ids:当前活跃(未提交)事务 ID 列表。
        • min_trx_idm_ids 中的最小值。
        • max_trx_id:下一个将被分配的事务 ID(当前系统最大事务 ID + 1)。
        • creator_trx_id:创建该 Read View 的事务自身 ID(如果是只读事务,可能为 0)。
      • 当访问一行数据时:
        • 读取该行的 DB_TRX_ID(最近修改它的事务 ID)。
        • 判断该行对当前 Read View 是否可见:
          • 如果 DB_TRX_ID < min_trx_id:说明修改该行的事务在当前 Read View 创建前已提交,可见
          • 如果 DB_TRX_ID >= max_trx_id:说明修改该行的事务在当前 Read View 创建后才开启,不可见
          • 如果 min_trx_id <= DB_TRX_ID < max_trx_id:检查 DB_TRX_ID 是否在 m_ids 中。
            • 在:说明修改它的事务还在活跃,不可见
            • 不在:说明修改它的事务已提交,可见
        • 如果需要不可见的版本: 通过该行的 DB_ROLL_PTR 找到指向的 undo log record。
          • 从 undo log record 中重建该行在修改前的数据(旧版本)。
          • 检查这个旧版本的 DB_TRX_ID 对当前 Read View 是否可见。
          • 如果还不可见,继续沿着 undo log 链(通过 undo log record 中的指针)回溯到更早的版本,直到找到一个可见的版本或到达链头。
    • 通过这种方式,SELECT 语句可以在不加锁的情况下,获取到在它启动时已提交的数据快照。

🧩 三、Redo Log 与 Undo Log 的协同工作

  1. 修改数据:

    • 事务修改一行数据 (e.g., UPDATE)。
    • InnoDB 在 Buffer Pool 中找到(或读入)该数据页。
    • 为该修改生成 undo log record (记录旧值),写入 Undo Tablespace 的 Undo Segment。这个 undo log 的写入本身也会产生 redo log! (因为 undo log 也是持久化数据,其修改也需要崩溃恢复保障)。
    • 在 Buffer Pool 中更新数据页(变成脏页),更新行的 DB_TRX_ID 为当前事务 ID,DB_ROLL_PTR 指向刚生成的 undo log record。
    • 生成 redo log record (描述对数据页的修改操作),写入 Log Buffer。
  2. 事务提交:

    • 根据 innodb_flush_log_at_trx_commit 策略,将包含本次事务数据修改和 undo log 修改的 redo log 记录(在 Log Buffer 中)刷写到磁盘的 redo log files(可能需要 fsync)。
    • 注意: Undo log 本身在提交时需要立即刷盘!它的持久性依赖于保护它的 redo log 已经刷盘。如果崩溃,redo log 恢复时会重建出 undo log 的状态。
    • 事务标记为已提交。其产生的 undo log 被移到 History List,等待 Purge。
  3. 事务回滚:

    • 使用该事务的 undo log 链,逆序执行每个操作的逆操作(逻辑回滚)。
    • 这些回滚操作本身也是数据修改,同样会产生 redo log!(保证回滚操作本身的持久性)。
    • 回滚完成后,该事务的 undo log 可以被立即清理(不需要进入 History List)。
  4. 崩溃恢复:

    • Phase 1: Redo Pass (重做阶段)
      • 从最近的 checkpoint LSN 开始扫描 redo log。
      • 应用所有有效的 redo log record(包括:用户数据页的修改、undo log 页的修改、索引页的修改等)。
      • 此时,Buffer Pool 被恢复到崩溃那一刻的状态:包含了所有已提交事务和部分未提交事务所做的修改,undo log 也被恢复到正确状态。
    • Phase 2: Undo Pass (回滚阶段)
      • 扫描恢复出来的 undo log。
      • 对于所有在崩溃时处于 ACTIVE 状态的事务(未提交),利用它们的 undo log 链执行回滚操作(撤销修改)。
      • 这些回滚操作同样会产生 redo log,但由于此时数据库尚未开放访问,这些 redo log 只在内存中执行(不写入文件),目的是让 Buffer Pool 中的数据页回滚到一致状态。
    • 最终: 数据库恢复到崩溃前最后一个一致的状态:所有已提交事务的修改都在,所有未提交事务的修改都被撤销。

📌 总结

  • Redo Log 是 InnoDB 的“安全气囊”🧯,通过 WAL 原则和高效的物理逻辑日志、LSN、Checkpoint、组提交等机制,确保已提交事务的修改永不丢失,并极大提升了写入性能和崩溃恢复速度。它是持久性的守护者。
  • Undo Log 是 InnoDB 的“时光机”⏳,通过记录逻辑逆操作、构建行版本链,并与 Read View 配合,实现了事务的原子性回滚和 MVCC 多版本并发控制(非锁定读)。它是原子性和一致性读的关键。
  • 两者密不可分:Undo Log 的持久化依赖 Redo Log 的保护;崩溃恢复需要先 Redo 恢复现场(包括 Undo Log),再用恢复的 Undo Log 进行回滚。Purge 机制则负责清理不再需要的 Undo Log 和垃圾行版本。
  • 理解 Redo/Undo Log 的实现,是深入掌握 InnoDB 事务、并发控制和崩溃恢复原理的核心。 它们共同构成了 InnoDB 高可靠、高性能事务引擎的基石。

JPA 实现机制

一、redo log 与 undo log 深度解析

1. redo log (重做日志)

作用与原理:

事务执行
生成redo记录
写入redo log buffer
事务提交时
刷新到redo log文件
数据库崩溃恢复

核心特性:

  • 持久性保障:确保已提交事务的修改不会丢失
  • 顺序写入:追加写入模式,避免磁盘随机I/O
  • 循环覆盖:固定大小文件,循环使用
  • WAL(Write-Ahead Logging):先写日志再修改数据页

工作流程:

  1. 事务修改数据时生成 redo 记录
  2. 记录写入内存中的 redo log buffer
  3. 事务提交时,buffer 内容强制刷盘(fsync
  4. 后台线程将脏页刷新到数据文件
  5. 崩溃恢复时重放 redo log

2. undo log (回滚日志)

作用与原理:

事务开始
生成undo记录
写入undo段
事务回滚/多版本控制
回滚修改/提供一致性读

核心特性:

  • 原子性保障:支持事务回滚
  • 多版本控制(MVCC):实现非锁定读
  • 链式结构:通过指针形成版本链
  • 独立存储:存储在专门的 undo 表空间

工作流程:

  1. 事务修改前生成 undo 记录
  2. 记录旧值并构建版本链
  3. 回滚时应用 undo 记录恢复数据
  4. MVCC 读操作通过 undo 构建历史版本

3. redo 与 undo 对比

特性redo logundo log
主要目的崩溃恢复事务回滚/MVCC
写入时机修改发生时修改前
存储内容物理修改逻辑修改
生命周期独立于事务随事务结束而失效
清理机制检查点推进无活动事务依赖时
可见性仅内部使用用户可见(MVCC)

二、JPA 如何实现数据恢复与回滚

1. 事务回滚机制(依赖 undo log)

JPA 回滚实现:

@Transactional
public void updateEntity(Long id, String newValue) {try {Entity entity = entityManager.find(Entity.class, id);entity.setValue(newValue);// 模拟业务异常if (invalidCondition) {throw new BusinessException("Rollback trigger");}} catch (Exception e) {// 自动回滚(利用undo log)TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();throw e;}
}

底层过程:

  1. JPA 触发 SQL UPDATE 语句
  2. 数据库生成 undo 记录存储旧值
  3. 事务回滚时执行反向操作
  4. 应用 undo log 恢复数据到修改前状态

2. 崩溃恢复机制(依赖 redo log)

JPA 无需特殊处理:

@Service
public class RecoveryService {@Transactionalpublic void criticalOperation(Entity entity) {// 1. JPA保存操作repository.save(entity); // 2. 生成redo记录// 3. 此时系统崩溃...}
}

恢复过程:

  1. 数据库重启时进入恢复模式
  2. 读取 redo log 找出已提交事务
  3. 重做(replay)这些事务的修改
  4. 撤销(rollback)未完成事务的修改
  5. 保证数据达到崩溃前的一致状态

3. MVCC 实现(依赖 undo log)

JPA 一致性读示例:

@Transactional(isolation = Isolation.REPEATABLE_READ)
public void consistentRead(Long id) {// 第一次读取(版本V1)Entity e1 = repository.findById(id).orElseThrow();// 其他事务修改该记录...// 第二次读取(仍为V1版本)Entity e2 = repository.findById(id).orElseThrow();assert e1.getValue().equals(e2.getValue());
}

底层机制:

  1. 首次读取时记录当前事务ID
  2. 读取数据时检查行版本
  3. 如果行版本较新,通过 undo log 链找到合适版本
  4. 返回该事务开始时已提交的数据版本

三、JPA 高级事务控制

1. 保存点(嵌套事务)

@Transactional
public void nestedOperations() {Entity entity = new Entity("A");repository.save(entity);// 创建保存点Object savepoint = TransactionAspectSupport.currentTransactionStatus().createSavepoint();try {entity.setValue("B");repository.save(entity);throw new RuntimeException("Partial rollback");} catch (Exception e) {// 回滚到保存点TransactionAspectSupport.currentTransactionStatus().rollbackToSavepoint(savepoint);}// 此处entity.value仍为"A"
}

2. 自定义恢复逻辑

@Transactional
public void customRecovery(Long id) {Entity entity = repository.findById(id).orElseThrow();String originalValue = entity.getValue(); // 保存原始值try {entity.setValue("NEW");repository.save(entity);riskyOperation();} catch (Exception e) {// 手动恢复数据entity.setValue(originalValue);repository.save(entity);throw e;}
}

3. 悲观锁实现

@Transactional
public void pessimisticUpdate(Long id) {// 获取行级排他锁Entity entity = entityManager.find(Entity.class, id, LockModeType.PESSIMISTIC_WRITE);entity.setValue("Locked");// 其他事务在此处会被阻塞
}

四、性能优化实践

1. 批量操作优化

@Transactional
public void batchUpdate(List<Entity> entities) {int batchSize = 50;for (int i = 0; i < entities.size(); i++) {entityManager.persist(entities.get(i));if (i > 0 && i % batchSize == 0) {entityManager.flush(); // 分批刷新entityManager.clear(); // 清理持久化上下文}}
}

2. redo log 优化配置

# PostgreSQL 配置示例
# 增加WAL缓冲区
wal_buffers = 16MB# 异步提交提升性能
synchronous_commit = off# 日志检查点间隔
checkpoint_timeout = 15min

3. undo 表空间管理

-- 创建专用undo表空间
CREATE TABLESPACE undots LOCATION '/path/to/undo';-- 配置自动扩展
ALTER TABLESPACE undots SET (autovacuum = on, autovacuum_vacuum_cost_limit = 2000
);

五、异常处理最佳实践

事务状态检测

@Transactional
public void safeOperation() {try {// 业务操作...} catch (Exception e) {// 检测事务状态TransactionStatus status = TransactionAspectSupport.currentTransactionStatus();if (status.isRollbackOnly()) {logger.error("Transaction marked for rollback", e);} else if (!status.isCompleted()) {status.setRollbackOnly();}throw new CustomException("Operation failed", e);}
}

重试机制

@Retryable(maxAttempts = 3, backoff = @Backoff(delay = 1000))
@Transactional
public void retryableOperation() {// 可能触发乐观锁异常的操作Entity entity = repository.findById(id, LockModeType.OPTIMISTIC).orElseThrow();entity.setValue(newValue);repository.save(entity);
}

总结

  1. redo/undo 协同工作

    • redo log 保证持久性,确保提交事务不丢失
    • undo log 保证原子性,支持回滚和 MVCC
  2. JPA 事务实现

    • 回滚依赖数据库 undo log 机制
    • 崩溃恢复由数据库通过 redo log 自动完成
    • MVCC 通过 undo log 版本链实现一致性读
  3. 最佳实践

    应用设计
    合理事务边界
    短事务原则
    明确异常处理
    优化批量操作
    数据库配置
    redo log优化
    undo空间管理
    定期维护
  4. 性能关键

    • 批量操作时分批刷新和清理上下文
    • 根据业务需求选择合适隔离级别
    • 监控 redo/undo 空间使用和性能指标

理解 redo/undo 的底层机制,结合 JPA 的事务抽象能力,可以构建出既保证数据一致性又具有高性能的应用程序。数据库的日志系统是现代数据管理的基石,JPA 通过其事务管理 API 让开发者能够充分利用这些机制而无需关注底层细节。

http://www.dtcms.com/a/294934.html

相关文章:

  • 智能小e-集成配置
  • Nestjs框架: 基于Prisma的多租户功能集成和优化
  • 使用抓取 API 可靠高效地提取亚马逊 (Amazon)数据
  • CCD工业相机系统设计——基于FPGA设计
  • SQL执行顺序
  • LLM 隐藏层特征增强技术
  • 同步型降压转换器的“同步”是什么意思?
  • Vite 7.0 引入的几个重要新 API 详解
  • 三极管与场效应管的对比
  • Python脚本服务器迁移至K8S集群部署
  • k8s中的configmap存储
  • JavaWeb-Servlet
  • 内外网互传文件 安全、可控、便捷的跨网数据交换
  • 服务器版本信息泄露-iis返回包暴露服务器版本信息
  • Node.js 倒计时图片服务部署与 Nginx 反向代理实战总结
  • RCE随笔-奇技淫巧(2)
  • Android热修复实现方案深度分析
  • AI面试如何提升物流行业招聘效率?实战案例解析
  • ESP32-S3学习笔记<5>:SPI的应用
  • JDK 介绍与使用指南
  • CMake进阶:检查头文件存在性(check_include_file 和 check_include_fileCXX)
  • uniapp拦截返回事件
  • 应该切换到 NVMe 吗?
  • 学习 Pandas 库:Series 与 DataFrame 核心操作指南
  • c语言:预处理详解
  • CRMEB 单商户PRO多商户通用去版权教程
  • 二叉树解析
  • 51c大模型~合集158
  • RockyLinux 9.6 解决删除home后无法开机问题
  • 视觉BPE统一多模态理解-北大