揭秘MySQL 主从环境中大事务的传奇事迹

2023-10-21 02:20

本文主要是介绍揭秘MySQL 主从环境中大事务的传奇事迹,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

  • 或许某些人会说MySQL Low,而且乐此不疲,不过,我完全也可以说某些人压根就不会用MySQL,万事万物都具有两面性的,最终结果的好坏还得看是谁在用、怎么用。关于这个问题的争论,相信大家都喜闻乐见了,尤其是数据库界的"郭德纲"的言论尤其精彩,没事听听数据库界的"郭德纲"讲讲故事、讲讲段子,也是一件蛮有意思的事情。
  • 说回到大事务,大家或多或少都不太喜欢它,在日常的工作中,或许会在开发规范里明令禁止大事务(操作数据行数过万的事务可以毛估估的算作是大事务),也或许会在开发规范里建议程序员们尽可能将大事务拆分为小事务,即便特殊情况不得不跑大事务,至少也要在会话级别将binlog格式改成statement。大事务对数据库的影响,相信各位都或多或少有些体会。我就不一一列举,在这里,我想说的是,既然大家都觉得跑大事务不好,那大事务在MySQL里跑起来到底长啥样呢?下面我们围绕这个话题,针对"在MySQL主从复制环境中跑一个大事务"的场景,剖析一下大事务在MySQL中的"传奇事迹"

1、环境信息

  • 数据库版本:MySQL 5.7.27
  • 数据库关键配置参数:
    • 主从库:双一、long_query_time=1、binlog_rows_query_log_events=ON、binlog_format=row、slow_query_log=ON、innodb_buffer_pool_size=10G、max_binlog_size=512M
    • 从库:双TABLE、log_slow_slave_statements=ON、slave_parallel_type=LOGICAL_CLOCK、slave_parallel_workers=16、slave_preserve_commit_order=ON、slave_rows_search_algorithms='INDEX_SCAN,HASH_SCAN'、log_slave_updates=ON
  • 主从复制拓扑
    • 主库:10.10.30.162
    • 从库:10.10.30.163
  • 服务器硬件配置(kvm)
    • CPU:8 vcpus
    • 内存:16G
    • 磁盘:100G(LSI 1.6T FLASH卡)
  • 其他工具版本
    • sysbench:sysbench 1.0.9
    • percona-toolkit:percona-toolkit-3.0.13-1.el7.x86_64

2、环境准备

  • 主从库都启用所有的等待事件(这里使用了sys schema下的存储过程ps_setup_enable_instrument和ps_setup_enable_consumer进行快捷操作,代替使用UPDATE语句直接修改performance_schema系统库下的setup_instruments和setup_consumers表)

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

mysql > call sys.ps_setup_enable_instrument('wait');

+-------------------------+

| summary |

+-------------------------+

| Enabled 310 instruments |

+-------------------------+

1 row in set (0.01 sec)

Query OK, 0 rows affected (0.01 sec)

mysql > call sys.ps_setup_enable_consumer('wait');          

+---------------------+

| summary |

+---------------------+

| Enabled 3 consumers |

+---------------------+

1 row in set (0.00 sec)

  • 主库造数2张1000W的表

1

2

3

4

5

6

7

8

9

10

11

# 造数命令

[root@physical-machine ~]# sysbench --db-driver=mysql --time=99999 --threads=2 --report-interval=1 --mysql-host=10.10.30.162 --mysql-port=3306 --mysql-user=qbench --mysql-password=qbench --mysql-db=sbtest --tables=2 --table-size=10000000 oltp_read_write --db-ps-mode=disable prepare

# 表结构

CREATE TABLE `sbtest1` (

  `id` int(11) NOT NULL AUTO_INCREMENT,

  `k` int(11) NOT NULL DEFAULT '0',

  `c` char(120) COLLATE utf8_bin NOT NULL DEFAULT '',

  `pad` varchar(70) COLLATE utf8_bin NOT NULL DEFAULT '',

  PRIMARY KEY (`id`),

  KEY `k_1` (`k`)

) ENGINE=InnoDB AUTO_INCREMENT=20000001 DEFAULT CHARSET=utf8 COLLATE=utf8_bin  # 这里AUTO_INCREMENT=20000001是因为自增步长参数设置为了2,实际上数据只插入了1000W行

  • 主库造数完成之后,查看从库复制状态,确认从库复制无延迟

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

mysql > show slave status\G

*************************** 1. row ***************************

               Slave_IO_State: Waiting for master to send event

                  Master_Host: 10.10.30.162

                  Master_User: repl

                  Master_Port: 3306

                Connect_Retry: 10

              Master_Log_File: mysql-bin.000015

          Read_Master_Log_Pos: 215657137

               Relay_Log_File: mysql-relay-bin.000044

                Relay_Log_Pos: 215657350

        Relay_Master_Log_File: mysql-bin.000015

             Slave_IO_Running: Yes

            Slave_SQL_Running: Yes

......

          Exec_Master_Log_Pos: 215657137

              Relay_Log_Space: 215657644

......

        Seconds_Behind_Master: 0

......

           Retrieved_Gtid_Set: 6f43ada4-d39d-11e9-9c97-5254002a54f2:1-7485

            Executed_Gtid_Set: 6f43ada4-d39d-11e9-9c97-5254002a54f2:1-7485

                Auto_Position: 1

         Replicate_Rewrite_DB: 

                 Channel_Name: 

           Master_TLS_Version: 

1 row in set (0.00 sec)

# 从上述内容中,我们可以通过两种方式判断是否存在复制延迟(仅从SQL线程追赶IO线程的角度衡量,如果是IO线程读取的binlog日志本身与主库存在延迟,在从库侧无法判断,需要结合主从的实际情况进行判断)

## 方法一:比对二进制日志文件和位置信息,Master_Log_File = Relay_Master_Log_File && Read_Master_Log_Pos = Exec_Master_Log_Pos,如果它们完全相等,则说明从库侧无复制延迟

## 方法二:比对GTID SET信息,Retrieved_Gtid_Set = Executed_Gtid_Set,如果它们完全相等,则说明从库侧无复制延迟

  • 主从库各自清理performance_schema统计数据,并切换binlog,避免对后续的操作过程造成干扰

1

2

3

4

5

6

7

8

9

10

11

12

# 清理ps统计数据(sys.ps_truncate_all_tables()函数的作用:清空performance_schema下的%summary%和%history%表,有一个传参,表示是否在每一张表执行清空前打印表名,如果为FALSE,则不打印, 只在执行操作完成最后打印总操作表数量)

mysql > CALL sys.ps_truncate_all_tables(false);

+---------------------+

| summary |

+---------------------+

| Truncated 44 tables |

+---------------------+

1 row in set (0.09 sec)

Query OK, 0 rows affected (0.09 sec)

# 切换binlog

mysql > flush binary logs;

