写点什么

在业务高峰期拔掉服务器电源是一种怎样的体验?

用户头像
冰河
关注
发布于: 2021 年 04 月 09 日
在业务高峰期拔掉服务器电源是一种怎样的体验?

写在前面

当时的我在外面玩的正起劲,突然一个电话打来:“冰河,你在哪?服务器突然不能访问了!”。我:“又有什么情况啊?”。“我不小心踩到服务器电源连接的插线板了,服务器断电了,重启时提示无法启动”。我心里一万个无语,问:”是哪台服务器断电了“。“我不小心踩到那个小插线板的开关了,连接到这个插线板的服务器都断电了”。我:尼玛,那是数据库啊,我去。。。


于是我赶紧飞奔回公司,开始了苦逼的数据恢复过程。。。


文章已收录到:


https://github.com/sunshinelyz/technology-binghe


https://gitee.com/binghe001/technology-binghe

解决主库问题

主库问题重现

回到公司一看,断电的是公司的消息服务子系统数据库,数据库共 3 台,一种两从,并采用了分库分表的方式存储数据。我首先把三台服务器启动好,发现主数据库的进程无法启动,两台从数据库同步主库数据的状态异常。按照顺序,我先看主数据库的日志信息,发现 MySQL 的错误日志中输出了如下信息。


-----------------------------------------161108 23:36:45 mysqld_safe Starting mysqld daemon with databases from /usr/local/mysql/var2021-02-28 23:36:46 0 [Warning] TIMESTAMP with implicit DEFAULT value is deprecated. Please use --explicit_defaults_for_timestamp server option (see documentation for more details).2021-02-28 23:36:46 5497 [Note] Plugin 'FEDERATED' is disabled.2021-02-28 23:36:46 
7f11c48e1720 InnoDB: Warning: Using innodb_additional_mem_pool_size is DEPRECATED. This option may be removed in future releases, together with the option innodb_use_sys_malloc and with the InnoDB's internal memory allocator.2021-02-28 23:36:46 5497 [Note] InnoDB: Using atomics to ref count buffer pool pages2021-02-28 23:36:46 5497 [Note] InnoDB: The InnoDB memory heap is disabled2021-02-28 23:36:46 5497 [Note] InnoDB: Mutexes and rw_locks use GCC atomic builtins2021-02-28 23:36:46 5497 [Note] InnoDB: Memory barrier is not used2021-02-28 23:36:46 5497 [Note] InnoDB: Compressed tables
use zlib 1.2.32021-02-28 23:36:46 5497 [Note] InnoDB: Using CPU crc32 instructions2021-02-28 23:36:46 5497 [Note] InnoDB: Initializing buffer pool, size = 16.0M2021-02-28 23:36:46 5497 [Note] InnoDB: Completed initialization of buffer poolInnoDB: Database page corruption on disk or a failedInnoDB: file read of page 5.InnoDB: You may have to recover from a backup.2021-02-28 23:36:46 7f11c48e1720 InnoDB: Page dump in ascii and hex (16384 bytes): len 16384; hex 7478d078000000050000000000000000000000000f271f4d000700000000000000000000000000000000001b4000000000000000000200f20000000000000006000000000000002d000000000000002e000000000000002f0000000000000030000000000(省略很多类似代码)InnoDB: End of page dump2021-02-28 23:36:46 7f11c48e1720 InnoDB: uncompressed page, stored checksum in field1 1954074744, calculated checksums for field1: crc32 993334256, innodb 2046145943, none 3735928559, stored checksum in field2 1139795846, calculated checksums for field2:
crc32 993334256, innodb 1606613742, none 3735928559, page LSN 0 254222157, low 4 bytes of LSN at page end 254221236, page number (if stored to page already) 5, space id (if created with >= MySQL-4.1.1 and stored already) 0InnoDB: Page may be a transaction system pageInnoDB: Database page corruption on disk or a failedInnoDB: file read of page 5.InnoDB: You may have to recover from a backup.InnoDB: It is also possible that your operatingInnoDB: system has corrupted its own file cacheInnoDB: and rebooting your computer removes theInnoDB: error.InnoDB: If the corrupt page is an index pageInnoDB: you can also try to fix the corruptionInnoDB: by dumping, dropping, and reimportingInnoDB: the corrupt table. You can use CHECKInnoDB: TABLE to scan your table for corruption.InnoDB: See also http://dev.mysql.com/doc/refman/5.6/en/forcing-innodb-recovery.htmlInnoDB: about forcing recovery.InnoDB: Ending processing because of a corrupt database page.2021-02-28 23:36:46 7f11c48e1720 InnoDB: Assertion failure in thread 139714288817952 in file buf0buf.cc line 4201InnoDB: We intentionally generate a memory trap.InnoDB: Submit a detailed bug report to http://bugs.mysql.com.InnoDB: If you get repeated assertion failures or crashes, evenInnoDB:
immediately after the mysqld startup, there may beInnoDB: corruption in the InnoDB tablespace. Please refer toInnoDB: http://dev.mysql.com/doc/refman/5.6/en/forcing-innodb-recovery.htmlInnoDB: about forcing recovery.03:36:46 UTC - mysqld got signal 6 ;This could be because you hit a bug. It is also possible that this binaryor one of the libraries it was linked against is corrupt, improperly built,or misconfigured. This error can also be caused by malfunctioning hardware.We will try our best to scrape up some info that will hopefully helpdiagnose the problem, but since we have already crashed,something is definitely wrong and this may fail.key_buffer_size=16777216read_buffer_size=262144max_used_connections=0max_threads=1000thread_count=0connection_count=0It is possible that mysqld could use up tokey_buffer_size + (read_buffer_size + sort_buffer_size)*max_threads = 798063 K bytes of memoryHope that's ok; if not, decrease some variables in the equation.Thread pointer: 0x0Attempting backtrace. You can use the following information to find outwhere mysqld died. If you see no messages after this, something wentterribly wrong...stack_bottom = 0 thread_stack 0x40000/usr/local/mysql/bin/mysqld(my_print_stacktrace+0x35)[0x8e64b5]/usr/local/mysql/bin/mysqld(handle_fatal_signal+0x41b)[0x652fbb]/lib64/libpthread.so.0(+0xf7e0)[0x7f11c44c77e0]/lib64/libc.so.6(gsignal+0x35)[0x7f11c315d625]/lib64/libc.so.6(abort+0x175)
[0x7f11c315ee05]/usr/local/mysql/bin/mysqld[0xa585c5]/usr/local/mysql/bin/mysqld[0xa6c7b4]/usr/local/mysql/bin/mysqld[0xa6cbc7]/usr/local/mysql/bin/mysqld[0xa5bce2]/usr/local/mysql/bin/mysqld[0xa1e2ba]/usr/local/mysql/bin/mysqld[0xa0bf60]/usr/local/mysql/bin/mysqld[0x95a427]/usr/local/mysql/bin/mysqld(_Z24ha_initialize_handlertonP13st_plugin_int+0x48)[0x58f788]/usr/local/mysql/bin/mysqld[0x6e4a36]/usr/local/mysql/bin/mysqld(_Z11plugin_initPiPPci+0xb3e)[0x6e826e]/usr/local/mysql/bin/mysqld[0x582d85]/usr/local/mysql/bin/mysqld(_Z11mysqld_mainiPPc+0x4d8)[0x587d18]/lib64/libc.so.6(__libc_start_main+0xfd)[0x7f11c3149d5d]/usr/local/mysql/bin/mysqld[0x57a019]The manual page at http://dev.mysql.com/doc/mysql/en/crashing.html containsinformation that should help you find out what is causing the crash.161108 23:36:46 mysqld_safe mysqld from pid file /usr/local/mysql/var/VM_241_49_centos.pid ended------------------------------------------------------------------------------
复制代码

