MySQL线程处于Opening tables的问题解决(附示例)

本篇文章给大家带来的内容是关于MySQL线程处于Opening tables的问题解决(附示例),有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。

问题描述

最近有一台mysql5.6.21的服务器,在应用发布后,并发线程threads_running迅速升高,达到2000左右,大量线程处于等待opening tables、closing tables状态,应用端相关逻辑访问超时。

【分析过程】

1、16:10应用发布结束后,Opened_tables不断增加,如下图所示:
MySQL线程处于Opening tables的问题解决(附示例)

查看当时故障期间抓取的pt-stalk日志文件,时间点2019-01-18 16:29:37,Open_tables 的值为3430,而table_open_cache的配置值为2000。
当Open_tables值大于table_open_cache值时,每次新的session打开表,有一些无法命中table cache,而不得不重新打开表。这样反应出来的现象就是有大量的线程处于opening tables状态。

2、这个实例下的表,加上系统数据库下总计851张,远小于table_open_cache的2000,为什么会导致Open_tables达到3430呢
从官方文档中可以得到解释,
https://dev.mysql.com/doc/refman/5.6/en/table-cache.html

table_open_cache is related to max_connections. For example, for 200 concurrent running connections, specify a table cache size of at least 200 * N, where N is the maximum number of tables per join in any of the queries which you execute.

当时并发线程数达到1980,假设这些并发连接中有30%是访问2张表,其他都是单表,那么cache size就会达到(1980*30%*2+1980*70%*1)=2574

3、QPS在发布前后都比较平稳,从外部请求来看并没有突增的连接请求,但在发布后threads_running上升到接近2000的高位,一直持续。猜测是由于某个发布的SQL语句触发了问题。

4、查看当时抓取的processlist信息,有一句SQL并发访问很高,查询了8张物理表,SQL样本如下:

select id,name,email from table1 left join table2
union all
select id,name,email from table3 left join table4
union all
select id,name,email from table5 left join table6
union all
select id,name,email from table7 left join table8
where id in ('aaa');

5、在测试环境中创建相同的8张表,清空表缓存,单个session执行SQL前后对比,Open_tables的值会增加8,如果高并发的情况下,Open_tables的值就会大幅增加。

问题重现

在测试环境上模拟高并发访问的场景,并发1000个线程同时执行上面的SQL语句,复现了生产环境类似的现象,Open_tables迅速达到3800,大量进程处于Opening tables、closing tables状态。

优化方案

1、 定位到问题原因后,我们与开发同事沟通,建议优化该SQL,降低单句SQL查询表的数量或大幅降低该SQL的并发访问频率。
不过开发同事还没来的及优化,生产环境上故障又出现了。当时DBA排障时将table_open_cache从2000增加4000,CPU使用率上升,效果并不明显,等待Opening tables的问题依然存在。

2、 分析故障期间抓取的pstack信息,用pt-pmp聚合后,看到大量线程在open_table时等待mutex资源:

AI建筑知识问答 AI建筑知识问答

用人工智能ChatGPT帮你解答所有建筑问题

AI建筑知识问答 22 查看详情 AI建筑知识问答

#0  0x0000003f0900e334 in __lll_lock_wait () from /lib64/libpthread.so.0#1  0x0000003f0900960e in _L_lock_995 () from /lib64/libpthread.so.0#2  0x0000003f09009576 in pthread_mutex_lock () from /lib64/libpthread.so.0#3  0x000000000069ce98 in open_table(THD*, TABLE_LIST*, Open_table_context*) ()#4  0x000000000069f2ba in open_tables(THD*, TABLE_LIST**, unsigned int*, unsigned int, Prelocking_strategy*) ()#5  0x000000000069f3df in open_normal_and_derived_tables(THD*, TABLE_LIST*, unsigned int) ()#6  0x00000000006de821 in execute_sqlcom_select(THD*, TABLE_LIST*) ()#7  0x00000000006e13cf in mysql_execute_command(THD*) ()#8  0x00000000006e4d8f in mysql_parse(THD*, char*, unsigned int, Parser_state*) ()#9  0x00000000006e62cb in dispatch_command(enum_server_command, THD*, char*, unsigned int) ()#10 0x00000000006b304f in do_handle_one_connection(THD*) ()#11 0x00000000006b3177 in handle_one_connection ()#12 0x0000000000afe5ca in pfs_spawn_thread ()#13 0x0000003f09007aa1 in start_thread () from /lib64/libpthread.so.0#14 0x0000003f088e893d in clone () from /lib64/libc.so.6

