概叙

科普文:软件架构数据库系列之【如何查看MySQL运行状态:SHOW STATUS】-CSDN博客

科普文:软件架构数据库系列之【MySQL性能调优概叙】-CSDN博客

科普文:软件架构数据库系列之【MySQL状态参数:table cache源码浅析】-CSDN博客

科普文:软件架构数据库系列之【MySQL状态参数:open table浅析和[ERROR] Error in accept: Two many open files】-CSDN博客

关于table cache,前面的文章中都有提到,table cache作为MySQL的性能优化手段之一。table cache是MySQL中的一个缓冲区域,用来缓存MySQL在运行期间打开的表,提升MySQL的性能。

Table cache功能特性

MySQL是多线程的,每一个客户端都可能同时向服务端的同一个表执行多个查询,为了解决多个客户端在同一个表上有不同状态的问题,每一个会话都会单独打开表,这中做法使用了更多的内存但是提升MySQL的性能。对于MyISAM表,每个线程还会为数据文件使用一个额外的文件描述符(索引不需要,在所有会话中共享)。

table_open_cache和max_connections两个系统变量影响了服务器能够打开的最大文件数,增加这两个参数的值,可能会遇到操作系统对每个进程能够打开文件描述符个数的限制,可以通过修改修改操作系统的文件限制来解决。

table_open_cache的值和max_connections有关,例如,200个并发运行的连接,指定的table_open_cache的值至少是200*N,其中N是所有连接中每个连接查询表的最大数量,同时还需要为临时表和文件预留额外的文件描述符。在设置table_open_cache时,要确保系统可以处理打开的文件描述符的数量,如果table_open_cache太大,MySQL可能会用尽文件描述符,出现拒绝连接或者是查询失败等问题。MyISAM引擎的表每个表需要两个文件描述符,如果是MyISAM的分区表,则每个分区需要两个。可以通过修改open_files_limit系统变量来设置MySQL可用的文件描述符的数量。

table_open_cache表示MySQL打开表的数量,可以在数据库启动时设置好。

MySQL按照以下逻辑来从table cache中淘汰未使用的表:

  • table cache满了,且线程打开一个不再table cache中的表。
  • table cache中表数量达到了table_open_cache的值,且cache中有所有线程都不再使用的表。
  • 执行FLUSH TABLE语句,mysqladmin flush-tables或者mysqladmin refresh命令

当table cache满了时,服务器根据最近最久未使用原则淘汰缓存的表。如果新的表必须要打开,但是table cache已经满了,且没有表能够被释放,则缓存会临时按需扩展,此时,只要有表从使用状态退出到未使用状态就会被淘汰。

通过检查状态变量opened_tables(自从服务启动后MySQL打开的表的数量),可以知道table cache是否合理,如果opened_tables的值很大,且在快速增加(此时并未执行FLUSH TABLE语句),可以考虑在MySQL启动的时候增加table_open_cache的值。

Table cache功能特性存在的Bug

https://dev.mysql.com/doc/refman/5.7/en/table-cache.html

不过很不幸的是MySQL5.7频频被爆出Table cache 导致 MySQL 崩溃。

Bug #89126create table panic on innobase_parse_hint_from_comment

MySQL Bugs: #89126: create table panic on innobase_parse_hint_from_comment

MySQL Bug #89126 是一个关于复制的问题,它发生在MySQL 5.7.8到5.7.21的版本中,主要表现为复制操作时,如果主服务器上的某个表使用了全文索引(FULLTEXT index),可能会导致复制失败。

错误描述:

复制过程中,从服务器可能会遇到错误,提示“Table definition has changed, please recreate table”,这是因为复制过程中主服务器上的表结构与从服务器上的表结构不一致。

解决方法:

  1. 升级到MySQL 5.7.22或更高版本,这个版本修复了这个bug。

  2. 如果不能升级,可以在主服务器上重新创建包含全文索引的表,并确保所有数据都已经被复制到从服务器上。

  3. 暂时停止复制,手动修复表结构,然后重新开启复制。

  4. 使用pt-table-sync工具来同步主从服务器上的表结构。

注意:在执行任何操作之前,请确保备份了所有的数据,以防止数据丢失。

爱可生的黄炎大佬提的bug:2018年1月7号提的bug版本是5.7.16,在2019年2月2号修复。

 

最终修复版本是:Fixed as of the upcoming 5.7.26, 8.0.16 release, and here's the changelog entry:

同样问题的bug: MySQL Bug #86825

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

 MySQL Bug #86825 是一个与复制和事务隔离级别相关的问题。这个问题发生在MySQL 5.7.8之前的版本中,当使用READ COMMITTED隔离级别时,可能会导致复制中断或复制数据不一致的问题。