Query OK, 0 rows affected (0.02 sec)

  • 主从库分别执行一个加压辅助脚本,主库并行执行2个UPDATE语句(分别对sbtest1和sbtest2表发起UPDATE操作),从库并行执行2个SELECT语句(分别对sbtest1和sbtest2表发起SELECT操作)(脚本代码详见文末的下载链接)

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

# 主库(该脚本会按照指定的时间间隔循环检查执行时长超过该指定间隔时间的会话状态,如果存在超时的会话,则尝试使用show processlist语句、sys.session视图打印更加详细的会话信息,并尝试使用sys.innodb_lock_waits视图打印锁等待信息;如果不存在超时会话,则并行使用2个会话对指定的表执行UPDATE语句,注意:脚本中的两个UPDATE语句的id取值为id列的一个较小值,不要与后续需要执行的大事务中的id值范围有锁冲突)

[root@physical-machine ~]# sh exec_parallel_sql_master.sh 

## 脚本运行过程中,会按照指定的时间间隔打印一些执行日志,每打印一次就表示UPDATE语句执行成功了一次

now() mark

2019-09-11 13:40:36 ------------------

now() mark

2019-09-11 13:40:37 ------------------

now() mark

2019-09-11 13:40:37 ------------------

......

# 从库(与主库中的exec_parallel_sql_master.sh脚本作用类似,但在从库中,不存在超时会话时,并行运行的语句从UPDATE改为SELECT

[root@node2 ~]# sh exec_parallel_sql_slave.sh 

## 脚本运行过程中,会按照指定的时间间隔打印一些执行日志,以及SELECT语句的查询结果

2019-09-11 13:40:54 ------------------

id k c pad

1000001 4982373 33215995692-11643009243-86512240766-52474552185-05677806687-01251571470-76461373271-04600478707-30936631606-18731704317 91106890267-89662465049-88180742274-99973317521-51514147703

now() mark

2019-09-11 13:40:55 ------------------

id k c pad

2000001 4979176 84112274222-72580329847-55073805313-86104475687-27118798543-60440415134-55566695297-03778364972-89646151895-46966443039 40891678273-04756075401-41893687834-80508607710-52345565443

now() mark

2019-09-11 13:40:55 ------------------

......

  • 另起一个ssh终端会话,在从库运行一个每秒查询复制延迟的脚本(脚本代码详见文末的下载链接)

1

2

3

4

5

6

[root@node2 ~]# sh check_slave_delay.sh 

# 脚本运行过程中,会按照指定的时间间隔打印一些执行日志

当前备库复制延迟(intervel=1,2019-09-11_13:41:31): 0

当前备库复制延迟(intervel=1,2019-09-11_13:41:32): 0

当前备库复制延迟(intervel=1,2019-09-11_13:41:33): 0

......

  • 主从库各自先查看一下慢查询日志、解析binlog,并分析慢查询日志(这里的步骤是为了与后续执行完成大事务后的分析结果做对比,没有对比就没有伤害嘛)

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

# 主从库慢查询日志

## 查看主库慢查询日志文件,可以发现并没有慢查询日志内容

[root@node1 ~]# cat /data//mysqldata1/slowlog/slow-query.log    

/usr/local/mysql/bin/mysqld, Version: 5.7.27-log (MySQL Community Server (GPL)). started with:

Tcp port: 3306 Unix socket: /home/mysql/data/mysqldata1/sock/mysql.sock

Time Id Command Argument

......

[root@node1 ~]# 

## 查看从库慢查询日志文件,可以发现仍然没有慢查询日志内容

[root@node2 ~]# cat /data//mysqldata1/slowlog/slow-query.log 

/usr/local/mysql/bin/mysqld, Version: 5.7.27-log (MySQL Community Server (GPL)). started with:

Tcp port: 3306 Unix socket: /home/mysql/data/mysqldata1/sock/mysql.sock

Time Id Command Argument

......

[root@node2 ~]# 

# 主从库解析binlog,并分析慢查询日志

## 主库解析最后一个binlog,并使用pt-query-digest工具进行慢查询日志分析

[root@node1 binlog]# mysqlbinlog -vv mysql-bin.000023 > a.sql

[root@node1 binlog]# pt-query-digest --type=binlog a.sql

...... 解析结果详见下图1

## 从库解析最后一个binlog,并使用pt-query-digest工具进行慢查询日志分析

[root@node2 binlog]# mysqlbinlog -vv mysql-bin.000023 > b.sql

[root@node2 binlog]# pt-query-digest --type=binlog b.sql

......解析结果详见下图2

  • pt-query_digest 解析结果截图
    • 图1(主库)

    • 图2(从库)

  • 关于主从库binlog解析中,exec_time大于0秒的事务比对信息如下,通过这些信息可以看到,在从库回放binlog时,事务的执行时间被拉长了(exec_time这个属性的统计时间,是从每个事务修改的第一行记录开始计时,直到事务提交完成为止,期间,如果事务存在锁等待,则锁等待的时间也会被一并计算在内。但是要注意,在一个事务包含多个SQL语句时,或者中间夹杂着一些操作间隔时间,例如sleep函数等,那么这个值就不准确,但在我们的测试案例中,一个事务只修改一行记录,所以这个时间在这里是准确的,因此在这里可以认为该值为这个事务真正执行的总时间)

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

# 主库(从这里可以看到,exec_time>0的记录为1行,所以主库的binlog分析结果中,第一个语句也是唯一的一个语句的Exec time行的Total列是1S)

[root@node1 binlog]# cat a.sql |grep exec_time |grep -v 'exec_time=0'

#190911 13:52:26 server id 33061 end_log_pos 1010517 CRC32 0x9481512e Query thread_id=96524 exec_time=1 error_code=0

# 从库(从这里可以看到,exec_time>0的有10行记录,每行都是1,所以从库的binlog分析结果中,第一个语句也是唯一的一个语句的Exec time行的Total列是10S)

[root@node2 binlog]# cat b.sql |grep exec_time |grep -v 'exec_time=0' 

#190911 13:42:44 server id 33061 end_log_pos 181363 CRC32 0x0be0bb2f Query thread_id=88584 exec_time=1 error_code=0

#190911 13:42:45 server id 33061 end_log_pos 182787 CRC32 0x86386f64 Query thread_id=88587 exec_time=1 error_code=0

#190911 13:44:26 server id 33061 end_log_pos 323911 CRC32 0x258368bc Query thread_id=89320 exec_time=1 error_code=0

#190911 13:46:16 server id 33061 end_log_pos 477703 CRC32 0xb91bf3e6 Query thread_id=90964 exec_time=1 error_code=0

#190911 13:47:04 server id 33061 end_log_pos 545343 CRC32 0x8dae4390 Query thread_id=91691 exec_time=1 error_code=0

#190911 13:48:53 server id 33061 end_log_pos 697711 CRC32 0xcbe1b667 Query thread_id=93332 exec_time=1 error_code=0

