MySQL 22 MySQL有哪些“饮鸩止渴”提高性能的方法?

有时候,在业务高峰期,生产环境的MySQL压力太大,没法正常响应,需要短期内、临时性地提升一些性能。本文就来讲讲一些临时方法,并着重说它们可能存在的风险。

短连接风暴

正常的短连接模式是连接到数据库后,执行很少的SQL语句就断开,下次需要的时候再重连。如果使用的是短连接,在业务高峰期时,可能出现连接数突然暴涨的情况。

MySQL建立连接的过程成本很高,在数据库压力比较小的时候,这些额外成本并不明显。

短连接模型存在一个风险,就是一旦数据库处理慢一些,连接数就会暴涨。max_connections参数用来控制一个MySQL实例同时存在的连接数的上限,超过这个值,系统就会拒绝接下来的连接请求并报错。对于被拒绝连接的请求来说,从业务角度看就是数据库不可用。

当机器负载较高,处理现有请求时间变长,每个连接保持的时间也更长,这时来的新连接很可能就超过数量限制。此时如果调高参数,那么系统负载可能进一步加大,大量的资源耗费在权限验证等逻辑上,结果可能更差。

这里还有两种方法,但都是有损的。

方法一:先处理掉那些占着连接但是不工作的线程

对于不需要保持的连接,可以通过kill connection主动剔除,这个行为和事先设置wait_timeout效果相同。wait_timeout参数表示线程空闲多少时间后会被MySQL直接断开连接。

在show processlist的结果里,踢掉显示为sleep的线程,可能是有损的:

在上面的流程中,如果断开session A的连接,由于事务还未提交,MySQL只能按照回滚事务来处理;如果断开session B的连接,就没有大影响。因此按照优先级来说,应该先断开像session B这样的事务外的空闲连接。

如何判断哪些是事务外空闲的呢?session C执行show processlist看到的结果为:

id=4,id=5两个会话都是sleep状态,而要看事务具体状态的话,可以查information_schema库的innodb_trx表:

trx_mysql_thread_id=4,表示id=4的线程还处在事务中。

因此,如果连接数过多,可以优先断开事务外空闲久的连接;如果还是太多,再考虑断开事务内空闲久的连接。

服务端断开连接用的是kill connection+id的命令,处于sleep状态的客户端连接被服务端主动断开后,客户端并不会马上知道,直到客户端在发起下一个请求时,才会收到报错"Lost connection to MySQL server during query"。

方法二:减少连接过程的消耗

一种可能的做法是让数据库跳过权限验证阶段,方法是重启数据库,并使用-skip-grant-tables参数启动,这样整个MySQL会跳过所有权限验证阶段,包括连接过程和语句执行过程在内。

但该方法风险极高,并不建议使用。在MySQL 8.0版本中,如果启用该参数,MySQL会默认把--skip-networking参数打开,表示这时候数据库只能被本地客户端连接,不可外网访问。

慢查询性能问题

在MySQL中,会引发性能问题的慢查询大体分为三种可能:

  • 索引没有设计好;

  • SQL语句没写好;

  • MySQL选错索引。

接下来就具体分析这三种可能。

索引没有设计好

这种场景一般就是通过紧急创建索引来解决。在MySQL 5.6版本之后,创建索引已支持Online DDL,对于高峰期数据库被语句打挂了的情况,最高效的做法就是直接执行alter table语句。

理想情况是能在备库先执行。假设现在有主库A和备库B,该方案的大致流程为:

  • 在备考B上执行set sql_log_bin=off,即不写binlog,然后执行alter table去加索引;

  • 执行主备切换;

  • 在A上执行set sql_log_bin=off,然后执行alter table加索引。

平时变更时,应该考虑gh-ost这样的方案更为稳妥,但紧急处理时上面的方案效率最高。

SQL语句没写好

有时候语句没写好,会导致语句没有使用上索引。这时可以通过改写SQL语句来处理。MySQL 5.7提供了query_rewrite功能,可以把输入的一种语句改写成另一种模式。比如语句被错误写成select * from t where id+1=10000,可以通过下面的方式进行改写:

sql 复制代码
mysql> insert into query_rewrite.rewrite_rules(pattern, replacement, pattern_database) values ("select * from t where id + 1 = ?", "select * from t where id = ? - 1", "db1");

call query_rewrite.flush_rewrite_rules();

call query_rewrite.flush_rewrite_rules()的存储过程是让插入的新规则生效,可以用下面的方法来确认改写规则是否生效:

MySQL选错索引

这时应急方案就是给语句加上force index。

上面的三种情况,实际上出现最多的是前两种。为了避免这两种情况,可以通过下面的过程预先发现问题:

  • 上线前,在测试环境打开慢查询日志,且把long_query_time设为0,确保每个语句都会被记录入慢查询日志;

  • 在测试表里插入模拟线上的数据,做一遍回归测试;

  • 观察日志里 每类语句的输出,特别留意Rows_examined字段是否与预期一致。

如果新增SQL语句不多,手动跑一下就可以。如果是新项目或修改了原项目的表结构设计,全量回归测试都是必要的,这是可以借助开源工具pt-query-digest。

QPS突增问题

有时候由于业务突然出现高峰,或者应用程序bug,导致某个语句的QPS突然暴涨,也可能导致MySQL压力过大,影响服务。

对于功能bug,最理想的情况是让业务把这个功能下掉,下掉一个功能,从数据库端处理的话,对于不同的背景,有不同的方法可用:

  • 由全新业务的bug导致。假设你的DB运维是比较规范的,也就是说白名单是一个个加的。这种情况下,如果能够确定业务方会下掉这个功能,只是时间上没那么快,那么就可以从数据库端直接把白名单去掉;

  • 如果功能使用的是单独的数据库用户,可以删除这个用户然后断开现有连接;

  • 如果新增功能跟主体功能部署在一起,只能通过处理语句来限制,如把压力最大的SQL语句直接重写成select 1。这个操作风险很高,可能存在两个副作用:

    • 如果别的功能也用到了这个SQL语句模板,会有误伤;

    • 很多业务不是只靠一个语句完成,如果单独把这个语句重写,可能导致后面的业务逻辑一起失败。

相关推荐
靡樊6 小时前
MySQL:C语言链接
数据库·mysql
一只游鱼6 小时前
linux使用yum安装数据库
linux·mysql·adb
熏鱼的小迷弟Liu7 小时前
【MySQL】一篇讲透MySQL的MVCC机制!
数据库·mysql
一匹电信狗11 小时前
【MySQL】数据库基础
linux·运维·服务器·数据库·mysql·ubuntu·小程序
奥尔特星云大使15 小时前
MySQL 备份基础(一)
数据库·sql·mysql·备份·mysql备份
努力学习的小廉16 小时前
初识MYSQL —— 库和表的操作
数据库·mysql·oracle
简色18 小时前
预约优化方案全链路优化实践
java·spring boot·后端·mysql·spring·rabbitmq
小森( ﹡ˆoˆ﹡ )20 小时前
GPT_Data_Processing_Tutorial
数据库·gpt·mysql
lang2015092821 小时前
MySQL在线DDL:零停机改表实战指南
数据库·mysql
程序新视界1 天前
MySQL的联合索引以及其最左前缀原则
数据库·mysql