MySQL 做的时间长了,就有可能多次遇到相同的 Bug,这里记录一下,以便下次再遇到,能够参考。
1. 背景
业务执行 SQL 导致 MySQL 进程 Crash,做故障切换后,新的主库又 Crash 了。查看 MySQL 错误日志,发现多次 Crash 时的堆栈相同,如下:
Thread pointer: 0x7fb49b866000
Attempting backtrace. You can use the following information to find out
where mysqld died. If you see no messages after this, something went
terribly wrong...
stack_bottom = 7fb4bf979be8 thread_stack 0x80000
/usr/local/mysql/bin/mysqld(my_print_stacktrace+0x2c)[0xed36bc]
/usr/local/mysql/bin/mysqld(handle_fatal_signal+0x461)[0x7a26e1]
/lib64/libpthread.so.0[0x369ea0f7e0]
/usr/local/mysql/bin/mysqld(_ZN10Field_blob15copy_blob_valueEP11st_mem_root+0x28)[0x7e6298]
/usr/local/mysql/bin/mysqld(_Z25mysql_prepare_blob_valuesP3THDR4ListI4ItemEP11st_mem_root+0x2b8)[0xe1c748]
/usr/local/mysql/bin/mysqld(_Z12write_recordP3THDP5TABLEP9COPY_INFOS4_+0x9c0)[0xe1d450]
/usr/local/mysql/bin/mysqld(_ZN14Sql_cmd_insert12mysql_insertEP3THDP10TABLE_LIST+0x825)[0xe1dec5]
/usr/local/mysql/bin/mysqld(_ZN14Sql_cmd_insert7executeEP3THD+0xc2)[0xe1e702]
/usr/local/mysql/bin/mysqld(_Z21mysql_execute_commandP3THDb+0x18d7)[0xcad217]
/usr/local/mysql/bin/mysqld(_Z11mysql_parseP3THDP12Parser_state+0x5d5)[0xcb3385]
/usr/local/mysql/bin/mysqld(_Z16dispatch_commandP3THDPK8COM_DATA19enum_server_command+0xaba)[0xcb3eca]
/usr/local/mysql/bin/mysqld(_Z10do_commandP3THD+0x1b7)[0xcb5917]
/usr/local/mysql/bin/mysqld(_Z26threadpool_process_requestP3THD+0xc7)[0xd5a267]
/usr/local/mysql/bin/mysqld[0xd6979e]
/usr/local/mysql/bin/mysqld(pfs_spawn_thread+0x1b4)[0x123cf24]
/lib64/libpthread.so.0[0x369ea07aa1]
/lib64/libc.so.6(clone+0x6d)[0x369e2e893d]
另外,机器上还保存了 Crash 时产生的 core 文件,gdb 分析 core 文件,找到了 Crash 时正在执行的 SQL,是一条 insert into on duplicate key update 类型的的 SQL。分析表结构,发现表中含有 text 类型的字段。另外说明一下,MySQL 版本为 Percona Server for MySQL 5.7.19。
2. 原因分析
结合以上错误堆栈、SQL、表结构等信息,在 MySQL Bug 列表中搜索到相关 Bug,如下:
https://bugs.mysql.com/bug.php?id=79243
该 Bug 发生条件:
- 表中包含 text, blob 等大字段类型
- 使用 insert into on duplicate key update 更新数据
该 Bug 从 5.7.9 版本被提出来,在 5.7.16, 5.7.19,5.7.21 都有出现过,官方文档上说 5.7.19 已修复,但是修复的并不彻底,在 5.7.19, 5.7.21 相继复现,官方给出的最后修复版本是 5.7.22。
3. 解决方案
- 升级 MySQL 到最新版本,比如目前 5.7 最新版本为 5.7.30, 8.0 最新版本为 8.0.20。
- 如果不方便升级 MySQL 版本,可以考虑修改表字段类型,将 text,blob 大字段类型用其他类型替代,比如 varchar。
- 修改 SQL,将 insert into on duplicate key update 换成其他语法,比如 replace into,当然 replace into 本身在一些版本中也有一些问题,在使用前要调研清楚。参考:https://mytecdb.com/blogDetail.php?id=174