主库问题分析

从日志中可以看出是 innodb 引擎出了问题。日志里提示到 http://dev.mysql.com/doc/refman/5.6/en/forcing-innodb-recovery.html查看强制恢复的方法。在 mysql 的配置文件 my.cnf 里找到 [mysqld]字段下,添加 innodb_force_recovery=1:


[mysqld]innodb_force_recovery = 1
复制代码


如果 innodb_force_recovery = 1 不生效,则可尝试 2——6 几个数字


然后重启 mysql,重启成功。然后使用 mysqldump 或 pma 导出数据,执行修复操作等。修复完成后,把该参数注释掉,还原默认值 0。


配置文件的参数:innodb_force_recovery


innodb_force_recovery 影响整个 InnoDB 存储引擎的恢复状况。默认为 0,表示当需要恢复时执行所有的恢复操作(即校验数据页/purge undo/insert buffer merge/rolling back&forward),当不能进行有效的恢复操作时,mysql 有可能无法启动,并记录错误日志;


innodb_force_recovery 可以设置为 1-6,大的数字包含前面所有数字的影响。当设置参数值大于 0 后,可以对表进行 select,create,drop 操作,但 insert,update 或者 delete 这类操作是不允许的。


  • (SRV_FORCE_IGNORE_CORRUPT):忽略检查到的 corrupt 页。

  • (SRV_FORCE_NO_BACKGROUND):阻止主线程的运行,如主线程需要执行 full purge 操作,会导致 crash。

  • (SRV_FORCE_NO_TRX_UNDO):不执行事务回滚操作。

  • (SRV_FORCE_NO_IBUF_MERGE):不执行插入缓冲的合并操作。

  • (SRV_FORCE_NO_UNDO_LOG_SCAN):不查看重做日志,InnoDB 存储引擎会将未提交的事务视为已提交。

  • (SRV_FORCE_NO_LOG_REDO):不执行前滚的操作。