这时table_cache_manager中的mutex冲突非常严重。
由于MySQL5.6.21下table_open_cache_instances参数的默认值为1,想到增大table_open_cache_instances参数,增加表缓存分区,应该可以缓解争用。

3、 在测试环境上,我们调整两个参数table_open_cache_instances=32,table_open_cache=6000,同样并发1000个线程执行问题SQL,这次等待Opening tables、closing tables的线程消失了,MySQL的QPS也从12000上升到55000。
对比相同情况下,只调整table_open_cache=6000,等待Opening tables的进程数从861下降到203,问题有所缓解,有600多个进程已经从等待Opening tables变为运行状态,QPS上升到40000左右,但不能根治。

源码分析

查了下代码有关table_open_cache的相关逻辑:
1、Table_cache::add_used_table函数如下,当新的连接打开的表在table cache中不存在时,打开表加入到used tables list:

bool Table_cache::add_used_table(THD *thd, TABLE *table){  Table_cache_element *el;  assert_owner();  DBUG_ASSERT(table->in_use == thd);  /*    Try to get Table_cache_element representing this table in the cache    from array in the TABLE_SHARE.  */  el= table->s->cache_element[table_cache_manager.cache_index(this)];  if (!el)  {    /*      If TABLE_SHARE doesn't have pointer to the element representing table      in this cache, the element for the table must be absent from table the      cache.      Allocate new Table_cache_element object and add it to the cache      and array in TABLE_SHARE.    */    DBUG_ASSERT(! my_hash_search(&m_cache,                                 (uchar*)table->s->table_cache_key.str,                                 table->s->table_cache_key.length));    if (!(el= new Table_cache_element(table->s)))      return true;    if (my_hash_insert(&m_cache, (uchar*)el))    {      delete el;      return true;    }    table->s->cache_element[table_cache_manager.cache_index(this)]= el;  }  /* Add table to the used tables list */    el->used_tables.push_front(table);  m_table_count++;  free_unused_tables_if_necessary(thd);  return false;}

2、每次add_used_table会调用Table_cache::free_unused_tables_if_necessary函数,当满足m_table_count > table_cache_size_per_instance &&m_unused_tables时,执行remove_table,清除m_unused_tables列表中多余的cache。其中table_cache_size_per_instance= table_cache_size / table_cache_instances,MySQL5.6的默认配置是2000/1=2000,当m_table_count值大于2000并且m_unused_tables非空时就执行remove_table,将m_unused_tables中的table cache清空。这样m_table_count就是Open_tables的值正常会维持在2000上下。

void Table_cache::free_unused_tables_if_necessary(THD *thd){  /*    We have too many TABLE instances around let us try to get rid of them.    Note that we might need to free more than one TABLE object, and thus    need the below loop, in case when table_cache_size is changed dynamically,    at server run time.  */  if (m_table_count > table_cache_size_per_instance && m_unused_tables)  {    mysql_mutex_lock(&LOCK_open);    while (m_table_count > table_cache_size_per_instance &&           m_unused_tables)    {      TABLE *table_to_free= m_unused_tables;            remove_table(table_to_free);      intern_close_table(table_to_free);      thd->status_var.table_open_cache_overflows++;    }    mysql_mutex_unlock(&LOCK_open);  }}

3、增大table_cache_instances为32,当Open_tables超过(2000/32=62)时,就会满足条件,加速上述逻辑中m_unused_tables的清理,使得table cache中数量进一步减少,会导致Table_open_cache_overflows升高。

