作者:莫善
某互联网公司高级 DBA。
本文起源:原创投稿
* 爱可生开源社区出品,原创内容未经受权不得随便应用,转载请分割小编并注明起源。
背景
这两天看到一个 MySQL 群里在探讨一个乏味的话题,大家平时都是怎么敞开 MySQL 的,一个大佬还发动了一个投票。投票如下:
你是如何敞开 MySQL 数据库的?
- A、mysqladmin shutdown
- B、service mysqld stop(systemctl)
- C、kill mysqld_pid
- D、kill -9 mysqld_pid
投票后果如下(数据起源 InsideMySQL 公众号):
选项 | 人数 | 占比 |
---|---|---|
A | 141 | 33.9% |
B | 243 | 58.4% |
C | 15 | 3.6% |
D | 17 | 4.1% |
生产环境中根本都是多实例部署,所以用 A 的形式敞开比拟多,偶然也会贪不便间接采纳 C 的敞开形式,如果是单机单实例,用 B 也没故障,然而为什么会有人选 D 选项呢,发动投票的大佬逐个问过后得悉,都是因为过后 MySQL 曾经不可用了,无可奈何才采纳暴力敞开。
最初,大佬最终颁布答案说,生产环境有且只有一种正确的敞开 MySQL 的形式,那就是 D 形式,所以在大佬看来,简直团灭。
对这个颁布后果我是持狐疑态度的,所以我带着狐疑做了上面的测试。
仅对 5.7 的半同步场景做了测试,对于异步场景感觉没什么意义,所以没测。
一、环境介绍
环境架构采纳 MySQL 5.7 加强半同步,搭建的一主一从,信息如下:
角色 | ip | 端口 | 版本 |
---|---|---|---|
master | 192.168.168.11 | 6666 | 5.7.26 |
slave | 192.168.168.12 | 6666 | 5.7.26 |
1、主库配置
mysql> show variables like 'rpl%';
+-------------------------------------------+------------+
| Variable_name | Value |
+-------------------------------------------+------------+
| rpl_semi_sync_master_enabled | ON |
| rpl_semi_sync_master_timeout | 1000000 |
| rpl_semi_sync_master_trace_level | 32 |
| rpl_semi_sync_master_wait_for_slave_count | 1 |
| rpl_semi_sync_master_wait_no_slave | ON |
| rpl_semi_sync_master_wait_point | AFTER_SYNC |
| rpl_semi_sync_slave_enabled | OFF |
| rpl_semi_sync_slave_trace_level | 32 |
| rpl_stop_slave_timeout | 31536000 |
+-------------------------------------------+------------+
9 rows in set (0.00 sec)
mysql>
rpl_semi_sync_master_timeout = 1000000 是为了防止半同步降级为异步。
2、从库配置
mysql> show variables like 'rpl%';
+-------------------------------------------+------------+
| Variable_name | Value |
+-------------------------------------------+------------+
| rpl_semi_sync_master_enabled | OFF |
| rpl_semi_sync_master_timeout | 1000000 |
| rpl_semi_sync_master_trace_level | 32 |
| rpl_semi_sync_master_wait_for_slave_count | 1 |
| rpl_semi_sync_master_wait_no_slave | ON |
| rpl_semi_sync_master_wait_point | AFTER_SYNC |
| rpl_semi_sync_slave_enabled | ON |
| rpl_semi_sync_slave_trace_level | 32 |
| rpl_stop_slave_timeout | 31536000 |
+-------------------------------------------+------------+
9 rows in set (0.00 sec)
mysql>
二、测试演示
- 测试筹备
登录主库筹备测试数据
# /opt/soft/mysql57/bin/mysql -u dba -h 192.168.168.11 -P6666 -p123456
mysql> create database if not exists dbatest;
Query OK, 1 row affected, 1 warning (0.00 sec)
mysql> use dbatest
Database changed
mysql> create table t(id int not null auto_increment primary key,name varchar(10) default '') ;
Query OK, 0 rows affected (0.01 sec)
mysql> insert into t select 0,'mysql';
Query OK, 1 row affected (0.00 sec)
Records: 1 Duplicates: 0 Warnings: 0
mysql> insert into t select 0,'redis';
Query OK, 1 row affected (0.01 sec)
Records: 1 Duplicates: 0 Warnings: 0
mysql> select * from t;
+----+-------+
| id | name |
+----+-------+
| 1 | mysql |
| 2 | redis |
+----+-------+
2 rows in set (0.00 sec)
mysql>
登录从库查看测试数据
# /opt/soft/mysql57/bin/mysql -u dba -h 192.168.168.12 -P6666 -p123456
mysql> use dbatest
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Database changed
mysql> select * from t;
+----+-------+
| id | name |
+----+-------+
| 1 | mysql |
| 2 | redis |
+----+-------+
2 rows in set (0.00 sec)
mysql>
1、平安敞开的场景
(1)停掉从库的复制
# /opt/soft/mysql57/bin/mysql -u dba -h 192.168.168.12 -P6666 -p123456
mysql> stop slave;
Query OK, 0 rows affected (0.01 sec)
mysql>
模仿半同步故障,为了更直观查阅测试后果。
(2)模仿业务更新
这个流程波及两个操作,一是在主库发动一个更新申请,二是查看主库的 processlist 状态。
- 操作一
# /opt/soft/mysql57/bin/mysql -u dba -h 192.168.168.11 -P6666 -p123456 dbatest -e "update t set name ='tidb'where id = 1"
这个操作回车后会卡着,因为这个申请会等从库的 ACK。具体能够看【操作二】的查问后果。
- 操作二
# /opt/soft/mysql57/bin/mysql -u dba -h 192.168.168.11 -P6666 -p123456
mysql> show processlist;
+----+------+----------------------+---------+---------+------+--------------------------------------+-----------------------------------------+
| Id | User | Host | db | Command | Time | State | Info |
+----+------+----------------------+---------+---------+------+--------------------------------------+-----------------------------------------+
| 8 | dba | 192.168.168.13:49584 | dbatest | Query | 49 | Waiting for semi-sync ACK from slave | update t set name = 'tidb' where id = 1 |
| 9 | root | localhost | NULL | Query | 0 | starting | show processlist |
+----+------+----------------------+---------+---------+------+--------------------------------------+-----------------------------------------+
2 rows in set (0.00 sec)
mysql>
(3)敞开主库的 MySQL
这个流程向主库提交 shutdown 命令来平安敞开 MySQL。
# /opt/soft/mysql57/bin/mysql -u dba -h 192.168.168.11 -P6666 -p123456
mysql> shutdown;
Query OK, 0 rows affected (0.00 sec)
mysql>
提交完 shutdown 操作后,【操作一】的更新申请会被提交。
- 在业务端看来,id=1 这行数据曾经被批改,然而从库的 id= 1 这行数据未扭转。如果这个时候触发主从切换,那就丢数据了。由此看来,<font color=’red’> 在非凡场景下,平安敞开 MySQL 可能导致数据失落。</font>
- 这时候将主库从新拉起来,修好主从的半同步,被提交的事务还是会同步到从库的。
2、暴力敞开的场景
(1)停掉从库的复制
# /opt/soft/mysql57/bin/mysql -u dba -h 192.168.168.12 -P6666 -p123456
mysql> stop slave;
Query OK, 0 rows affected (0.01 sec)
mysql>
模仿半同步故障,为了更直观查阅测试后果。
(2)模仿业务更新
这个流程波及两个操作,一是在主库发动一个更新申请,二是查看主库的 processlist 状态。
- 操作一
# /opt/soft/mysql57/bin/mysql -u dba -h 192.168.168.11 -P6666 -p123456 dbatest -e "update t set name ='codis'where id = 2"
这个操作回车后会卡着,因为这个申请会等从库的 ACK。具体能够看【操作二】的查问后果。
- 操作二
# /opt/soft/mysql57/bin/mysql -u dba -h 192.168.168.11 -P6666 -p123456
mysql> show processlist;
+----+------+----------------------+---------+---------+------+--------------------------------------+-----------------------------------------+
| Id | User | Host | db | Command | Time | State | Info |
+----+------+----------------------+---------+---------+------+--------------------------------------+-----------------------------------------+
| 2 | dba | 192.168.168.13:34698 | dbatest | Query | 13 | Waiting for semi-sync ACK from slave | update t set name = 'codis' where id = 2 |
| 3 | root | localhost | NULL | Query | 0 | starting | show processlist |
+----+------+----------------------+---------+---------+------+--------------------------------------+-----------------------------------------+
2 rows in set (0.00 sec)
mysql>
(3)暴力敞开主库的 MySQL
这个操作向主库所在服务器的 MySQL 过程发送 kill -9 信号,来暴力敞开 MySQL。
# ps -ef|grep mysql|grep 6666
root 123194 1 0 15:45 pts/9 00:00:00 /bin/sh /opt/soft/mysql57/bin/mysqld_safe --defaults-file=//work/mysql6666/etc/my6666.cnf
mysql 124539 123194 4 15:45 pts/9 00:00:02 /opt/soft/mysql57/bin/mysqld --defaults-file=//work/mysql6666/etc/my6666.cnf --basedir=/opt/soft/mysql57 --datadir=/work/mysql6666/var --plugin-dir=/opt/soft/mysql57/lib/plugin --user=mysql --log-error=/work/mysql6666/log/mysql.err --open-files-limit=65535 --pid-file=/work/mysql6666/var/mysql.pid --socket=/work/mysql6666/tmp/mysql.sock --port=6666
# kill -9 124539 123194
#
kill -9 操作后,【操作一】所在的会话会被终止,并提醒【ERROR 2013 (HY000) at line 1: Lost connection to MySQL server during query】
- 在业务端看来,id= 2 这行数据没有被批改,所以数据是统一的。
三、停服流程介绍
本大节是通过浏览了 MySQL 官网文档后,简略介绍一下 MySQL 在接管了 SIGINT 信号后会做哪些事件,仅供参考。
该局部文档连贯 https://dev.mysql.com/doc/ref…
1、进行承受新连贯
为了预防在关闭系统过程再接管新工作,MySQL 会敞开 TCP/IP 端口、socket 等通道来阻止承受新的客户端连贯。
2、解决曾经建设的连贯
这个过程会解决曾经建设的连贯,对于闲暇连贯会马上杀掉。对于以后正在解决工作的线程(会定期检查它们的状态),如果是正在执行的未实现的事务,会回滚(非事务的引擎会导致局部失败局部胜利),所以这个过程须要更长的工夫。
针对这个我有一个疑难。在【平安敞开的测试场景】中,正在期待 ACK 的工作会被提交,那么跟这个流程就有出入,所以我猜想这个流程之前会先敞开【期待 ACK 的线程】,而后再解决曾经建设的连贯,这样就能说的通,在【平安敞开的测试场景】中正在期待 ACK 的事务被提交了。当然这个思路仅是通过 MySQL 谬误日志(如下展现)记录得出的猜想,仅供参考。
2022-03-25T17:17:58.165016+08:00 0 [Note] Giving 1 client threads a chance to die gracefully
2022-03-25T17:17:58.165059+08:00 0 [Note] Shutting down slave threads
2022-03-25T17:17:58.165070+08:00 3 [Warning] SEMISYNC: Forced shutdown. Some updates might not be replicated.
2022-03-25T17:17:58.165092+08:00 3 [Note] Semi-sync replication switched OFF.
2022-03-25T17:17:58.165654+08:00 0 [Note] Forcefully disconnecting 0 remaining clients
2022-03-25T17:17:58.165680+08:00 0 [Note] Event Scheduler: Purging the queue. 0 events
2022-03-25T17:17:58.165919+08:00 0 [Note] Binlog end
2022-03-25T17:17:58.167688+08:00 0 [Note] Shutting down plugin 'rpl_semi_sync_slave'
2022-03-25T17:17:58.167719+08:00 0 [Note] Shutting down plugin 'rpl_semi_sync_master'
2022-03-25T17:17:58.167745+08:00 0 [Note] Stopping ack receiver thread
2022-03-25T17:17:58.167831+08:00 0 [Note] unregister_replicator OK
2022-03-25T17:17:58.167843+08:00 0 [Note] Shutting down plugin 'ngram'
省略 shutting down plugin
2022-03-25T17:17:58.167985+08:00 0 [Note] Shutting down plugin 'InnoDB'
2022-03-25T17:17:58.168053+08:00 0 [Note] InnoDB: FTS optimize thread exiting.
2022-03-25T17:17:58.168140+08:00 0 [Note] InnoDB: Starting shutdown...
2022-03-25T17:18:00.704250+08:00 0 [Note] InnoDB: Shutdown completed; log sequence number 63089498676
2022-03-25T17:18:00.705160+08:00 0 [Note] InnoDB: Removed temporary tablespace data file: "ibtmp1"
2022-03-25T17:18:00.705183+08:00 0 [Note] Shutting down plugin 'MEMORY'
省略 shutting down plugin
2022-03-25T17:18:00.705547+08:00 0 [Note] Shutting down plugin 'binlog'
2022-03-25T17:18:00.706904+08:00 0 [Note] /opt/soft/mysql57/bin/mysqld: Shutdown complete
能够看到 shutdown 的整个过程,其中有一个【Stopping ack receiver thread】
3、敞开存储引擎
在这个阶段,服务器会刷新表缓存并敞开所有关上的表。
InnoDB 将其缓冲池刷新到磁盘(除非 innodb_fast_shutdown = 2),将以后 LSN 写入表空间,并终止其本人的外部线程。
4、服务器敞开
过程退出,端口敞开。
四、写在最初
测试后果有点诧异,已经认为危险的操作命令却是平安的,已经认为平安的操作命令反而会导致数据异样。即便如此,我感觉也不必太过纠结了,毕竟丢数据的场景还是很刻薄的。
最初须要揭示一下,在 5.7 版本以前,要慎用 kill -9。<font color=’red’> 生产环境十分复杂,请时刻放弃敬畏之心,任何操作请充沛测试。</font>