//
线上mysql复制错误排查一例
//
今天在线上遇到一个MySQL复制报错问题,简单记录了一下排查的过程。
01
问题描述
线上MySQL主从架构在正常运行的过程中,突然遇到了下面的报错:
localhost.(none)>show slave statusG *************************** 1. row *************************** Slave_IO_State: Master_Host: 10.xx.xx.xx Master_User: replica Master_Port: 6054 Connect_Retry: 60 Master_Log_File: mysql-bin.021323 Read_Master_Log_Pos: 728933261 Relay_Log_File: relay-bin.036006 Relay_Log_Pos: 227988073 Relay_Master_Log_File: mysql-bin.021317 Slave_IO_Running: No Slave_SQL_Running: No Last_Errno: 1026 Last_Error: Could not execute Update_rows event on table user.mole_user_growth_log; Error writing file '/dev/shm/MLoiYLtT' (Errcode: 28 - No space left on device), Error_code: 3; Error writing file '/dev/shm/MLoiYLtT' (errno: 28 - No space left on device), Error_code: 1026; handler error HA_ERR_RBR_LOGGING_FAILED; the event's master log mysql-bin.021317, end_log_pos 295118997
报错信息中不难看出来,是写入数据到/dev/shm这个目录的时候报错。报错信息也很明显,No space left on device;没有额外的空余磁盘空间。
报错的偏移量是:mysql-bin.021317,295118997
02
排查思路
1、为什么是这个路径?
那么MySQL在运行的时候,为什么会用到这个路径呢?可能是一些临时文件造成的,于是查了查tmp相关的变量,如下:
localhost.(none)>show variables like "%tmp%"; +----------------------------------+-----------+ | Variable_name | Value | +----------------------------------+-----------+ | default_tmp_storage_engine | InnoDB | | innodb_tmpdir | | | internal_tmp_disk_storage_engine | InnoDB | | max_tmp_tables | 256 | | slave_load_tmpdir | /tmp | | tmp_table_size | 134217728 | | tmpdir | /dev/shm | +----------------------------------+-----------+ 7 rows in set (0.00 sec)
可以看到,tmpdir这个变量的路径指的是/dev/shm这个目录。
2、目录查看
接着看这个目录的磁盘情况,使用df -h /dev/shm命令查看当前目录的磁盘使用情况,如下:
[root@bx-10-13-32-125 mysql]# df -h /dev/shm Filesystem Size Used Avail Use% Mounted on shm 64M 0 64M 0% /dev/shm
可以看到,这个磁盘的剩余空间还有64MB,目前还不满,但是报错信息是数据写不进去了。
3、查看报错的事务
通过mysqlbinlog解析对应的binlog,可以发现binlog中对应的偏移量点位,发现当前点位位于一个update语句的事务当中,而这个事务发生了回滚,该事务的大小,在binlog中有69MB.(通过事务的binlog position点位相减计算得出),计算方法大致如下:
事务开始的位置: 227988083 事务最后的位置: 301232763 计算值的大小: localhost.(none)>select 301232763-227988083; +---------------------+ | 301232763-227988083 | +---------------------+ | 73244680 | +---------------------+ 1 row in set (0.00 sec) localhost.(none)>select 73244680/1024/1024; +--------------------+ | 73244680/1024/1024 | +--------------------+ | 69.85157013 | +--------------------+ 1 row in set (0.00 sec)
到这里,问题就比较明显了,应该是这个update事务产生的中间临时文件超过了64MB,导致当前的路径下的磁盘容量不足,所以产生报错。
这个从库的实例是通过k8s调度docker容器启动的,发生了报错;这个端口还有从库实例跑在物理机上,物理机上的从库没有发现相同的报错信息。
这让人感到奇怪,但是直观猜测,物理机和容器中的/dev/shm路径肯定存在某种不同,查看了当前路径在物理机和容器中的磁盘情况,对比如下:
[root@ mysql]# df -h /dev/shm Filesystem Size Used Avail Use% Mounted on shm 64M 0 64M 0% /dev/shm [root@ mysql]# exit exit [root@ /]# df -h /dev/shm Filesystem Size Used Avail Use% Mounted on tmpfs 63G 12K 63G 1% /dev/shm
通过上面的对比,不难看出,当前目录在物理机上的容量是63G,而容器中的大小是64M,所以容器中出现了报错。
到这里,问题就可以解决了。
4、解决方案
解决这个问题的方法有两个:
1、修改tmpdir参数指向的路径,可以修改成datadir的路径,毕竟,数据目录一般比较大,肯定可以容纳临时文件。注意这个参数是个只读的,不能动态修改:
localhost.(none)>set global tmpdir='/tmp'; ERROR 1238 (HY000): Variable 'tmpdir' is a read only variable
手工修改配置文件,并重启实例即可解决。
2、直接修改容器启动为物理机启动,使用物理机的/dev/shm目录,63G的容量,肯定够用。
处理方法:
修改权限
[root@ /data1]# ll drwxr-xr-x 13 1001 1000 36864 May 7 14:51 mysql6054 [root@ /data1]# chown -R my6054.mysql mysql6054 [root@ /data1]# ll drwxr-xr-x 13 my6054 mysql 36864 May 7 14:51 mysql6054
重新启动:
[root@ /data1]# /usr/local/mysql-5.7.24/bin/mysqld_safe --defaults-file=/data1/mysql6054/my6054.cnf & [1] 18992 localhost.(none)>show slave statusG *************************** 1. row *************************** Slave_IO_State: Waiting for master to send event Master_Host: 10.41.14.80 Master_User: replica Master_Port: 6054 Connect_Retry: 60 Master_Log_File: mysql-bin.021323 Read_Master_Log_Pos: 749382634 Relay_Log_File: relay-bin.036006 Relay_Log_Pos: 310712974 Relay_Master_Log_File: mysql-bin.021317 Slave_IO_Running: Yes Slave_SQL_Running: Yes
有帮助的话还希望点下再看哈