#190911 13:48:54 server id 33061 end_log_pos 698423 CRC32 0x44114126 Query thread_id=93338 exec_time=1 error_code=0

#190911 13:50:41 server id 33061 end_log_pos 847943 CRC32 0x27c5a588 Query thread_id=94939 exec_time=1 error_code=0

#190911 13:52:26 server id 33061 end_log_pos 995327 CRC32 0x3307103c Query thread_id=96524 exec_time=1 error_code=0

#190911 13:53:19 server id 33061 end_log_pos 1069375 CRC32 0x0540667c Query thread_id=97310 exec_time=1 error_code=0

  • 现在,准备工作已经就绪,在主从库中执行切换binlog,准备入场了

1

mysql > flush binary logs;

3、执行大事务

  • 首先,在主从库中,各自执行一个按照指定的时间间隔循环打印数据库redo log、binlog、ibdata、undo、表数据文件写盘的吞吐统计脚本(脚本代码详见文末的下载链接)

1

2

3

4

5

# 该脚本的作用是,按照指定间隔时间,从sys.x$io_global_by_file_by_bytes视图中抓取文件IO吞吐数据,该视图中的数据基本可以认为是实时数据(详情可查看微信公众号"沃趣技术"中的sys schema系列文章)。注意,针对主库运行的脚本中的mysql_ip参数修改为主库IP,针对从库运行的脚本中mysql_ip参数修改为从库IP

## 主库(执行打印结果详见下图3)

[root@node1 ~]# sh count_mysql_disk_file_io.sh

## 从库(执行打印结果详见下图4)

[root@node2 ~]# sh count_mysql_disk_file_io.sh

  • count_mysql_disk_file_io.sh脚本打印截图如下
    • 图3(主库)

    • 图4(从库)

  • 从图3和图4的比对结果来看,主从库中都频繁抓取到redo log和binlog有0~30KB范围内的吞吐,说明此时,对于主库中的UPDATE事务产生的数据文件的吞吐量,在主从库中是一致的
  • 现在,登录到主从数据库中,各自使用sys.io_global_by_file_by_bytes视图查询当前主从库中相关的数据文件总吞吐统计值,以便后续大事务执行完成之后,再次抓取该统计值进行对比

1

2

3

4

5

6

# 主库(执行结果详见下图5)

mysql > select from sys.io_global_by_file_by_bytes where count_read!=0 order by count_read desc;

......

# 从库(执行结果详见下图6)

mysql > select from sys.io_global_by_file_by_bytes where count_read!=0 order by count_read desc;

......

  • sys.io_global_by_file_by_bytes视图的执行结果截图如下
    • 图5(主库)

    • 图6(从库)

  • 从图5和图6的对比数据可以看到,主库的写流量主要在binlog上,而且读流量在binlog上也有几兆;从库的写流量主要在relay log上,读流量也主要在relay log;另外,proc.MYD文件的读流量主要是因为相关的辅助脚本持续调用相关的查询视图产生的。当然,这里的数据可能不准确(例如:从库中relay log在重放完成之后,如果启用了purge_relay_log参数,会被自动清理,在这个视图中也就看不到被清理的relay log文件相关的统计信息了),但不要紧,这里仅仅只是让我们知晓一个流量的量级,等到后续大事务执行完成之后,再次查询该视图中的数据,你将会发现大事务执行前后相关的数据文件在读写流量上存在量级上的差异
  • 现在,登录到主库中,执行修改数据量级超过400W行的UPDATE语句(给定id值范围不要与exec_parallel_sql_master.sh脚本中给定的id值发生锁冲突),这里我们以操作sbtest1表为例执行大事务

1

2

3

4

5

6

7

8

9

10

11

12

13

mysql > use sbtest

Database changed

# 注意,这里不要加limit,会干扰exec_parallel_sql_master.sh和exec_parallel_sql_slave.sh脚本中的判断逻辑(通过上述环境信息可知,我们造数为1000W,且自增不长设置为2,所以,id>10000000大概是500W行的数据量级别)

mysql > explain update sbtest1 set k=k+1 where id>10000000;

+----+-------------+---------+------------+-------+---------------+---------+---------+-------+---------+----------+-------------+

| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |

+----+-------------+---------+------------+-------+---------------+---------+---------+-------+---------+----------+-------------+

| 1 | UPDATE | sbtest1 | NULL | range | PRIMARY PRIMARY | 4 | const | 4938195 | 100.00 | Using where |

+----+-------------+---------+------------+-------+---------------+---------+---------+-------+---------+----------+-------------+

1 row in set (0.00 sec)

# 使用显式的事务执行该语句

mysql > beginupdate sbtest1 set k=k+1 where id>10000000;commit;

......

  • 在主库大事务未执行完成之前,可在主库反复执行show processlist查看线程状态信息(该步骤不是必须的)

1

2

3

4

5

6

7

8

9

10

mysql > show processlist;

+--------+------+--------------------+--------+------------------+-------+---------------+--------------------------------------------+

| Id | User | Host | db | Command | Time | State | Info |

+--------+------+--------------------+--------+------------------+-------+----------------+--------------------------------------------+

| 4 | repl | 10.10.30.163:33375 | NULL | Binlog Dump GTID | 80999 | Master has sent all binlog to slave; waiting for more updates | NULL |

| 5 | repl | 10.10.30.164:49847 | NULL | Binlog Dump GTID | 80998 | Master has sent all binlog to slave; waiting for more updates | NULL |

| 88201 | root | localhost | sbtest | Query | 19 | updating | update sbtest1 set k=k+1 where id>10000000 |

| 107409 | root | localhost | NULL | Query | 0 | starting | show processlist |

+--------+------+--------------------+--------+------------------+-------+-----------------+--------------------------------------------+

rows in set (0.00 sec)

  • 查看主库中数据文件的流量吞吐情况(该信息为count_mysql_disk_file_io.sh脚本的打印日志),从下图中可以发现,事务未执行完成之前,除了binlog之外,表空间文件、共享表空间、undo、redo的磁盘流量都有不同程度的大幅增加,说明事务未提交时,这几种文件都会写盘且,binlog不写盘(以往没少听说过“事务提交之后,脏页在后台刷新”这种话吧?嘿,这话听起来挺有迷惑性的。事务提交之后,后台线程的确会做刷脏的事情,不过,这句话可没说事务未提交之前后台线程不刷脏。这样设计显然是合理的,否则大事务产生的脏页总量超过内存时,事务就无法执行了)

  • 持续观察主库中的语句执行进度,直到执行完成(这一步可千万不要中断了,否则上述一系列步骤就要重新来过了)

1

2

3

4

5

mysql > beginupdate sbtest1 set k=k+1 where id>10000000;commit;

Query OK, 0 rows affected (0.00 sec)  # 这里是begin语句执行的时间,为0.00 sec