问题解释:

READ COMMITTED 隔离级别下,如果一个事务在另一个事务修改了某些数据后读取这些数据,那么它可能会看到“脏”数据(即未提交的数据)。MySQL复制中的问题在于,如果从服务器上的复制线程在事务提交前读取了数据,然后在主服务器上提交了这个事务,而这个事务的数据变更没有及时同步到从服务器上,从服务器就可能会丢失这些更改,导致主从数据不一致。

解决方法:

  1. 升级到MySQL 5.7.8或更高版本,这个版本修复了该Bug。

  2. 如果不能升级,可以考虑将复制从基于行的复制切换到基于SQL的复制,或使用其他的复制方法来减少这种不一致的风险。

  3. 在主服务器上设置transaction-isolation = REPEATABLE-READ,这样可以减少READ COMMITTED隔离级别下的不一致风险,但是这也意味着在事务中有可能读取到过时的数据。

务必在进行任何更改之前备份数据库,并在测试环境中验证解决方案。

同样问题的bug:MySQL Bug #90126

MySQL Bugs: #90126: Sig 11 in dict_index_set_merge_threshold | dict/dict0dict.cc:5923

MySQL Bug #90126 是一个关于复制的问题,它发生在MySQL 5.7.8到5.7.9版本中,并涉及到在主从复制环境中,如果主服务器上的事务在从服务器上未能及时同步或应用,可能会导致主服务器上的事务提交失败。

问题解释:

复制中的一致性校验失败,导致主服务器认为一些事务未能正确复制到从服务器上,因此主服务器上的这些事务被回滚。

解决方法:

  1. 升级到MySQL 5.7.9或更高版本,这会包含此问题的修复。

  2. 如果不能升级,可以考虑临时停止复制,手动解决复制中断的问题,然后重新开启复制。

  3. 检查网络状况,确保主从服务器之间的网络连接稳定,以避免复制中断。

  4. 检查复制延迟,如果延迟过高,考虑优化复制配置或服务器性能。

  5. 查看MySQL错误日志,了解详细的错误信息,并根据具体情况采取相应的解决措施。

务必在执行任何操作前备份数据,以防止数据丢失。

3个bug的缺陷现象

 其主要现象是:在使用很多表的数据库中,执行 create table 会导致数据库崩溃。

  1. 在MySQL中创建大量表,填满 InnoDB的表定义缓存 (缓存的大小配置参看后文)。

  2. 继续执行 create table ...,有一定概率使得MySQL在如下堆栈或类似堆栈崩溃: 

缺陷的原理图解

  1. 图2,create table ... 向缓存中插入了新的表定义,并持有了表定义的引用,但代码中将 表定义 释放了

  2. 图3,InnoDB在此时刚好进行缓存回收,回收掉了已经释放的表定义。

  3. 图4,create table ... 继续进行,使用了表定义的引用,由于使用了被回收的内存地址,MySQL继而崩溃。

  4. 对缺陷的修复:第1步的行为变更为不释放表定义,从而在第2步中表定义不会回收 (目前这个修复方法尚未被官方接受)。

相关的知识点

  1. 表定义缓存 (table cache):InnoDB 会对表定义进行缓存,以减少不停读取表定义文件造成的IO压力。

  2. 缓存的内部同时维护了两个数据结构:

    1. Hash结构,方便缓存的查找

    2. LRU (Least Recently Used) 结构,方便缓存的回收

  3. InnoDB 对缓存进行定时回收,回收策略分为 忙时 和 闲时 两种策略,用于减少回收动作造成的负担。

  4. 缓存的容量可临时超过其限制大小。这个设计与定时回收有关。

  5. 缓存的容量上限通过参数 table_definition_cache 设置:

    1. 参数的最小值是400

    2. 默认值是通过以下公式计算得出: min(400+table_open_cache/2),2000) 

配置建议

  1. 除非有上万张表,一般建议设置 table_definition_cache 值略大于表数量,使缓存能够容纳所有的表定义

其他

科普文:软件架构数据库系列之【MySQL:8.0全新的字典缓存(代替5.7 frm文件)】作者|重庆八怪-CSDN博客

不过可惜啊,就在今年2024年07月MySQL8.0、8.4、MySQL9.0都还爆出数据库下表的数量超过1万张表时,触发重大bug Crash。

MySQL 8.0.38,MySQL 8.4.1,MySQL 9.0.0三个版本中被确认这个问题在 >= 8.0.38 版本中存在,包括 8.4.1 和 9.0.0。

MySQL :: MySQL 8.4 Release Notes :: Changes in MySQL 8.4.2 (2024-07-23, LTS Release)

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