4、当table_open_cache_instances从1增大到32时,1个LOCK_open锁分散到32个m_lock的mutex上,大大降低了锁的争用。

/** Acquire lock on table cache instance. */  void lock() { mysql_mutex_lock(&m_lock); }  /** Release lock on table cache instance. */  void unlock() { mysql_mutex_unlock(&m_lock); }

解决问题

我们生产环境同时采取下面优化措施,问题得以解决:
1、 读写分离,增加read节点,分散master库的压力;
2、 调整table_open_cache_instances=16;
3、 调整table_open_cache=6000;

总结

当出现Opening tables等待问题时,
1、建议找出打开表频繁的SQL语句,优化该SQL,降低单句SQL查询表的数量或大幅降低该SQL的并发访问频率。

2、设置合适的table cache,同时增大table_open_cache_instances和 table_open_cache参数的值。

以上就是MySQL线程处于Opening tables的问题解决(附示例)的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/377886.html

赞 (0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
Switch2截图直连PC传输 但USB端口使用存在隐藏限制
上一篇 2025年11月6日 10:17:30
咪咕视频会员5种取消方法介绍_咪咕会员扣话费了该怎么办
下一篇 2025年11月6日 10:17:47

相关推荐

  • 如何从Linux命令行直接执行MySQL/MariaDB查询

    如何从Linux命令行直接执行MySQL/MariaDB查询如何从Linux命令行直接执行MySQL/MariaDB查询如何从Linux命令行直接执行MySQL/MariaDB查询如何从Linux命令行直接执行MySQL/MariaDB查询

    如果您负责管理数据库服务器,可能需要定期执行查询并仔细检查其结果。虽然您可以在mysql/mariadb shell中执行这些操作,但本文介绍的技巧将使您能够直接在linux命令行中运行mysql/mariadb查询,并将输出保存到文件中,以便日后检查。这在查询返回大量记录时尤为有用。 让我们从一些…

    2026年9月23日 • 用户投稿
    200
  • 在MySQL中有效处理空值NULL的技巧

    在MySQL中有效处理空值NULL的技巧在MySQL中有效处理空值NULL的技巧在MySQL中有效处理空值NULL的技巧在MySQL中有效处理空值NULL的技巧

    1.在mysql中直接比较null值会出错,因为null代表的是“未知”状态,任何与null的比较结果都是unknown,而不是true或false;2.处理空值应使用is null、is not null判断,使用ifnull提供单一替代值,coalesce按优先级取第一个非null值,以及用nu…

    2026年9月23日 • 用户投稿
    600
  • 修改innodb参数解决MySQL事务日志乱码

    事务日志乱码通常并非日志本身问题,而是查看方式或配置不当所致。首先确认是否为正常显示现象,如show engine innodb status输出中的二进制结构表示或锁等待信息,检查客户端编码设置,避免误判;其次若需分析事务日志文件(如ib_logfile0、ib_logfile1),可调整inno…

    2026年9月23日
    000
  • 在PHP中将JSON数组值声明为变量

    本文介绍了如何在PHP中从数据库获取数据并将其编码为JSON数组,然后通过AJAX调用将其传递到另一个页面。重点讲解了如何在接收数据的页面中解析JSON数据,并将JSON数组中的特定值提取为PHP变量,以便在后续的函数或查询中使用。 从数据库获取数据并编码为JSON 首先,我们需要从数据库中获取数据…

    2026年9月23日
    1100
  • mysql怎么执行连接查询 mysql输入多表关联代码教程

    mysql怎么执行连接查询 mysql输入多表关联代码教程mysql怎么执行连接查询 mysql输入多表关联代码教程mysql怎么执行连接查询 mysql输入多表关联代码教程mysql怎么执行连接查询 mysql输入多表关联代码教程

    mysql多表关联查询的核心是join语句,常见的类型包括inner join、left join、right join和cross join。1. inner join返回两个表中匹配的行,适用于查询有明确关联的数据;2. left join返回左表所有行及右表匹配的行,未匹配列显示为null,适…

    2026年9月23日 • 用户投稿
    100
  • mysql安装完如何远程 mysql开启远程连接的配置方法

    mysql安装完如何远程 mysql开启远程连接的配置方法mysql安装完如何远程 mysql开启远程连接的配置方法mysql安装完如何远程 mysql开启远程连接的配置方法mysql安装完如何远程 mysql开启远程连接的配置方法

    要开启mysql远程连接需修改配置文件绑定地址为0.0.0.0并重启服务;创建或修改用户权限允许远程ip访问;确保服务器及云平台防火墙开放3306端口。1. 修改mysqld.cnf中的bind-address为0.0.0.0并重启mysql。2. 创建新用户或修改现有用户权限,使用’y…

    2026年9月23日 • 用户投稿
    100
  • 防止Spring Boot集成测试中数据冲突的策略与实践

    在Spring Boot集成测试中,并发执行测试可能导致数据冲突,尤其是在使用TestContainers和自动生成ID的场景下。本文将深入探讨此类问题,并提供基于@Transactional注解的有效解决方案,确保每个测试方法在独立且干净的数据环境中运行,从而提高测试的稳定性和可靠性。 理解集成测…

    2026年9月23日
    200
  • 如何验证MySQL是否安装成功?

    如何验证MySQL是否安装成功?如何验证MySQL是否安装成功?如何验证MySQL是否安装成功?如何验证MySQL是否安装成功?

    验证mysql是否安装成功需检查三点:服务状态、客户端连接和版本信息。首先在linux用systemctl status mysql或service mysql status,windows则查看服务状态确认运行;其次执行mysql -u root -p尝试登录并输入密码进入mysql>提示符…

    2026年9月23日 • 用户投稿
    000
  • Java中如何实现客户信息管理系统

    答案:通过定义Customer类封装客户信息,CustomerManager类管理客户列表,实现增删改查功能,主程序测试操作流程,系统可扩展至数据库存储和界面开发。 实现一个客户信息管理系统,核心是管理客户的基本信息,比如姓名、电话、地址等,支持增删改查功能。在Java中可以通过面向对象设计结合集合…

    2026年9月23日
    100
  • 如何在mysql中使用读写分离提高并发

    读写分离通过主从复制实现读写分流,应用层或中间件路由SQL,需关注主从延迟与故障切换,确保数据一致性。 在高并发场景下,MySQL 的读写分离是一种有效提升数据库性能的策略。通过将读操作分发到多个从库(Slave),写操作集中在主库(Master),可以减轻主库压力,提高整体吞吐量。以下是实现读写分…

    2026年9月23日
    000
  • mysql如何查看索引 mysql创建索引并验证效果步骤

    mysql如何查看索引 mysql创建索引并验证效果步骤mysql如何查看索引 mysql创建索引并验证效果步骤mysql如何查看索引 mysql创建索引并验证效果步骤mysql如何查看索引 mysql创建索引并验证效果步骤

    查看索引使用show index和show create table;2. 创建索引用create index或alter table;3. 验证索引使用explain分析查询计划;4. 索引失效原因包括数据类型不匹配、函数操作、模糊查询以%开头、or条件复杂、优化器判断选择性低等;5. 常见索引类…

    2026年9月23日 • 用户投稿
    100
  • VSCode如何通过扩展实现SQL查询 VSCode SQL编辑器插件的使用技巧

    首先确认数据库服务运行且vscode可访问数据库服务器,其次检查扩展配置信息如数据库类型、主机地址、端口、用户名密码等,确保本地路径正确或远程连接正常,检查防火墙是否开放数据库端口,确认已安装必要驱动程序如mysql或psycopg2,查看vscode输出面板获取错误信息,尝试更新或重装扩展,必要时…

    2026年9月23日
    100
  • Navicat连接MySQL的完整步骤

    Navicat连接MySQL的完整步骤Navicat连接MySQL的完整步骤Navicat连接MySQL的完整步骤Navicat连接MySQL的完整步骤

    navicat连接mysql的关键在于正确配置连接信息并排除常见问题。步骤包括:①下载安装navicat;②启动后创建mysql连接;③填写主机名、端口、用户名和密码等信息;④测试连接并保存;⑤双击连接进入数据库。常见问题及解决:①mysql服务未启动需手动启动;②端口被占用可检查并释放;③防火墙阻…

    2026年9月23日 • 用户投稿
    100
  • mysql添加索引命令 mysql创建普通唯一复合索引教程

    mysql添加索引命令 mysql创建普通唯一复合索引教程mysql添加索引命令 mysql创建普通唯一复合索引教程mysql添加索引命令 mysql创建普通唯一复合索引教程mysql添加索引命令 mysql创建普通唯一复合索引教程

    在mysql中给表加索引的核心目的是提升查询效率。解决方案是通过create index或alter table语句创建不同类型的索引:1. 普通索引用于加快非唯一列的查询;2. 唯一索引确保列值唯一性并提升性能;3. 复合索引支持多列组合查询,遵循最左前缀原则;4. 复合唯一索引结合复合和唯一特性…

    2026年9月23日 • 用户投稿
    100
  • PHP:根据条件动态禁用表格中的按钮

    本文旨在解决在PHP中,根据表格行中两列的值是否相等,动态禁用对应按钮的问题。通过修改循环遍历数据并生成HTML表格的代码,在生成按钮时增加条件判断,实现当mi_name列和item_name列的值相等时,禁用该行的按钮。文章提供两种实现方式,并附带代码示例,帮助开发者快速实现此功能。 在PHP中动…

    2026年9月23日
    200
  • 解决WordPress插件中$wpdb对象未初始化导致的数据库更新失败问题

    本文旨在解决wordpress插件开发中,使用`$wpdb`对象进行数据库更新时出现`call to a member function query() on null`错误。该错误通常是由于`$wpdb`对象未正确初始化所致。教程将详细解释错误原因,并提供通过引入`wp-config.php`文件…

    2026年9月23日
    200
  • 如何在mysql中使用FULL JOIN模拟查询

    MySQL不支持FULL JOIN,但可用LEFT JOIN和RIGHT JOIN结合UNION模拟实现,返回两表所有记录,无匹配时补NULL。例如查询users和orders表中所有用户及订单信息,包括无关联的记录,通过先左连接再右连接并合并结果,确保完整输出Alice(无订单)、Bob的订单及u…

    2026年9月23日
    100
  • mysql安装完如何连接java mysql jdbc驱动配置教程

    mysql安装完如何连接java mysql jdbc驱动配置教程mysql安装完如何连接java mysql jdbc驱动配置教程mysql安装完如何连接java mysql jdbc驱动配置教程mysql安装完如何连接java mysql jdbc驱动配置教程

    下载并导入jdbc驱动包;2. 正确配置数据库连接信息;3. 加载驱动并建立连接。使用java连接mysql的关键在于配置好jdbc驱动,首先去mysql官网下载对应版本的mysql-connector-java.jar包并导入项目,普通项目放入lib目录并添加为库,maven项目则在pom.xml…

    2026年9月23日 • 用户投稿
    300
  • 如何授予MySQL用户特定权限?

    如何授予MySQL用户特定权限?如何授予MySQL用户特定权限?如何授予MySQL用户特定权限?如何授予MySQL用户特定权限?

    要授予mysql用户特定权限,需使用grant语句并遵循最小权限原则。1. 登录mysql,使用root或有grant权限的账户;2. 使用grant 权限 on 数据库名.表名 to ‘用户名’@’主机名’语法授予权限,如select、insert等…

    2026年9月23日 • 用户投稿
    100
  • Django 项目运行时遇到“django.core.exceptions.ImproperlyConfigured”错误,如何解决?

    当在运行 Django 项目时遇到“django.core.exceptions.ImproperlyConfigured”错误时,这表明 Django 无法导入其预期的数据库后端。 在给定的代码中,错误消息指出 Django 无法导入“django.db.backends.mysql”,这可能是因…

    2026年9月23日
    100

发表回复

登录后才能评论
关注微信