Query OK, 5000000 rows affected (2 min 58.55 sec)  # 这里可以看到UPDATE语句本身执行了2 min 58.55 sec

Rows matched: 5000000 Changed: 5000000 Warnings: 0

Query OK, 0 rows affected (5.01 sec) # 从这里可以看到,这里是UPDATE语句后边的commit语句,执行了5.01 sec

  • 待到主库大事务执行完成之后,迅速观察从库的复制延迟情况,并查看下show processlist;

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

# 查看从库复制延迟

mysql > show slave status\G

*************************** 1. row ***************************

               Slave_IO_State: Waiting for master to send event

                  Master_Host: 10.10.30.162

                  Master_User: repl

                  Master_Port: 3306

                Connect_Retry: 10

              Master_Log_File: mysql-bin.000025

          Read_Master_Log_Pos: 139733

               Relay_Log_File: mysql-relay-bin.000064

                Relay_Log_Pos: 642431

        Relay_Master_Log_File: mysql-bin.000024

             Slave_IO_Running: Yes

            Slave_SQL_Running: Yes

......

          Exec_Master_Log_Pos: 642218

              Relay_Log_Space: 1909355863

......

        Seconds_Behind_Master: 101 # 从这里可以看到,这个延迟数值已经有126秒了(虽然不准确,但是这个量级的延迟数字已经足够说明复制存在延迟了)

......

           Retrieved_Gtid_Set: 6f43ada4-d39d-11e9-9c97-5254002a54f2:1-27419

            Executed_Gtid_Set: 6a3227ed-d39d-11e9-a7b0-525400c33752:1-2,

6f43ada4-d39d-11e9-9c97-5254002a54f2:1-27224

                Auto_Position: 1

         Replicate_Rewrite_DB:

                 Channel_Name:

           Master_TLS_Version:

1 row in set (0.00 sec)

# 查看从库线程状态

mysql > show processlist;

+--------+-------------+-----------+------+---------+-------+----------------+--------------------------------------------+

| Id | User | Host | db | Command | Time | State | Info |

+--------+-------------+-----------+------+---------+-------+---------------+--------------------------------------------+

| 5 | system user | | NULL Connect | 81296 | Waiting for master to send event | NULL |

| 6 | system user | | NULL Connect | 0 | Reading event from the relay log | NULL |

# 大事务的SQL语句在这里(注意,row格式下数据回放的是数据的逐行变更日志,并不是SQL语句文本,这里看到的SQL语句文本来自binlog_rows_query_log_events=ON参数记录的注释性的原始SQL文本,在从库回放主库binlog时会当做注释用途展示在这里)

| 7 | system user | | NULL Connect | 316 | Executing event | update sbtest1 set k=k+1 where id>10000000 |

| 8 | system user | | NULL Connect | 10270 | Waiting for an event from Coordinator | NULL |

| 9 | system user | | NULL Connect | 81296 | Waiting for an event from Coordinator | NULL |

| 10 | system user | | NULL Connect | 81296 | Waiting for an event from Coordinator | NULL |

| 11 | system user | | NULL Connect | 81296 | Waiting for an event from Coordinator | NULL |

| 12 | system user | | NULL Connect | 81296 | Waiting for an event from Coordinator | NULL |

| 13 | system user | | NULL Connect | 81296 | Waiting for an event from Coordinator | NULL |

| 14 | system user | | NULL Connect | 81296 | Waiting for an event from Coordinator | NULL |

| 15 | system user | | NULL Connect | 81296 | Waiting for an event from Coordinator | NULL |

| 16 | system user | | NULL Connect | 81296 | Waiting for an event from Coordinator | NULL |

| 17 | system user | | NULL Connect | 81296 | Waiting for an event from Coordinator | NULL |

| 18 | system user | | NULL Connect | 81296 | Waiting for an event from Coordinator | NULL |

| 19 | system user | | NULL Connect | 81296 | Waiting for an event from Coordinator | NULL |

| 20 | system user | | NULL Connect | 81296 | Waiting for an event from Coordinator | NULL |

| 21 | system user | | NULL Connect | 81296 | Waiting for an event from Coordinator | NULL |

| 22 | system user | | NULL Connect | 81296 | Waiting for an event from Coordinator | NULL |

| 107297 | root | localhost | NULL | Sleep | 596 | | NULL |

| 127830 | root | localhost | NULL | Query | 0 | starting | show processlist |

+--------+-------------+-----------+------+---------+-------+-----------------+--------------------------------------------+

20 rows in set (0.00 sec)

  • 顺便看一眼从库的数据文件流量探测脚本的情况(这些信息来自脚本count_mysql_disk_file_io.sh的打印日志),从下图中标记的地方,对比下主库的数据文件流量打印日志,可以发现它们在量级上是差不多的

  • 关注check_slave_delay.sh脚本的打印情况,当发现复制延迟降为0时,表示大事务已经在从库回放完成了,通过check_slave_delay.sh脚本打印的信息,我们可以非常直观地看到,从07分26秒开始出现5秒延迟,10分54秒延迟降为0,毛估估的算一下,这个大事务在从库中的执行时间为3分28秒(该事务在主库中的执行时长为3分4秒),如下
    • 探测到开始大幅延迟的时间点

    • 探测到延迟结束的时间点

  • 此时,大事务在主从库都已经执行完成,可以将所有辅助脚本停止运行了
  • 上述对于主从库数据文件的流量探测截图中(这里指的是count_mysql_disk_file_io.sh脚本打印日志截图)都没有截取到binlog磁盘流量大幅增加的信息,难道这个大事务没写binlog?非也,因为binlog不是持续写的,在事务持久化的两阶段提交中,事务持久化完成之后,写binlog就结束了,因为人为操作的截图,错过打印的时间点没有截取到,我们打开脚本日志找找看

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

# 主库(从下面找到的信息来看,大事务在主库的提交过程中,5秒左右就将binlog落盘完成了,紧随其后,大约有8秒的时间大量binlog读盘操作,这里是主库通过binlog dump线程将binlog发送给从库的过程。注意,如果sync_binlog不为1,那么在这个时候是否有大量binlog读盘操作不确定,有兴趣的同学自行测试)

[root@node1 ~]# grep -E 'Wed Sep|binlog' /tmp/count_mysql_disk_file_io.sh.log

---------------------------Wed Sep 11 14:07:19 CST 2019-------------------------

binlog 本次写盘差异量:count_read:4,total_read:2.82 KiB,count_write:2,total_written:1.41 KiB

---------------------------Wed Sep 11 14:07:20 CST 2019-------------------------

binlog 本次写盘差异量:count_read:4,total_read:2.82 KiB,count_write:51695,total_written:395.17 MiB

---------------------------Wed Sep 11 14:07:22 CST 2019-------------------------

binlog 本次写盘差异量:count_read:0,total_read:0 bytes,count_write:72236,total_written:552.22 MiB

---------------------------Wed Sep 11 14:07:23 CST 2019-------------------------

