MySQL事务-binlog与分布式事务

Posted 梁小明10000

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MySQL事务-binlog与分布式事务相关的知识,希望对你有一定的参考价值。

mysql事务-binlog与分布式事务

1、binlog_rows_query_log_events
这个参数默认是off


"root@localhost:mysql.sock  [xx]>show variables like '%binlog_rows_query_log_events%';
+------------------------------+-------+
| Variable_name                | Value |
+------------------------------+-------+
| binlog_rows_query_log_events | OFF   |
+------------------------------+-------+
1 row in set (0.00 sec)

"root@localhost:mysql.sock  [xx]>flush binary logs;
Query OK, 0 rows affected (0.05 sec)

"root@localhost:mysql.sock  [xx]> create table test_bin_2 (a int);
Query OK, 0 rows affected (0.06 sec)

"root@localhost:mysql.sock  [xx]> insert into test_bin_2 values(1),(2);
Query OK, 2 rows affected (0.01 sec)
Records: 2  Duplicates: 0  Warnings: 0

"root@localhost:mysql.sock  [xx]>commit;
Query OK, 0 rows affected (0.00 sec)

"root@localhost:mysql.sock  [xx]>show master status;
+------------+----------+--------------+------------------+--------------------------------------------------+
| File       | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set                                |
+------------+----------+--------------+------------------+--------------------------------------------------+
| bin.000040 |      622 |              |                  | 790536c7-1936-11e8-a811-005056ad8986:1-101579736 |
+------------+----------+--------------+------------------+--------------------------------------------------+
1 row in set (0.00 sec)

"root@localhost:mysql.sock  [xx]>show binlog events in "bin.000040";
+------------+-----+----------------+-----------+-------------+-----------------------------------------------
----------------------------+| Log_name   | Pos | Event_type     | Server_id | End_log_pos | Info                                          
                            |+------------+-----+----------------+-----------+-------------+-----------------------------------------------
----------------------------+| bin.000040 |   4 | Format_desc    |        11 |         123 | Server ver: 5.7.17-log, Binlog ver: 4         
                            || bin.000040 | 123 | Previous_gtids |        11 |         194 | 790536c7-1936-11e8-a811-005056ad8986:1-1015797
34                          || bin.000040 | 194 | Gtid           |        11 |         259 | SET @@SESSION.GTID_NEXT= '790536c7-1936-11e8-a
811-005056ad8986:101579735' || bin.000040 | 259 | Query          |        11 |         360 | use `xx`; create table test_bin_2 (a int)     
                            || bin.000040 | 360 | Gtid           |        11 |         425 | SET @@SESSION.GTID_NEXT= '790536c7-1936-11e8-a
811-005056ad8986:101579736' || bin.000040 | 425 | Query          |        11 |         495 | BEGIN                                         
                            || bin.000040 | 495 | Table_map      |        11 |         546 | table_id: 274 (xx.test_bin_2)                 
                            || bin.000040 | 546 | Write_rows     |        11 |         591 | table_id: 274 flags: STMT_END_F               
                            || bin.000040 | 591 | Xid            |        11 |         622 | COMMIT /* xid=82 */                           
                            |+------------+-----+----------------+-----------+-------------+-----------------------------------------------
-- 只能看到页的变化

现在设置为on
"root@localhost:mysql.sock  [xx]>set binlog_rows_query_log_events=1;
Query OK, 0 rows affected (0.00 sec)


"root@localhost:mysql.sock  [xx]>show variables like "binlog_rows_query_log_events";
+------------------------------+-------+
| Variable_name                | Value |
+------------------------------+-------+
| binlog_rows_query_log_events | ON    |
+------------------------------+-------+
1 row in set (0.00 sec)

"root@localhost:mysql.sock  [xx]> insert into test_bin_2 values(3),(4);
Query OK, 2 rows affected (0.02 sec)
Records: 2  Duplicates: 0  Warnings: 0

"root@localhost:mysql.sock  [xx]> commit;
Query OK, 0 rows affected (0.00 sec)

"root@localhost:mysql.sock  [xx]>show binlog events in "bin.000040";
+------------+-----+----------------+-----------+-------------+-----------------------------------------------
----------------------------+| Log_name   | Pos | Event_type     | Server_id | End_log_pos | Info                                          
                            |+------------+-----+----------------+-----------+-------------+-----------------------------------------------
