记一次MySQL Crash处理

在MySQL 5.7的高并发场景下,有可能会触发MySQL5.7的Bug,导致宕机,临时的紧急修复方法可针对实例进行参数的修改。Bug的详细信息,请参考Bug#77588Bug#100771

Bug的详细信息:

2020-12-10 18:12:42 7f5e54b3e700  InnoDB: Assertion failure in thread 140043124729600 in file ha_innodb.cc line 10730
InnoDB: Failing assertion: index->table->stat_initialized
InnoDB: We intentionally generate a memory trap.
InnoDB: Submit a detailed bug report to [http://bugs.mysql.com](http://bugs.mysql.com/).
InnoDB: If you get repeated assertion failures or crashes, even
InnoDB: immediately after the mysqld startup, there may be
InnoDB: corruption in the InnoDB tablespace. Please refer to
InnoDB: [http://dev.mysql.com/doc/refman/5.6/en/forcing-innodb-recovery.html](http://dev.mysql.com/doc/refman/5.6/en/forcing-innodb-recovery.html)
InnoDB: about forcing recovery.
18:12:42 UTC - mysqld got signal 6 ;
This could be because you hit a bug. It is also possible that this binary
or 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 help
diagnose the problem, but since we have already crashed, 
something is definitely wrong and this may fail.

key_buffer_size=33554432
read_buffer_size=131072
max_used_connections=6139
max_threads=7000
thread_count=4112
connection_count=4109
It is possible that mysqld could use up to 
key_buffer_size + (read_buffer_size + sort_buffer_size)*max_threads = 2810236 K  bytes of memory
Hope that's ok; if not, decrease some variables in the equation.

Thread pointer: 0xce7b1b30
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 = 7f5e54b3de18 thread_stack 0x40000
/usr/sbin/mysqld(my_print_stacktrace+0x35)[0x8db795]
/usr/sbin/mysqld(handle_fatal_signal+0x494)[0x664dc4]
/lib64/libpthread.so.0[0x3280a0f710]
/lib64/libc.so.6(gsignal+0x35)[0x3280632625]
/lib64/libc.so.6(abort+0x175)[0x3280633e05]
/usr/sbin/mysqld[0x9893b4]
/usr/sbin/mysqld[0x98baab]
/usr/sbin/mysqld(_ZN7handler7ha_openEP5TABLEPKcii+0x3e)[0x5a8a1e]
/usr/sbin/mysqld(_Z21open_table_from_shareP3THDP11TABLE_SHAREPKcjjjP5TABLEb+0x684)[0x76ded4]
/usr/sbin/mysqld(_Z10open_tableP3THDP10TABLE_LISTP18Open_table_context+0x1e3)[0x69cc13]
/usr/sbin/mysqld(_Z11open_tablesP3THDPP10TABLE_LISTPjjP19Prelocking_strategy+0xc9f)[0x69f49f]
/usr/sbin/mysqld(_Z30open_normal_and_derived_tablesP3THDP10TABLE_LISTj+0x4f)[0x69f5cf]
/usr/sbin/mysqld[0x6df2e1]
/usr/sbin/mysqld(_Z21mysql_execute_commandP3THD+0x1553)[0x6e1c63]
/usr/sbin/mysqld(_Z11mysql_parseP3THDPcjP12Parser_state+0x3c0)[0x6e5790]
/usr/sbin/mysqld(_Z16dispatch_command19enum_server_commandP3THDPcj+0x152b)[0x6e6d6b]
/usr/sbin/mysqld(_Z24do_handle_one_connectionP3THD+0xcf)[0x6b32bf]
/usr/sbin/mysqld(handle_one_connection+0x47)[0x6b33e7]
/usr/sbin/mysqld(pfs_spawn_thread+0x12a)[0x960b4a]
/lib64/libpthread.so.0[0x3280a079d1]
/lib64/libc.so.6(clone+0x6d)[0x32806e88fd]

Trying to get some variables.
Some pointers may be invalid and cause the dump to abort.
Query (7f5de8f1fac0): is an invalid pointer
Connection ID (thread ID): 246446
Status: NOT_KILLED

解决方法:

set global innodb_stats_auto_recalc=off;
set global innodb_stats_persistent=off;
set global innodb_spin_wait_delay=6;
set global innodb_sync_spin_loops=30;
set global table_definition_cache=3000;
  1. innodb_stats_auto_recalc
    参数含义:是否自动触发更新统计信息
    触发条件:
    a. 表修改时,确认变化的数据是否超过10%,超过自动收集统计信息;
    b. 表的索引统计信息是持久化存储;
    默认值:ON

    详细解读:
    由于自动统计信息重新计算(发生在后台)是异步,在运行影响超过10%的表的DML操作时(即使innodb_stats_auto_recalc启用后),可能不会立即重新计算统计信息 。在某些情况下,统计重新计算可能会延迟几秒钟(10s)。如果在更改表的重要部分之后立即需要最新统计信息,请运行ANALYZE TABLE以启动统计信息的同步(前台)重新计算。

    如果禁用了innodb_stats_auto_recalc,请在对索引列进行实质性更改后,通过为每个适用的表发出ANALYZE TABLE语句来确保统计信息的准确性。

    在表上添加索引或者添加删除索引中的列时,将自动计算索引统计信息并将其添加到innodb_index_stats表,不受innodb_stats_auto_recalc的值影响。

  2. innodb_stats_persistent
    参数含义:是否启用持久化统计信息功能
    默认值:ON

    详细解读:
    持久化统计信息功能通过将统计信息存储到磁盘并使其在服务器重新启动期间保持不变来提高执行计划的稳定性,以便优化器更有可能每次为给定查询做出一致的选择。

  3. innodb_spin_wait_delay
    参数含义:每次互斥锁自旋的等待的时间
    默认值:6

  1. innodb_sync_spin_loops
    参数含义:获取互斥锁的自旋次数
    默认值:30

  2. table_definition_cache
    参数含义:数据库实例打开表定义文件的数量,应大于实际表数量,最大值为2000
    默认值:-1

关于innodb_spin_wait_delay和innodb_sync_spin_loops,涉及到InnoDB内核的一些知识点,这里做一些介绍。

InnoDB 中的mutex 和 rw_lock 在早期的版本都是通过系统提供的cas,tas 语义自己进行实现,并没有使用pthread_mutex_t,pthread_rwlock_t,这样实现的好处在于便于统计,以及为了性能考虑,还有解决早期操作系统的一些限制。

大概的原理就是,在mutex_enter 之后,在spin 的次数超过 innodb_sync_spin_loops=30 每次最多 innodb_spin_wait_delay=6如果还没有拿到Mutex,会主动yield() 这个线程,然后wait 在自己实现的wait array 进行等待。

这里每次spin 时候,等待的时候执行的是ut_delay,在ut_delay 中是执行 "pause" 指定,当innodb_spin_wait_delay = 6 的时候,在当年100MHz Pentium cpu,这个时间最大是1us。

wait array 也是InnoDB 实现的一种cond_wait 的实现,早期的MySQL 需要wait array 是因为操作系统无法提供超过100000 event,因此wait array 在用户态去进行这些event 维护,但是到了MySQL 5.0.30 以后,大部分操作系统已经能够处理100000 event,那么现在之所以还需要 wait array,主要是为了统计。

在wait array 的实现里面其实有一把大wait array mutex,是一个pthread_mutex_t,然后在wait array 里面的每一个wait cell 中,包含了os_event_t,wait 的时候调用了os_event_wait_low(),然后在 os_event_t 里面也包含了一个mutex,因此在一次wait 里面就有可能调用了两次pthread_mutex_t 的wait。

并且在os_event_t 唤醒的机制中是直接通过pthread_cond_boradcast(),当有大量线程等待在一个event 的时候, 会造成很多无谓的唤醒。

但是现有的mutex 实现会导致cpu 利用过高,差不多比使用pthread mutex 高16%,并且上下文切换也会更高。

主要的原因是:

  1. 因为Mutex 的唤醒在os_event 里面,os_event 实现中,如果需要执行唤醒操作,那么需要执行pthread_cond_boradcast() 操作,需要把所有等待的pthread 都唤醒,而不是只唤醒一个;
  2. 在wait array 的实现中,需要有一个全局的pthread_mutex_t 保护 sync array;
  3. 在默认的配置中,innodb_spin_wait_delay=6 是ut_delay 执行1us,innodb_sync_spin_loops=30 会执行30次,那么每次mutex 有可能都需要spin 30us;

不同场景需要的mutex 是不一样的,比如buffer pool 上面的page 的mutex 希望的就是一直spin。有些mutex 其实则是希望立刻就进入等待,只用使用这些mutex 的使用者知道接下来哪一个策略更合适。操作系统提供了futex 可能比InnoDB 自己通过wait array 的实现方式,对于通知机制而言会做的更好。

总结了现有的 mutex 实现存在的问题:

  1. 只有自己实现的ib_mutex_t, 并没有支持futex 的实现;
  2. 所有的ib_mutex_t 的行为都是一样的,通过两个变量 innodb_spin_wait_delay(控制在Test 失败以后, 最多会delay 的时间),innodb_sync_spin_loops(控制spin 的次数)。不可以对某一个单独的ib_mutex_t 设置单独的wait + loop 次数;
  3. 所有的ib_mutex_t 由两个全局的变量控制,因为mutex 在尝试了innodb_sync_spin_loops 次以后,会等待在一个wait array 里面的一个wait cell 上,所有的wait cell 都会注册到一个叫wait array 的队列中进行等待;

在 InnoDB 8.0 的代码中总共实现了4种mutex 的实现方式,2种的策略:

  1. TTASFutexMutex 是spin + futex 的实现,在mutex_enter 之后,会首先spin 然后在futex 进行wait;
  2. TTASMutex 全spin 方式实现,在spin 的次数超过 innodb_sync_spin_loops=30 每次最多 innodb_spin_wait_delay=6us 以后,会主动yield() 这个线程,然后通过TAS(test and set 进行判断) 是否可以获得;
  3. OSTrackMutex,在系统自带的mutex 上进行封装,增加统计计数等等;
  4. TTASEevntMutex,InnoDB 一直使用的自己实现的Mutex;

同时在8.0 的实现中定义了两种策略,GenericPolicy和BlockMutexPolicy。这两种策略主要的区别在于在show engine innodb mutex 的时候不同的统计方式。

BlockMutexPolicy 用于统计所有buffer pool 使用的mutex,因此该Mutex 特别多。如果每一个bp 单独统计,浪费大量的内存空间,因此所有bp mutex 都在一起统计,事实上buffer pool 的rw_lock 也是一样。

GenericPolicy 用于除了buffer pool mutex 以外的其他地方。

目前InnoDB 里面都是使用 TTASEventMutex。只不过buffer pool 的mutex 使用的是 BlockMutexPolicy,而且他的mutex 使用的是 GenericPolicy,不过从目前的代码来看,也只是统计的区别而已。

但是从目前来看,不同场景使用不同的mutex,Buffer pool 使用 TTASMutex 实现,其他mutex 使用 TTASEventMutex,并且新加入的 TTASFutexMutex,也就是spin + futex 的实现方式,其实也不是默认使用的,而且wai array 的实现方式也并没有改动。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,904评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,581评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,527评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,463评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,546评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,572评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,582评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,330评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,776评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,087评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,257评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,923评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,571评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,192评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,436评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,145评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,127评论 2 352

推荐阅读更多精彩内容

  • 1. 序 innodb存储引擎在show engine innodb status输出中,显示除了大量的内部信息,...
    mysia阅读 1,432评论 0 0
  • Rocksdb相对于leveldb的很重要的一个改进点就是写聚合流程的改进。在leveldb中,写的互斥就一把锁搞...
    从此启航阅读 1,151评论 0 1
  • InnoDB体系架构 后台线程分类 Master Thread 主要负责将缓冲池中的数据异步刷新到磁盘,保证数据的...
    xiaolyuh阅读 582评论 0 0
  • 当一个系统访问量上来的时候,不只是数据库性能瓶颈问题了,数据库数据安全也会浮现,这时候合理使用数据库锁机制就显得异...
    初来的雨天阅读 3,570评论 0 22
  • Schema与数据类型优化 选择优化的数据类型 有几个简单的原则: 更小的通常更好一般情况下使用可以正确存储数据的...
    陈晨_软件五千言阅读 3,516评论 1 65