binlog 本次写盘差异量:count_read:0,total_read:0 bytes,count_write:63161,total_written:482.84 MiB

---------------------------Wed Sep 11 14:07:24 CST 2019-------------------------

binlog 本次写盘差异量:count_read:0,total_read:0 bytes,count_write:51005,total_written:389.92 MiB

---------------------------Wed Sep 11 14:07:25 CST 2019-------------------------

binlog 本次写盘差异量:count_read:35448,total_read:276.85 MiB,count_write:10,total_written:7.24 KiB

---------------------------Wed Sep 11 14:07:26 CST 2019-------------------------

binlog 本次写盘差异量:count_read:63276,total_read:494.19 MiB,count_write:2,total_written:1.41 KiB

---------------------------Wed Sep 11 14:07:28 CST 2019-------------------------

binlog 本次写盘差异量:count_read:72738,total_read:568.09 MiB,count_write:2,total_written:1.41 KiB

---------------------------Wed Sep 11 14:07:29 CST 2019-------------------------

binlog 本次写盘差异量:count_read:63220,total_read:493.69 MiB,count_write:2,total_written:1.41 KiB

---------------------------Wed Sep 11 14:07:30 CST 2019-------------------------

binlog 本次写盘差异量:count_read:73495,total_read:574.16 MiB,count_write:4,total_written:2.82 KiB

---------------------------Wed Sep 11 14:07:31 CST 2019-------------------------

binlog 本次写盘差异量:count_read:60512,total_read:472.76 MiB,count_write:2,total_written:1.41 KiB

---------------------------Wed Sep 11 14:07:32 CST 2019-------------------------

binlog 本次写盘差异量:count_read:69182,total_read:540.49 MiB,count_write:2,total_written:1.41 KiB

---------------------------Wed Sep 11 14:07:33 CST 2019-------------------------

binlog 本次写盘差异量:count_read:27818,total_read:217.25 MiB,count_write:2,total_written:1.41 KiB

---------------------------Wed Sep 11 14:07:35 CST 2019-------------------------

binlog 本次写盘差异量:count_read:4,total_read:2.82 KiB,count_write:2,total_written:1.41 KiB

# 从库(从下面找到的信息来看,大事务在从库的提交过程中,6秒左右就将binlog落盘完成了。注意,这里指的是从库将大事务回放完成之后,写入自己的binlog文件的流量,而不是从库拉取主库binlog写relay log的流量)

[root@node2 ~]# grep -E 'Wed Sep|binlog' /tmp/count_mysql_disk_file_io.sh.log

......

---------------------------Wed Sep 11 14:10:48 CST 2019-------------------------

binlog 本次写盘差异量:count_read:0,total_read:0 bytes,count_write:0,total_written:0 bytes

---------------------------Wed Sep 11 14:10:49 CST 2019-------------------------

binlog 本次写盘差异量:count_read:0,total_read:0 bytes,count_write:56748,total_written:433.82 MiB

---------------------------Wed Sep 11 14:10:50 CST 2019-------------------------

binlog 本次写盘差异量:count_read:0,total_read:0 bytes,count_write:76567,total_written:585.33 MiB

---------------------------Wed Sep 11 14:10:51 CST 2019-------------------------

binlog 本次写盘差异量:count_read:0,total_read:0 bytes,count_write:63901,total_written:488.50 MiB

---------------------------Wed Sep 11 14:10:52 CST 2019-------------------------

binlog 本次写盘差异量:count_read:0,total_read:0 bytes,count_write:40881,total_written:312.51 MiB

---------------------------Wed Sep 11 14:10:54 CST 2019-------------------------

binlog 本次写盘差异量:count_read:0,total_read:0 bytes,count_write:382,total_written:263.80 KiB

---------------------------Wed Sep 11 14:10:55 CST 2019-------------------------

binlog 本次写盘差异量:count_read:0,total_read:0 bytes,count_write:38,total_written:26.42 KiB

---------------------------Wed Sep 11 14:10:56 CST 2019-------------------------

binlog 本次写盘差异量:count_read:0,total_read:0 bytes,count_write:2,total_written:1.39 KiB

......

  • 上述代码段可能不便阅读,这里将上述内容截图列出
    • 图7(主库)

    • 图8(从库)

4、分析大事务的影响

  • 首先在主从库分别查看慢查询日志文件

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

# 主库(发现大事务语句已经被记录在案了)

[root@node1 ~]# cat /data/mysqldata1/slowlog/slow-query.log

/usr/local/mysql/bin/mysqld, Version: 5.7.27-log (MySQL Community Server (GPL)). started with:

Tcp port: 3306 Unix socket: /home/mysql/data/mysqldata1/sock/mysql.sock

Time Id Command Argument

......

Time: 2019-09-11T14:07:20.291512+08:00

User@Host: root[root] @ localhost [] Id: 88201

# Query_time: 178.546835 Lock_time: 0.000057 Rows_sent: 0 Rows_examined: 5000000

use sbtest;

SET timestamp=1568182040;

update sbtest1 set k=k+1 where id>10000000;

Time: 2019-09-11T14:07:25.307290+08:00

User@Host: admin[admin] @ [10.10.30.16] Id: 109940

# Query_time: 4.375712 Lock_time: 0.000000 Rows_sent: 0 Rows_examined: 0

SET timestamp=1568182045;

commit;

# 从库(咦,在从库的慢日志中,大事务逃脱了?嗯,看起来是的,因为咋们的复现环境是使用的row格式复制,回放过程中有效的日志记录只有数据的逐行变更记录,并不包含语句文本,不会完整地经过SQL层的一些流程,也就无法记录到慢查询日志中)

[root@node2 ~]# cat /data//mysqldata1/slowlog/slow-query.log

/usr/local/mysql/bin/mysqld, Version: 5.7.27-log (MySQL Community Server (GPL)). started with:

Tcp port: 3306 Unix socket: /home/mysql/data/mysqldata1/sock/mysql.sock

Time Id Command Argument

......

  • 顺便用pt-query-digest工具解析下主库的慢查询日志,截个图列出
    • 图9(主库)

    • 图10(从库)

  • 现在,在主从库分别解析binlog日志查看,并使用pt-query-digest工具分析binlog

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

# 主库(解析最后2个binlog,为什么要解析最后2个binlog呢,因为大事务一定不在倒数第一个binlog中,大事务执行完成之后,binlog尺寸会因为超过了max_binlog_size设置的大小,而立即进行日志切换,当然,如果大事务执行完成之后,后续没有新的事务进来,binlog就不一定会立即进行切换了)

[root@node1 binlog]# ls -lh

total 13G

......

-rw-r----- 1 mysql mysql 1.8G Sep 11 14:07 mysql-bin.000024

-rw-r----- 1 mysql mysql 449K Sep 11 14:12 mysql-bin.000025

-rw-r----- 1 mysql mysql 1.3K Sep 11 14:07 mysql-bin.index

