日志暴涨源于事务、索引、隔离级别叠加导致的指数级日志放大。mysql三重日志(binlog/redo/undo)不共享不压缩;sql server因快照隔离和tempdb版本存储及长事务致日志暴增;oracle归档日志增速取决于dml行数、提交频率与触发器;所有数据库中每多一个二级索引,单行update日志量成倍增长。

日志暴涨不是“写得多”,而是事务、索引、隔离级别三者叠加产生的指数级放大效应。单次百万行UPDATE,在MySQL里可能生成数GB binlog + undo log + redo log;在SQL Server中则直接撑爆tempdb和事务日志文件(ldf)。根本原因不在数据量本身,而在日志记录的粒度和生命周期。
MySQL:binlog + undo + redo 三重写入叠加
一条 UPDATE 在 MySQL 中会同时触发三层日志写入:
-
binlog(Server 层):ROW 格式下默认记录每行新旧值全量,binlog_row_image=FULL是默认值——哪怕只改一个字段,也写整行旧值+整行新值 -
redo log(InnoDB 层):保证崩溃恢复,记录物理页变更。大更新引发大量页分裂、合并,每页操作都生成独立 redo 记录 -
undo log(InnoDB 层):MVCC 和回滚必需,每行更新都存完整旧值;主键变更或字段变长时,undo 体积翻倍(DELETE+INSERT 模式)
示例:更新 10 万行,平均每行 200 字节 → binlog 写入可能超 400MB(FULL 模式),undo 日志再叠加 300MB+,redo 因页分裂再加 100MB。三者不共享、不压缩、不同步清理。
SQL Server:tempdb 版本存储 + 事务日志双爆
当 READ_COMMITTED_SNAPSHOT=ON 或 ALLOW_SNAPSHOT_ISOLATION=ON 时,每次 UPDATE 都在 tempdb 中保留旧版本行(row version),且只要事务未提交、或有长查询依赖该快照,这些版本就无法清理。
- 没走索引的
WHERE条件 → 引发排序/哈希溢出到tempdb,执行计划中出现Sort或Hash Match节点即为信号 - 单事务内更新 50 万行 →
tempdb瞬间增长数GB,sys.dm_tran_active_snapshot_database_transactions中elapsed_time_seconds高企说明版本被钉住 - 事务日志(
.ldf)暴涨主因是单事务太长:日志不能截断(log truncation),直到事务结束;log_reuse_wait_desc = 'ACTIVE_TRANSACTION'即为此类
Oracle:归档日志(archivelog)暴增直指 DML 频率与范围
AWR 报告里真正要盯的不是 “SQL ordered by CPU Time”,而是 “SQL ordered by Rows Processed” + “SQL ordered by Executions”。归档日志生成速率(ARCHIVE LOG LIST 中的 Log Sequence 增速)与以下强相关:
- 单条 UPDATE 影响行数超高(如无
WHERE或条件失效)→ 物理读暴涨 + redo size 指数上升 - 批量脚本用循环逐条
COMMIT→ 每次 commit 都刷一次日志,小事务高频提交比大事务更伤 I/O 吞吐 - 触发器隐式扩大 DML 范围:主表 INSERT 触发库存扣减 + 审计插入 → redo size ×3,但 AWR 中只显示主 SQL 的
sql_id
查不到对应 SQL?直接从 DBA_HIST_ACTIVE_SESS_HISTORY 按 sql_opname IN ('UPDATE') + object_name 聚合,绕过 SQL_ID 缓存缺失问题。
所有数据库共通的隐形放大器:索引与写入放大
每多一个二级索引,每次 UPDATE 就多一次索引树维护 + 对应日志写入。5 个索引的表,单行 UPDATE 实际产生 6 倍于数据行的日志量(1 聚簇 + 5 二级)。
- 宽索引字段(如
VARCHAR(1000))→ 日志中按最大长度预留空间,即使实际只存 10 字节 - 函数索引(如
UPPER(name))→ UPDATE 基础列时强制重算并写日志,哪怕表达式结果未变 - 唯一约束检查 → INSERT/UPDATE 时额外做 duplicate check,失败回滚仍记日志
真正容易被忽略的是:日志暴涨往往不是某条 SQL 的锅,而是它激活了多个隐性路径——触发器、函数索引、快照隔离、宽索引、全表扫描 WHERE ——这些模块单独看都“合理”,叠在一起却让日志体积失控。上线前必须用真实数据压测日志增长曲线,而不是只看执行时间。