主库解决方案

一般修复方法参考:


第一种方法


建立一张新表:


create table demo_bak  #和原表结构一样,只是把INNODB改成了MYISAM。
复制代码


把数据导进去


insert into demo_bak select * from demo;
复制代码


删除掉原表:


drop table demo;
复制代码


注释掉 innodb_force_recovery 之后,重启。


重命名:


rename table demo_bak to demo;
复制代码


最后改回存储引擎:


alter table demo engine = innodb
复制代码


第二种方法


另一个方法是使用 mysqldump 将表格导出,然后再导回到 InnoDB 表中。这两种方法的结果是相同的。备份导出(包括结构和数据):


mysqldump -uroot -p123 test > test.sql
复制代码


还原方法 1:


use test;source test.sql
复制代码


还原方法 2(系统命令行):


mysql -uroot -p123 test < test.sql;
复制代码


注意,CHECK TABLE 命令在 InnoDB 数据库中基本上是没有用的。


第三种方法


(1)配置 my.cnf


配置 innodb_force_recovery = 1 或 2——6 几个数字,重启 MySQL


(2)导出数据脚本


mysqldump -uroot -p123 test > test.sql
复制代码


导出 SQL 脚本。或者用 Navicat 将所有数据库/表导入到其他服务器的数据库中。注意:这里的数据一定要备份成功。然后删除原数据库中的数据。


(3)删除 ib_logfile0、ib_logfile1、ibdata1


备份 MySQL 数据目录下的 ib_logfile0、ib_logfile1、ibdata1 三个文件,然后将这三个文件删除


(4)配置 my.cnf


将 my.cnf 中 innodb_force_recovery = 1 或 2——6 几个数字这行配置删除或者配置为 innodb_force_recovery = 0,重启 MySQL 服务


(5)将数据导入 MySQL 数据库


mysql -uroot -p123 test < test.sql; 或者用Navicat将备份的数据导入到数据库中。
复制代码


此种方法下要注意的问题:


  • ib_logfile0、ib_logfile1、ibdata1 这三个文件一定要先备份后删除;

  • 一定要确认原数据导出成功了

  • 当数据导出成功后,删除原数据库中的数据时,如果提示不能删除,可在命令行进入 MySQL 的数据目录,手动删除相关数据库的文件夹或者数据库文件夹下的数据表文件,前提是数据一定导出或备份成功。


这里,我使用的是第三种方法恢复了主数据库的数据。


接下来,我们再来看看从数据库数据的恢复。

解决从库问题

主从复制原理

这里,我就简单的说下 MySQL 数据库的主从复制原理。


MySQL 主从复制原理,也称为 A/B 原理。


(1) Master 将数据改变记录到二进制日志(binary log)中,也就是配置文件 log-bin 指定的文件, 这些记录叫做二进制日志事件(binary log events);


(2) Slave 通过 I/O 线程读取 Master 中的 binary log events 并写入到它的中继日志(relay log);


(3) Slave 重做中继日志中的事件,把中继日志中的事件信息一条一条的在本地执行一次,完 成数据在本地的存储,从而实现将改变反映到它自己的数据(数据重放)。



复制过滤可以让你只复制服务器中的一部分数据,有两种复制过滤:


(1) 在 Master 上过滤二进制日志中的事件;


(2) 在 Slave 上过滤中继日志中的事件。如下:



relay_log 配置中继日志,log_slave_updates 表示 slave 将复制事件 写进自己的二进制日志.当设置 log_slave_updates 时,你可以让 slave 扮演其它 slave 的 master.此时,slave 把 sql 线程执行的事件写进自己的二进制日志(binary log)然后,它的 slave 可以获取这些事件并执行它。如下图所示(发送复制事件到其它的 Slave):


从库问题重现

恢复主库的数据后,向主库中插入了一批测试数据,大概有 1000 条,但是插入数据后,从库迟迟没有将数据同步过来。于是我先登录主库,执行如下命令。


mysql>show processlist;
复制代码


查看下进程是否 Sleep 太多。发现很正常。


再查看下主库的状态。


show master status;
复制代码


也正常。


mysql> show master status;+-------------------+----------+--------------+-------------------------------+| File              | Position | Binlog_Do_DB | Binlog_Ignore_DB              |+-------------------+----------+--------------+-------------------------------+| mysqld-bin.000001 |     3260 |              | mysql,test,information_schema |+-------------------+----------+--------------+-------------------------------+1 row in set (0.00 sec)
复制代码


再到从库上查看从库的状态。


mysql> show slave status\G                                                 Slave_IO_Running: YesSlave_SQL_Running: No
复制代码