[root@node1 binlog]# mysqlbinlog mysql-bin.000024 mysql-bin.000025 > b.sql

[root@node1 binlog]# ls -lh b.sql

-rw-r--r-- 1 root root 2.5G Sep 11 15:38 b.sql

[root@node1 binlog]# pt-query-digest --type=binlog b.sql

# 大事务的解析结果比较大,使用pt-query-digest工具解析会发生(core dumped),pt-query-digest工具解析时不需要用到BINLOG编码,所以,这里可以将binlog中不需要的BINLOG编码过滤掉再进行解析

[root@node1 binlog]# grep -E '\#|/\*|SET|BEGIN|COMMIT|DELIMITER' b.sql > b.txt

[root@node1 binlog]# ls -lh b.txt

-rw-r--r-- 1 root root 28M Sep 11 16:52 b.txt

[root@node1 binlog]# pt-query-digest --type=binlog b.txt

......  # 解析结果详见图11

# 从库

[root@node2 binlog]# ls -lh

total 13G

......

-rw-r----- 1 mysql mysql 1.8G Sep 11 14:10 mysql-bin.000024

-rw-r----- 1 mysql mysql 442K Sep 11 14:12 mysql-bin.000025

[root@node2 binlog]# mysqlbinlog mysql-bin.000024 mysql-bin.000025 > c.sql

[root@node2 binlog]# ls -lh c.sql

-rw-r--r-- 1 root root 2.5G Sep 11 15:42 c.sql

[root@node2 binlog]# grep -E '\#|/\*|SET|BEGIN|COMMIT|DELIMITER' c.sql > c.txt

[root@node2 binlog]# pt-query-digest --type=binlog c.txt

...... # 解析结果详见图12

  • pt-query_digest 解析结果截图
    • 图11(主库)

    • 图12(从库)

  • 从上面两个图的对比结果来看
    • 大事务在主库binlog中逃脱了,因为row格式中有效的binlog记录的是数据的逐行变更数据,而非SQL语句文本,同理,在从库中也应该不会记录该大事务的语句
    • 从库的binlog分析结果中,语句的总执行时间大幅增加。在上文中更早之前我们通过分析binlog已经知道,从库回放会拉长事务的执行时间,但是图12中从库binlog解析记录的SQL总执行时间为43853s,换算成小时,约12个小时,可我们在这里,整个操作过程不到一个小时,很显然,pt-query-digest工具的分析结果中,是将所有BEGIN语句的时间都加在一起了,又因为在测试环境中的从库启用了多线程复制,每个worker线程独立运行,也就是说,假设有N个worker线程同时在回放事务,而每个worker线程同时将执行时间拉长1S,那么,pt-query-digest工具的统计结果中的总延迟时间就变成了N*1S=NS,以此推理可得,大事务在执行过程中,小事务分配binlog event变慢了,又或者被阻塞了,so...
    • 下面,我同样也将exec_time属性不为0的记录过滤出来瞧瞧

1

2

3

4

5

6

7

8

9

10

11

12

13

# 主库(发现主库一共有2行exec_time属性不为0的记录)

[root@node1 binlog]# cat b.txt |grep exec_time |grep -v 'exec_time=0'

#190911 14:07:21 server id 33061 end_log_pos 338 CRC32 0xdd75591e Query thread_id=109957 exec_time=4 error_code=0

#190911 14:07:22 server id 33061 end_log_pos 1061 CRC32 0x9482c46d Query thread_id=109974 exec_time=3 error_code=0

# 从库(发现从库一共有423行exec_time属性不为0的记录)

[root@node2 binlog]# cat c.txt |grep exec_time |grep -v 'exec_time=0'

......

#190911 14:07:21 server id 33061 end_log_pos 367 CRC32 0xe616b934 Query thread_id=109957 exec_time=212 error_code=0

#190911 14:07:22 server id 33061 end_log_pos 1079 CRC32 0x03ca1c89 Query thread_id=109974 exec_time=211 error_code=0

#190911 14:07:25 server id 33061 end_log_pos 1791 CRC32 0xc4e998ea Query thread_id=110012 exec_time=208 error_code=0

......

[root@node2 binlog]# cat c.txt |grep exec_time |grep -v 'exec_time=0' |wc -l

423

  • 现在,主从库分别查看脚本日志(主库中查看exec_parallel_sql_master.sh脚本的执行日志,从库中查看exec_parallel_sql_slave.sh脚本的执行日志),看看主库是否有发生较长时间的写阻塞,从库是否有发生较长时间的读阻塞(注:以下代码段中为部分日志,完整日志请从文末的下载链接中获取)

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

# 主库(发现了SQL被阻塞的记录,从下面show processlist输出结果中可以看到,两个update语句只修改一行记录,原本是不会被阻塞的,但是,现在,因为大事务,发生了阻塞,从后续的sys.session视图这里可以大致判断,其他小事务由于大事务在提交binlog阶段发生了阻塞,甚至导致出现了2个一模一样的UPDATE SQL,因为前一个SQL被阻塞未执行完,后一个完全一样的SQL进入之后甚至导致出现了锁等待)

[root@node1 binlog]# vim /tmp/exec_parallel_sql_master.sh.log

......

==============================================================================================

警告!SQL发生阻塞!!!!!!-----------------------------------------

==============================================================================================

show processlist信息如下:---------------------------------

Id User Host db Command Time State Info

4 repl 10.10.30.163:33375 NULL Binlog Dump GTID 81163 Master has sent all binlog to slave; waiting for more updates NULL

5 repl 10.10.30.164:49847 NULL Binlog Dump GTID 81162 Master has sent all binlog to slave; waiting for more updates NULL

88201 root localhost sbtest Query 4 starting commit

107409 root localhost NULL Sleep 164 NULL

109940 admin 10.10.30.16:62563 sbtest Query 4 starting commit

109957 admin 10.10.30.16:62597 sbtest Query 3 updating update sbtest1 set k=k+1 where id>1000000 limit 1

109974 admin 10.10.30.16:62631 sbtest Query 2 updating update sbtest1 set k=k+1 where id>1000000 limit 1

109991 admin 10.10.30.16:62665 NULL Query 0 starting show processlist

sys.session视图信息如下:---------------------------------

*************************** 1. row ***************************

                thd_id: 109982

               conn_id: 109940

                  user: admin@10.10.30.16

                    db: sbtest

               command: Query

                 state: starting

                  time: 4

     current_statement: commit

     statement_latency: 3.09 s

......

        current_memory: 0 bytes

             last_wait: wait/synch/mutex/sql/MYSQL_BIN_LOG::LOCK_log

     last_wait_latency: Still Waiting

......

                   pid: 24345

          program_name: mysql

*************************** 2. row ***************************

# 锁等待信息

*************************** 1. row ***************************

