社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  DATABASE

MySQL的三大日志

脚本之家 • 1 年前 • 325 次点击  
将 脚本之家 设为“星标
第一时间收到文章更新
图片
来源 | 苏三说技术 (ID:susanSayJava)

前言

飞机失事靠黑匣子还原真相,MySQL崩溃靠三大日志保障数据安全。

作为一个工作多年的程序员,我见过太多因日志配置不当引发的灾难:数据丢失、主从同步中断、事务回滚失败...

今天,我将用最通俗的方式,带你彻底掌握MySQL三大日志的底层原理,希望对你会有所帮助。

一、引子:一个数据丢失的教训

事故现场:某电商平台数据库服务器宕机后,发现最近2小时订单数据丢失。

问题根源:  错误配置导致redo log刷盘失效:


SHOW VARIABLES LIKE 'innodb_flush_log_at_trx_commit';
+--------------------------------+-------+
| Variable_name                  | Value |
+--------------------------------+-------+
| innodb_flush_log_at_trx_commit | 0     |  -- 应设为1
+--------------------------------+-------+

核心结论

  1. 日志系统是MySQL的安全气囊
  2. 不理解日志机制,等于在数据安全上裸奔

二、Redo Log:保证持久性的守护神

2.1 核心作用:崩溃恢复

WAL原则(Write-Ahead Logging)

2.2 物理结构解析

循环写入机制

关键参数

-- 查看日志配置
SHOW VARIABLES LIKE 'innodb_log%';
+---------------------------+---------+
| Variable_name             | Value   |
+---------------------------+---------+
| innodb_log_file_size      | 50331648| -- 单个日志文件大小
| innodb_log_files_in_group | 2       | -- 日志文件数量
| innodb_log_buffer_size    | 16777216| -- 缓冲区大小
+---------------------------+---------+