发现是 Slave 不同步了。这里,如果主从数据库版本一致或不一致又会存在两种解决方案。

主从版本一致解决方案

下面介绍两种解决方法


方法一:忽略错误后,继续同步


该方法适用于主从库数据相差不大,或者要求数据可以不完全统一的情况,数据要求不严格的情况


解决:


stop slave; #表示跳过一步错误,后面的数字可变set global sql_slave_skip_counter =1;start slave;
复制代码


之后再用 mysql> show slave status\G 查看


mysql> show slave status\GSlave_IO_Running: YesSlave_SQL_Running: Yes
复制代码


ok,现在主从同步状态正常了。。。


方式二:重新做主从,完全同步


该方法适用于主从库数据相差较大,或者要求数据完全统一的情况


解决步骤如下:


(1)先进入主库,进行锁表,防止数据写入


使用命令:


mysql> flush tables with read lock;
复制代码


注意:该处是锁定为只读状态,语句不区分大小写


(2)进行数据备份


#把数据备份到 mysql.bak.sql 文件


mysqldump -uroot -p -hlocalhost > mysql.bak.sql
复制代码


这里注意一点:数据库备份一定要定期进行,可以用 shell 脚本或者 python 脚本,都比较方便,确保数据万无一失。


(3)查看 master 状态


mysql> show master status;+-------------------+----------+--------------+-------------------------------+| File              | Position | Binlog_Do_DB | Binlog_Ignore_DB              |+-------------------+----------+--------------+-------------------------------+| mysqld-bin.000001 |     3260 |              | mysql,test,information_schema |+-------------------+----------+--------------+-------------------------------+1 row in set (0.00 sec)
复制代码


(4)把 mysql 备份文件传到从库机器,进行数据恢复


scp mysql.bak.sql root@192.168.128.101:/tmp/
复制代码


(5)停止从库的状态


mysql> stop slave;
复制代码


(6)然后到从库执行 mysql 命令,导入数据备份


mysql> source /tmp/mysql.bak.sql
复制代码


(7)设置从库同步,注意该处的同步点,就是主库 show master status 信息里的| File| Position 两项


change master to master_host = '192.168.128.100', master_user = 'rsync',  master_port=3306, master_password='', master_log_file =  'mysqld-bin.000001', master_log_pos=3260;
复制代码


(8)重新开启从同步


mysql> start slave;
复制代码


(9)查看同步状态


mysql> show slave status\G  
Slave_IO_Running: YesSlave_SQL_Running: Yes
复制代码


(10)回到主库并执行如下命令解除表锁定。


UNLOCK TABLES;
复制代码


如果主从数据库的版本是一致的,以上述方式回复从数据库是没啥问题的,如果主从数据库版本不一致的话,以上述方式回复主从数据库可能还会存在问题。

主从版本不一致解决方案

如果 MySQL 主库和从库的版本不一致时,使用 show slave status \G命令查看从库状态时可能会看到如下所示的信息。


Slave_IO_Running: YesSlave_SQL_Running: No……Last_Errno: 1755Last_Error: Cannot execute the current event group in the parallel mode. Encountered event Gtid, relay-log name ./mysql-relay.000002, position 123321 which prevents execution of this event group in parallel mode. Reason: The master event is logically timestamped incorrectly...
复制代码


注意如下的输出信息。


Last_Errno: 1755Last_Error: Cannot execute the current event group in the parallel mode. Encountered event Gtid, relay-log name ./mysql-relay.000002, position 123321 which prevents execution of this event group in parallel mode. Reason: The master event is logically timestamped incorrectly...
复制代码


这是由于主库使用的是 MySQL5.6,从库使用的是 MySQL5.7,数据库版本不一致引起的。


从 MySQL 官网搜索 1755 的报错看到是并行复制 bug 导致的报错。想要解决的办法也很简单,直接关掉并行复制即可。


stop slave;set global slave_parallel_workers=0;start slave;
复制代码


如果从库还是存在问题,则可按照主从版本一致的方案来恢复从库的数据。


这里,附加一个 MySQL 官方文档对于 1755 错误码的描述。


https://bugs.mysql.com/bug.php?id=71495


好了,今天就到这儿吧,我是冰河,大家有啥问题可以在下方留言,也可以加我微信:sun_shine_lyz,我拉你进群,一起交流技术,一起进阶,一起牛逼~~

发布于: 2021 年 04 月 09 日阅读数: 32
用户头像

冰河

关注

公众号:冰河技术 2020.05.29 加入

Mykit系列开源框架发起者、核心架构师和开发者,《海量数据处理与大数据技术实战》与《MySQL开发、优化与运维实战》作者。【冰河技术】微信公众号作者。

评论

发布
暂无评论
在业务高峰期拔掉服务器电源是一种怎样的体验?