*************************** 2. row ***************************

                wait_started: 2019-09-11 14:07:22

                    wait_age: 00:00:02

               wait_age_secs: 2

                locked_table: `sbtest`.`sbtest1`

                locked_index: PRIMARY

                 locked_type: RECORD

              waiting_trx_id: 49083

         waiting_trx_started: 2019-09-11 14:07:22

             waiting_trx_age: 00:00:02

     waiting_trx_rows_locked: 2

   waiting_trx_rows_modified: 0

                 waiting_pid: 109974

               waiting_query: update sbtest1 set k=k+1 where id>1000000 limit 1

             waiting_lock_id: 49083:41:6881:62

           waiting_lock_mode: X

             blocking_trx_id: 49082

                blocking_pid: 109957

              blocking_query: update sbtest1 set k=k+1 where id>1000000 limit 1

            blocking_lock_id: 49082:41:6881:62

          blocking_lock_mode: X

        blocking_trx_started: 2019-09-11 14:07:21

            blocking_trx_age: 00:00:03

    blocking_trx_rows_locked: 2

  blocking_trx_rows_modified: 0

     sql_kill_blocking_query: KILL QUERY 109957

sql_kill_blocking_connection: KILL 109957

*************************** 3. row ***************************

......

# 从库(在从库中并未发现SQL阻塞和锁等待记录)

[root@node2 ~]# cat /tmp/exec_parallel_sql_slave.sh.log |grep -E 'sys.session|wait_started'

......

  • 现在,登录到主从数据库中,各自使用sys.io_global_by_file_by_bytes视图查询相关数据文件的总吞吐统计数据

1

2

3

4

5

6

# 主库(见图13)

mysql > select from sys.io_global_by_file_by_bytes where count_read!=0 order by count_read desc;

......

# 从库(见图14)

mysql > select from sys.io_global_by_file_by_bytes where count_read!=0 order by count_read desc;

......

  • sys.io_global_by_file_by_bytes视图的执行结果截图
    • 图13(主库)

    • 图14(从库)

  • 从上述图13和图14中可以看到,吞吐统计数据都毫无悬念地大幅增加了,不过。。主库中出现了一个临时文件/data/mysqldata1/tmpdir/MLrxkcac,读取和写入的总流量都为1.78G,从库中也出现了一个临时文件/data/mysqldata1/tmpdir/MLirV7PX,读取和写入总流量也都为1.78G,额,还真是巧了,这个临时文件是做啥的呢?再仔细一看图13中主库的/data/mysqldata1/binlog/mysql-bin.000024文件的写入总流量也为1.78G,主从库中出现的临时文件会不会跟写binlog有啥关系呢?咋们来推导一下,大事务未提交时,binlog是不能落盘的,那整个事务的binlog日志量往哪里放呢?内存?那万一内存不够用怎么办?那写磁盘吧?往哪儿写呢?临时文件?会不会这里看到的这个临时文件就是用来暂存未提交大事务的binlog的呢?
  • 查看一下磁盘上的binlog文件看看,从侧面证实下推导的结论

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

# 查看binlog目录下的mysql-bin.000024文件

[root@node1 binlog]# ls -lh

total 22G

......

-rw-r----- 1 mysql mysql 1.8G Sep 11 14:07 mysql-bin.000024 # 咦,不对啊,磁盘上这个文件是1.8G,对不上?别慌,我们换个思路,继续往下看

-rw-r----- 1 mysql mysql 449K Sep 11 14:12 mysql-bin.000025

# 会不会是单位算法不一样呢?sys.io_global_by_file_by_bytes视图中的字节流单位是使用sys.format_bytes()函数进行转换的,那我们改用ls -l查看,去掉-h选项查看磁盘文件,把字节数据拿出来,使用数据库中的sys.format_bytes()函数算算看

[root@node1 binlog]# ls -l

total 22186044

......

-rw-r----- 1 mysql mysql 1909214744 Sep 11 14:07 mysql-bin.000024  # 从这里可以看到,字节数据为1909214744

-rw-r----- 1 mysql mysql 459299 Sep 11 14:12 mysql-bin.000025

-rw-r----- 1 mysql mysql 1300 Sep 11 14:07 mysql-bin.index

# 登录数据库算算

mysql > select sys.format_bytes(1909214744);

+------------------------------+

| sys.format_bytes(1909214744) |

+------------------------------+

| 1.78 GiB |  # 从这里可以看到,正好1.78G

+------------------------------+

1 row in set (0.00 sec)

  • PS:对于ML开头的临时文件,事务未执行提交之前,在ps中无法被捕获到,但可以在操作系统层面使用lsof命令查看,如下(看到某个文件的字节数在不断增加的时候,就表示mysqld进程正在不断地使用临时文件存放一些临时数据,但是,当事务提交时,lsof命令捕获到的临时文件条目的字节数会被清零):

5、优化大事务的影响

  • 主库参数优化,添加如下两个优化参数

1

2

3

4

5

6

7

8

9

10

11

12

# 将优化参数持久化

[root@node1 binlog]# cat /etc/my.cnf

......

[mysqld]

binlog_transaction_dependency_tracking=WRITESET

transaction_write_set_extraction=XXHASH64

......

# 也可以动态修改,但是顺序不能搞错,需要先修改transaction_write_set_extraction参数为XXHASH64,否则会报错

mysql > set global transaction_write_set_extraction=XXHASH64;

Query OK, 0 rows affected (0.00 sec)

mysql > set global binlog_transaction_dependency_tracking=WRITESET;

Query OK, 0 rows affected (0.00 sec)

  • 现在,咋们将上述所有步骤重新跑一下,看看大事务是否会阻塞其他的小事务(过程略)
  • 从测试结果来看,很遗憾,没有太大的优化效果
    • 对于主库来说,小事务仍然会在大事务提交binlog阶段发生短暂阻塞(如果磁盘吞吐不太好,那这个问题可大可小了)
    • 对于从库来说,复制延迟仍然存在,但通过pt-query-digest工具解析binlog结果来看,总执行时间减小了一些,但仍然与未优化前处于同一个量级
  • 那是不是说这两个优化参数没啥用呢?不是的,测试结果只能证明,这两个参数的优化效果对大事务场景没太大用处,但对于其他一些特定场景就可能有用。那在什么场景下有用呢?这个话题,咱们日后再聊
  • 现在回过头来继续聊大事务究竟怎么搞呢?当你没有办法去优化某件某个大事务的,最好的办法就是...消灭它,如何消灭大事务呢?回到文章开头那句话:“在开发规范里明令禁止大事务(单个事务操作的数据量不建议超过10000行),尽可能将大事务拆分为小事务,即便特殊情况不得不跑大事务,至少也要在会话级别将binlog格式改成statement。
  • 最后
    • 特别强调一下,对于MySQL InnoDB存储引擎来说,建议设置主键或唯一索引,再不济也要有二级索引,否则,那感觉一定很酸爽,你可以准备两张没有任何索引的表,用上述过程操作一遍试试看!
    • 另外,喜欢用级联复制的同学们注意了,除非必须,否则不建议使用级联复制,以现在的服务器硬件配置来说,使用级联复制带来的好处还不如带来的问题多