2.3 刷盘策略实战




    
// JDBC事务提交示例
Connection conn = DriverManager.getConnection(url, user, pwd);
try {
    conn.setAutoCommit(false);
    Statement stmt = conn.createStatement();
    stmt.executeUpdate("UPDATE account SET balance=balance-100 WHERE id=1");
    stmt.executeUpdate("UPDATE account SET balance=balance+100 WHERE id=2");
    
    // 核心配置:刷盘策略
    conn.setClientInfo("innodb_flush_log_at_trx_commit""1");
    conn.commit(); // 触发redo log刷盘
catch (SQLException e) {
    conn.rollback();
}

刷盘策略对比

参数值
安全性
性能
适用场景
0
低(每秒刷)
最高
可丢失数据的缓存
1
最高(实时)
最低
金融交易系统
2
中(OS缓存)
较高
常规业务系统

三、Undo Log:事务回滚的时光机

3.1 MVCC实现原理

多版本控制流程

3.2 回滚操作源码级解析

-- 事务回滚示例
START TRANSACTION;
UPDATE users SET name='张三' WHERE id=1
-- 在undo log中记录:
-- | 事务ID | 行ID | 旧值 | 回滚指针 |
-- | 101    | 1    | '李四'| 0x7F8A9B|

ROLLBACK-- 根据undo log恢复数据

3.3 长事务引发的灾难

问题场景

-- 查询运行超过60秒的事务
SELECT * FROM information_schema.innodb_trx 
WHERE TIME_TO_SEC(TIMEDIFF(NOW(), trx_started)) > 60;

严重后果

  1. Undo Log暴涨占用磁盘空间
  2. 历史版本链过长导致查询性能下降

解决方案

@Transactional(timeout = 30// 单位:秒
public void updateOrder(Order order) {
    // 业务逻辑
}

Spring Boot项目可以设置事务超时时间。

四、Binlog:主从复制的桥梁

4.1 三种格式深度对比

格式
特点
数据安全
复制效率
STATEMENT
记录SQL语句
ROW
记录行变化
MIXED
自动切换模式

ROW格式的优势

-- 原始SQL
UPDATE users SET status=1 WHERE age>30;

-- ROW格式binlog实际记录
/* 修改前镜像 */
id:1, status:0, age:35
id:2, status:0, age:40
/* 修改后镜像 */
id:1, status:1, age:35
id:2, status:1, age:40

4.2 主从复制全流程剖析

4.3 数据恢复实战

场景:误删全表数据
恢复步骤

# 1. 解析binlog找到删除位置
mysqlbinlog --start-position=763 --stop-position=941 binlog.000001 > recovery.sql

# 2. 提取回滚SQL
grep -i 'DELETE FROM users' recovery.sql

# 3. 生成反向补偿语句
sed 's/DELETE FROM/INSERT INTO/g' recovery.sql > rollback.sql

# 4. 执行恢复
mysql -u root -p < rollback.sql

五、三大日志协同工作图

更新语句执行流程

两阶段提交关键点

  1. redo log prepare 与 binlog 写入的原子性
  2. 崩溃恢复时的决策逻辑:
  • binlog完整:提交事务
  • binlog不完整:回滚事务

六、生产环境优化指南

6.1 参数调优模板

my.cnf 关键配置:

[mysqld]
# Redo Log
innodb_log_file_size = 2G        # 建议4个日志文件
innodb_log_files_in_group = 4
innodb_flush_log_at_trx_commit = 1

# Undo Log
innodb_max_undo_log_size = 1G
innodb_undo_log_truncate = ON
innodb_purge_threads = 4

# Binlog
server_id = 1
log_bin = /data/mysql-bin
binlog_format = ROW
binlog_expire_logs_seconds = 604800 # 保留7天
sync_binlog = 1                   # 每次提交刷盘

6.2 监控指标清单

-- 关键监控SQL
SELECT
/* Redo Log */
  (SELECT VARIABLE_VALUE 
   FROM performance_schema.global_status 
   WHERE VARIABLE_NAME='Innodb_os_log_written'AS redo_written,
   
/* Undo Log */
  (SELECTSUM(DATA_LENGTH) 
   FROM information_schema.TABLES 
   WHERE TABLE_SCHEMA='mysql'
   AND TABLE_NAME  LIKE'undo%'AS undo_size,
   
/* Binlog */
  (SELECT VARIABLE_VALUE 
   FROM performance_schema.global_status 
   WHERE VARIABLE_NAME='Binlog_cache_disk_use'AS binlog_disk_use;

6.3 常见问题解决方案

问题1:redo log文件设置过小导致频繁checkpoint。

现象

SHOW GLOBAL STATUS LIKE 'Innodb_log_waits';
+------------------+-------+
| Variable_name    | Value |
+------------------+-------+
| Innodb_log_waits | 542   | -- 值>0表示存在等待
+------------------+-------+

解决

# 动态调整(需重启生效)
SET GLOBAL innodb_log_file_size = 2147483648

问题2:大事务导致binlog暴涨。

预防方案

// 事务拆分示例
public void batchProcess(List orders) {
    int batchSize = 100// 每100条一个事务
    for (int i=0; i
        transactionTemplate.execute(status -> {
            List subList = orders.subList(i, Math.min(i+batchSize, orders.size()));
            processBatch(subList);
            return null;
        });
    }
}

七、总结

  1. Redo Log是生命线

  • 配置原则:innodb_flush_log_at_trx_commit=1 + 足够大的日志文件
  • 监控重点:Innodb_log_waits 应趋近于0
  • Undo Log是后悔药

    • 及时清理:开启 innodb_undo_log_truncate
    • 避免长事务:监控 information_schema.innodb_trx
  • Binlog是复制基石

    • 格式选择:金融级系统必须用ROW格式
    • 同步策略:主从复制时 sync_binlog=1

    数据库的可靠性不是偶然发生的,而是通过三大日志的精密协作实现的。

    当你下次执行COMMIT时,请记住背后有三个强大的守护者在为你工作:

    1. Redo Log确保你的数据不会丢失
    2. Undo Log保证你的操作可以撤销
    3. Binlog让数据在集群间流动
    
    
    
        
    

    END

    图片

      推荐阅读:
    1. 面试官:MySQL 8.0版本引入的 hash join 有什么优势?
    2. 面试官:MySQL 使用 group by 语句时发现执行很慢,可能是什么原因?
    3. 为 MySQL 加字段,我竟然遇史诗级 Bug?
    4. 面试官:使用 MySQL 时如果发现数据不一致了,可能是什么原因?

    5. 面试官:MySQL表中有2千万条数据,B+树层高是多少?

    Python社区是高质量的Python/Django开发社区
    本文地址:http://www.python88.com/topic/184399