----------------------------+| bin.000040 |   4 | Format_desc    |        11 |         123 | Server ver: 5.7.17-log, Binlog ver: 4         
                            || bin.000040 | 123 | Previous_gtids |        11 |         194 | 790536c7-1936-11e8-a811-005056ad8986:1-1015797
34                          || bin.000040 | 194 | Gtid           |        11 |         259 | SET @@SESSION.GTID_NEXT= '790536c7-1936-11e8-a
811-005056ad8986:101579735' || bin.000040 | 259 | Query          |        11 |         360 | use `xx`; create table test_bin_2 (a int)     
                            || bin.000040 | 360 | Gtid           |        11 |         425 | SET @@SESSION.GTID_NEXT= '790536c7-1936-11e8-a
811-005056ad8986:101579736' || bin.000040 | 425 | Query          |        11 |         495 | BEGIN                                         
                            || bin.000040 | 495 | Table_map      |        11 |         546 | table_id: 274 (xx.test_bin_2)                 
                            || bin.000040 | 546 | Write_rows     |        11 |         591 | table_id: 274 flags: STMT_END_F               
                            || bin.000040 | 591 | Xid            |        11 |         622 | COMMIT /* xid=82 */                           
                            || bin.000040 | 622 | Gtid           |        11 |         687 | SET @@SESSION.GTID_NEXT= '790536c7-1936-11e8-a
811-005056ad8986:101579737' || bin.000040 | 687 | Query          |        11 |         757 | BEGIN                                         
                            || bin.000040 | 757 | Rows_query     |        11 |         817 | # insert into test_bin_2 values(3),(4)        
                            || bin.000040 | 817 | Table_map      |        11 |         868 | table_id: 274 (xx.test_bin_2)                 
                            || bin.000040 | 868 | Write_rows     |        11 |         913 | table_id: 274 flags: STMT_END_F               
                            || bin.000040 | 913 | Xid            |        11 |         944 | COMMIT /* xid=88 */
                            
通过信息可以看到多了Rows_query,可以看到对应着SQL信息

2、ROW

1. 当写入的数据量较小时,ROW和Statement所占用的空间差不多;
2. 当写入的数据量较大的时候(比如导入数据,
或者批量操作时[update tb set a=a+1;]),ROW要记录每行的变化,所以比较占用空间。
3. 且写入数据量很大时,ROW格式下,commit会比较耗时间,因为他还要写binlog( binlog在提交时才写入 )
假设更新一张几百万的表,产的 binlog 可能会有几百兆,当 commit 时,写入的数据量就是几百兆,所以会有 “ 阻塞 ” 等待的效果。
但其实是在写 binlog 到磁盘而已。
上面这些结论,大家可以通过实验去验证哦。

3、binlog_cache

binlog默认写入到 binlog_cache 中

"root@localhost:mysql.sock  [xx]>show variables like "binlog_cache_size";
+-------------------+-------+
| Variable_name     | Value |
+-------------------+-------+
| binlog_cache_size | 32768 | -- 默认为32K(内存中),线程级别的变,别设置的太大
+-------------------+-------+
1 row in set (0.00 sec)

"root@localhost:mysql.sock  [xx]>show global status like "binlog%";
+----------------------------+-------+
| Variable_name              | Value |
+----------------------------+-------+
| Binlog_cache_disk_use      | 0     |-- 记录了使用临时文件写二进制日志的次数(做监控需要关注这个)
| Binlog_cache_use           | 6     |-- 记录了使用缓冲写二进制日志的次数

| Binlog_stmt_cache_disk_use | 0     |
| Binlog_stmt_cache_use      | 3     |
+----------------------------+-------+
4 rows in set (0.00 sec)

写日志本来就挺慢的,现在cache写不下,再写入磁盘,然后再写binlog,就是两次写磁盘,就更慢了。
如果参数 Binlog_cache_disk_use 次数很多,
就要看一下 binlog_cache_size 设置是否太小 ,
或者事物本身是否太大 MySQL使用在OLTP的场景下,应该是很快速的小事物。
如果有大的事物,应该把大的事物拆成小事物去执行。

4、 binlog与redo的一致性(深入理解,后面会和复制有关)
使用内部分布式事物来保证一致性
在 commit 时( 无论用户自己入,或者系统自动添加 ),会有如下几个步骤:

(1)InnoDB 层 写 prepare log
  写的还是redo file (或者就是redo log,只是内容不一样,这里不是记录页的变化了)
  写入的是 xid ( 事物 id , show binlog events in “bin.000056”)
准确的说, xid 是写在 undo 页上的(后面会提到)
(2)MySQL 层 写 binlog
(3)InnoDB 层 写 commit log (这里同样也是redo log file)


(1)假设,如果没有第一步的 prepare log ,而是直接写第二步的 MySQL binlog ,以及接着写第三步的 InnoDB commit log :
此时假设出现 binlog写入成功 ,而 commit log(redo)写入失败 的情况( 比如宕机 ),
那随后机器重启时 恢复 时,就会对该事物 回滚 ;
万一此时的 binlog 已经传递到了 slave 机器上,且 slave上commit 了。
那此时 主从就不一致 了(Master 上回滚了 )

(2)现在有 prepare log 了以后, prepare log写入成功 ,假设还是 binlog写入成功 ,而 commit log(redo)写入失败 的情况下;
此时事物恢复的时候, 检查到prepare log写入成功,binlog写入成功 ,那就直接 commit 了( 可以理解成补了那次失败的 commit),
而不管commit log是否写入成功 了。

(3)如果 prepare log写入成功 , binlog写入失败 了,那恢复时,也会回滚

(4)如果 没开binlog ,就没有第一和第二步, 只写第三步的commit log ,恢复的时候没有commit log,就会回滚。

注意:
一个事物在prepare log中写入成功,在binlog中写入成功,那就必须要提交 (commit)
用户/系统 commit ===> redo file( prepare log) ===> binlog ===> redo file( commit log)
xid 即写入prepare log 中,也会写入到binlog 中,
当恢复时,会对比一下某个 xid 在两个文件中是否都存在,如果都存在,该xid对应的事物才会提交

5、分布式事务

终端1:
"root@localhost:mysql.sock  [xx]> create table test_xa_1(a int primary key);
Query OK, 0 rows affected (0.05 sec)

"root@localhost:mysql.sock  [xx]>xa start 'A'; -- 开始一个分布式事物A,不是传统的begin。而是 xa start
Query OK, 0 rows affected (0.00 sec)

"root@localhost:mysql.sock  [xx]> insert into test_xa_1 values(10);
Query OK, 1 row affected (0.00 sec)

"root@localhost:mysql.sock  [xx]>xa end 'A';  -- 结束一个分布式事物A,此时并未提交
Query OK, 0 rows affected (0.00 sec)

"root@localhost:mysql.sock  [xx]>xa prepare 'A';  -- 两阶段事物- prepare
Query OK, 0 rows affected (0.01 sec)

"root@localhost:mysql.sock  [xx]>xa recover;   -- 查看分布式事物
+----------+--------------+--------------+------+
| formatID | gtrid_length | bqual_length | data |
+----------+--------------+--------------+------+
|        1 |            1 |            0 | A    |
+----------+--------------+--------------+------+
1 row in set (0.00 sec)

终端2:
"root@localhost:mysql.sock  [xx]> select * from test_xa_1; -- 虽然在会话1中已 end 了,但是其实在会话2中是看不到的,符合ACID
Empty set (0.00 sec)

"root@localhost:mysql.sock  [xx]>

终端1:
"root@localhost:mysql.sock  [xx]> xa commit 'A';  -- xa commit 才是提交分布式事物
Query OK, 0 rows affected (0.01 sec)

终端2:
"root@localhost:mysql.sock  [xx]> select * from test_xa_1;
+----+
| a  |
+----+
| 10 |
+----+
1 row in set (0.00 sec)

解释如下:
XA START xid   # 开启一个分布式事物
XA END xid # 结束一个分布式事物
XA PREPARE xid  # 将分布式事物变成prepare状态
XA COMMIT xid   # 提交一个分布式事物
XA ROLLBACK xid  # 回滚一个分布式事物
XA RECOVER       # 查看分布式事物

备注:个人学习整理,仅用于学习,如有侵权,联系马上删除!

以上是关于MySQL事务-binlog与分布式事务的主要内容,如果未能解决你的问题,请参考以下文章

MySQL binlog 组提交与 XA(分布式事务两阶段提交)

MySQL 分布式事务的“路”与“坑”

MariaDB与MySQL对比 --- 对分布式事务的支持

innodb_support_xa

Mysql分布式事务

深入理解PHP+Mysql分布式事务与解决方案