6、附录

  • 上文中使用到的相关脚本和相关日志文件下载链接如下
    • 脚本: https://pan.baidu.com/s/11t-WbJns42jO8_s7YW5pDQ
    • 日志: https://pan.baidu.com/s/1SvVhvt59peB1gHuhHqAA0g
  • PS:
    • 关于exec_time属性的详细解释,详情可参考 高鹏(重庆八怪)的《深入理解MySQL主从原理》专栏中的第八讲
    • 关于大事务binlog未落盘之前,是否会写临时文件的详细解释,详情可参考 高鹏(重庆八怪)的《深入理解MySQL主从原理》专栏中的第十三、第十四讲。最后,附一张高清图(该图为第十四讲文末的高清图),留意左侧绿色方框内的内容

| 作者简介

罗小波·沃趣科技高级数据库技术专家

IT从业多年,主要负责MySQL 产品的数据库支撑与售后二线支撑。曾参与版本发布系统、轻量级监控系统、运维管理平台、数据库管理平台的设计与编写,熟悉MySQL体系结构,Innodb存储引擎,喜好专研开源技术,多次在公开场合做过线下线上数据库专题分享,发表过多篇数据库相关的研究文章。

这篇关于揭秘MySQL 主从环境中大事务的传奇事迹的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/251295

相关文章

大模型研发全揭秘:客服工单数据标注的完整攻略

在人工智能(AI)领域,数据标注是模型训练过程中至关重要的一步。无论你是新手还是有经验的从业者,掌握数据标注的技术细节和常见问题的解决方案都能为你的AI项目增添不少价值。在电信运营商的客服系统中,工单数据是客户问题和解决方案的重要记录。通过对这些工单数据进行有效标注,不仅能够帮助提升客服自动化系统的智能化水平,还能优化客户服务流程,提高客户满意度。本文将详细介绍如何在电信运营商客服工单的背景下进行

SQL中的外键约束

外键约束用于表示两张表中的指标连接关系。外键约束的作用主要有以下三点: 1.确保子表中的某个字段(外键)只能引用父表中的有效记录2.主表中的列被删除时,子表中的关联列也会被删除3.主表中的列更新时,子表中的关联元素也会被更新 子表中的元素指向主表 以下是一个外键约束的实例展示

基于MySQL Binlog的Elasticsearch数据同步实践

一、为什么要做 随着马蜂窝的逐渐发展,我们的业务数据越来越多,单纯使用 MySQL 已经不能满足我们的数据查询需求,例如对于商品、订单等数据的多维度检索。 使用 Elasticsearch 存储业务数据可以很好的解决我们业务中的搜索需求。而数据进行异构存储后,随之而来的就是数据同步的问题。 二、现有方法及问题 对于数据同步,我们目前的解决方案是建立数据中间表。把需要检索的业务数据,统一放到一张M

如何去写一手好SQL

MySQL性能 最大数据量 抛开数据量和并发数,谈性能都是耍流氓。MySQL没有限制单表最大记录数,它取决于操作系统对文件大小的限制。 《阿里巴巴Java开发手册》提出单表行数超过500万行或者单表容量超过2GB,才推荐分库分表。性能由综合因素决定,抛开业务复杂度,影响程度依次是硬件配置、MySQL配置、数据表设计、索引优化。500万这个值仅供参考,并非铁律。 博主曾经操作过超过4亿行数据

性能分析之MySQL索引实战案例

文章目录 一、前言二、准备三、MySQL索引优化四、MySQL 索引知识回顾五、总结 一、前言 在上一讲性能工具之 JProfiler 简单登录案例分析实战中已经发现SQL没有建立索引问题,本文将一起从代码层去分析为什么没有建立索引? 开源ERP项目地址:https://gitee.com/jishenghua/JSH_ERP 二、准备 打开IDEA找到登录请求资源路径位置

MySQL数据库宕机,启动不起来,教你一招搞定!

作者介绍:老苏,10余年DBA工作运维经验,擅长Oracle、MySQL、PG、Mongodb数据库运维(如安装迁移,性能优化、故障应急处理等)公众号:老苏畅谈运维欢迎关注本人公众号,更多精彩与您分享。 MySQL数据库宕机,数据页损坏问题,启动不起来,该如何排查和解决,本文将为你说明具体的排查过程。 查看MySQL error日志 查看 MySQL error日志,排查哪个表(表空间

揭秘世界上那些同时横跨两大洲的国家

我们在《世界人口过亿的一级行政区分布》盘点全球是那些人口过亿的一级行政区。 现在我们介绍五个横跨两州的国家,并整理七大洲和这些国家的KML矢量数据分析分享给大家,如果你需要这些数据,请在文末查看领取方式。 世界上横跨两大洲的国家 地球被分为七个大洲分别是亚洲、欧洲、北美洲、南美洲、非洲、大洋洲和南极洲。 七大洲示意图 其中,南极洲是无人居住的大陆,而其他六个大洲则孕育了众多国家和

三国地理揭秘:为何北伐之路如此艰难,为何诸葛亮无法攻克陇右小城?

俗话说:天时不如地利,不是随便说说,诸葛亮六出祁山,连关中陇右的几座小城都攻不下来,行军山高路险,无法携带和建造攻城器械,是最难的,所以在汉中,无论从哪一方进攻,防守方都是一夫当关,万夫莫开;再加上千里运粮,根本不需要打,司马懿只需要坚守城池拼消耗就能不战而屈人之兵。 另一边,洛阳的虎牢关,一旦突破,洛阳就无险可守,这样的进军路线,才是顺势而为的用兵之道。 读历史的时候我们常常看到某一方势

阿里开源语音识别SenseVoiceWindows环境部署

SenseVoice介绍 SenseVoice 专注于高精度多语言语音识别、情感辨识和音频事件检测多语言识别: 采用超过 40 万小时数据训练,支持超过 50 种语言,识别效果上优于 Whisper 模型。富文本识别:具备优秀的情感识别,能够在测试数据上达到和超过目前最佳情感识别模型的效果。支持声音事件检测能力,支持音乐、掌声、笑声、哭声、咳嗽、喷嚏等多种常见人机交互事件进行检测。高效推

MySQL高性能优化规范

前言:      笔者最近上班途中突然想丰富下自己的数据库优化技能。于是在查阅了多篇文章后,总结出了这篇! 数据库命令规范 所有数据库对象名称必须使用小写字母并用下划线分割 所有数据库对象名称禁止使用mysql保留关键字(如果表名中包含关键字查询时,需要将其用单引号括起来) 数据库对象的命名要能做到见名识意,并且最后不要超过32个字符 临时库表必须以tmp_为前缀并以日期为后缀,备份