开发老大要求通过binlog查询一条被修改的数据,数据被查出后问我,有没有可能binlog中不会记录,回答不会,因为数据被修改,若失败直接回滚,不会在binlog中记录,此刻一个朋友用了洪荒之力告诉我,失败的话也会记录,坐地无语,因为他sqlserver dba,用sqlserver的思维考虑MySQL,哈哈哈哈哈,用实验让他闭嘴!
简单测试步骤如下:
root(yoon)> flush logs;
Query OK, 0 rows affected (0.01 sec)
root((none))> show binlog events in 'mysql-bin.000041';
+------------------+-----+-------------+-----------+-------------+---------------------------------------+
| Log_name | Pos | Event_type | Server_id | End_log_pos | Info |
+------------------+-----+-------------+-----------+-------------+---------------------------------------+
| mysql-bin.000041 | 4 | Format_desc | 232242 | 120 | Server ver: 5.6.26-log, Binlog ver: 4 |
+------------------+-----+-------------+-----------+-------------+---------------------------------------+
root(yoon)> begin;
Query OK, 0 rows affected (0.00 sec)
root(yoon)> update yoon set id=7 where id=1;
Query OK, 1 row affected (0.02 sec)
Rows matched: 1 Changed: 1 Warnings: 0
在没有commit情况下,二进制日志的位置偏移量未发生变化:
root(yoon)> show binlog events in 'mysql-bin.000041';
+------------------+-----+-------------+-----------+-------------+---------------------------------------+
| Log_name | Pos | Event_type | Server_id | End_log_pos | Info |
+------------------+-----+-------------+-----------+-------------+---------------------------------------+
| mysql-bin.000041 | 4 | Format_desc | 232242 | 120 | Server ver: 5.6.26-log, Binlog ver: 4 |
+------------------+-----+-------------+-----------+-------------+---------------------------------------+
root(yoon)> commit;
Query OK, 0 rows affected (1.01 sec)
提交后再次查看日志偏移量,发生变化,并记录在binlog中
root(yoon)> show binlog events in 'mysql-bin.000041';
+------------------+-----+-------------+-----------+-------------+---------------------------------------------+
| Log_name | Pos | Event_type | Server_id | End_log_pos | Info |
+------------------+-----+-------------+-----------+-------------+---------------------------------------------+
| mysql-bin.000041 | 4 | Format_desc | 232242 | 120 | Server ver: 5.6.16-log, Binlog ver: 4 |
| mysql-bin.000041 | 120 | Query | 232242 | 199 | BEGIN |
| mysql-bin.000041 | 199 | Query | 232242 | 304 | use `yoon`; update yoon set id=7 where id=1 |
| mysql-bin.000041 | 304 | Xid | 232242 | 335 | COMMIT /* xid=18 */ |
+------------------+-----+-------------+-----------+-------------+---------------------------------------------+
MySQL事务提交过程
开启binlog后事务提交流程会变成两阶段提交,这里的两阶段提交并不涉及分布式事务,当然mysql把它称之为内部xa事务(Distributed Transactions),与之对应的还有一个外部xa事务。
这里所谓的两阶段提交分别是prepare阶段和commit阶段。
内部xa事务主要是mysql内部为了保证binlog与redo log之间数据的一致性而存在的,这也是由其架构决定的(binlog在mysql层,而redo log 在存储引擎层);
外部xa事务则是指支持多实例分布式事务,这个才算是真正的分布式事务。
既然是xa事务,必然涉及到两阶段提交,对于内部xa而言,同样存在着提交的两个阶段。
下文会结合源码详细解读内部xa的两阶段提交过程,以及各种情况下,mysqld crash后,mysql如何恢复来保证事务的一致性。
数据库版本:5.6.16
操作系统版本:CentOS 6.5
配置文件参数:
log-bin=/my/log/mysql-bin
binlog_format=ROW
set autocommit=0
innodb_support_xa=1
sync_binlog=1
innodb_flush_log_at_trx_commit=1
【innodb_flush_log_at_trx_commit=1,sync_binlog=1
不同的模式区别在于,写文件调用write和落盘fsync调用的频率不同,所导致的后果是mysqld 或 os crash后,不严格的设置可能会丢失事务的更新。
双一模式是最严格的模式,这种设置情况下,单机在任何情况下不会丢失事务更新。】
测试条件:
set autocommit=0;
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` int(20) NOT NULL,
`account` varchar(20) NOT NULL,
`name` varchar(20) NOT NULL,
PRIMARY KEY (`id`),
KEY `id` (`id`) USING BTREE,
KEY `name` (`name`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
测试语句:
insert into user values(1, 'sanzhang', '张三');
commit;
prepare阶段:
1.设置undo state=TRX_UNDO_PREPARED; //trx_undo_set_state_at_prepare调用
2.刷事务更新产生的redo日志;【步骤1产生的redo日志也会刷入】
MYSQL_BIN_LOG::prepare
ha_prepare_low
{
engine:
binlog_prepare
innobase_xa_prepare
mysql:
trx_prepare_for_mysql
{
1.trx_undo_set_state_at_prepare //设置undo段的标记为TRX_UNDO_PREPARED
2.设置事务状态为TRX_STATE_PREPARED
3.trx_flush_log_if_needed //将产生的redolog刷入磁盘
}
}
commit阶段:
1.将事务产生的binlog写入文件,刷入磁盘;
2.设置undo页的状态,置为TRX_UNDO_TO_FREE或TRX_UNDO_TO_PURGE; // trx_undo_set_state_at_finish调用
3.记录事务对应的binlog偏移,写入系统表空间; //trx_sys_update_mysql_binlog_offset调用
MYSQL_BIN_LOG::commit
ordered_commit
{
1.FLUSH_STAGE
flush_cache_to_file // 刷binlog
2.SYNC_STAGE
sync_binlog_file //Call fsync() to sync the file to disk.
3.COMMIT_STAGE
ha_commit_low
{
binlog_commit
innobase_commit
trx_commit(trx)
{
trx_write_serialisation_history(trx, mtr); //更新binlog位点,设置undo状态
trx_commit_in_memory(trx, lsn); //释放锁资源,清理保存点列表,清理回滚段
}
}