0%

应急性能提升手段

短连接风暴

正常的短连接模式是连接到数据库后,执行很少的SQL语句就断开,下次需要的时候再重连。使用短连接,在业务高峰期的时候,可能出现连接数突然暴涨的情况。MySQL建立连接的过程成本很高,除了网络连接三次握手外,还要做登录权限判断和获取该连接的读取权限。

max_connextions参数,控制一个MySQL实例同时存在的连接数的上限,超过这个值,系统就会拒绝接下来的连接请求,并报错提示“Too many connections”,对业务来说就是数据库不可用。
负载比较高时,单纯调高max_connections的值,让更多的连接进来,会让系统的负载进一步加大,资源耗费在权限验证逻辑上,适得其反,导致已连接线程拿不到CPU资源去执行业务的SQL请求。

1.先处理掉那些占着连接但是不工作的线程

max_connections的计算,不是看谁在running,只要连着就占用一个计数位置。对于不需要保持的连接,可以通过kill connection主动踢掉。
wait_timeout参数,表示一个线程空闲wait_timeout这么多秒之后,就会被MySQL直接断开连接。

sessionA sessionB sessionC
T begin;
insert into t values(1,1);
select * from t where id=1;
T+30s show processlist
id user host db command time state info
4 root localhost:xxxxx test sleep 30 null
5 root localhost:yyyyy test sleep 30 null
6 root localhost:zzzzz test query 0 starting show processlist

会话4、5都是sleep状态,查询information_schema库的innodb_trx表查看事务具体状态:
select * from information_schema.innodb_trx;
其中trx_mysql_thread_id=4;表示id=4的线程处在事务中。还未提交不宜断开,会导致对应事务回滚。

如果连接数过多,优先断开事务外空闲太久的连接;如果还不够,再考虑断开事务内空闲太久的连接。

使用kill connection+id命令从服务器断开连接。一个客户端处于sleep状态时,它的连接被服务器主动断开后,这个客户端不会马上知道,知道客户端发起下一个请求时,才会收到报错”ERROR 2013(HY000):lost connection to MySQL server during query”。

从数据库端主动断开连接可能是有损的,若应用端接收到错误后,不重新连接,而是直接用这个已经不能用的句柄重试查询,导致从应用端看上去”MySQL一直没恢复”。(链接异常断开是常有的事,业务开发的应用代码需要有正确的重连并重试的机制。

2.减少连接过程的消耗

让数据库跳过权限验证阶段:重启数据库,并使用-skip-grant-tables参数启动。整个数据库会跳过所有的权限验证阶段,包括连接过程和语句执行过程。

跳过权限验证风险极高,MySQL8.0使用时会默认把–skip-networking参数打开,让数据库只能被本地的客户端连接。

慢查询性能问题

  1. 上线前,在测试环境,把慢查询日志(slow log)打开slow_query_log(默认关闭),并且把long_query_time设置成0(默认值为10),确保每个语句都会被记录人慢查询日志;
  2. 在测试表里插入模拟线上的数据,做一遍回归测试;
  3. 观察慢查询日志里每类语句的输出,特别留意Rows_examined字段(server层统计值)是否与预期一致。

开源工具pt-query-digest分析。

索引没有设计好

通过紧急创建索引解决。(MySQL5.6版本以后,创建索引支持Online DDL,可以直接执行alter table 语句)。

主库A、备库B:

  1. 在备库B上执行set sql_log_bin=off,也就是不写binlog,然后执行alter table语句加上索引;
  2. 执行主备切换;主库B、备库A。
  3. 在A上执行set sql_log_bin=off,然后执行alter table语句加上索引。

使用gh-ost在线ddl变更。

SQL语句没有写好

MySQL-相同逻辑,不同SQL,性能差异
条件字段函数操作、隐式类型转换、隐式字符编码转换等原因,导致语句没有使用上索引。

通过改写SQL语句来处理。MySQL5.7提供了query_rewrite功能,可以把输入的一种语句改写成另外一种模式。

1
2
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();-- 让插入的新规则生效

查询重写

Query ‘select * from t where id + 1 = 10000 ‘ rewritten to ‘select * from t where id = 10000 - 1’ by a query rewrite plugin;

MySQL选错了索引

MySQL-索引选择异常及处理

应急方案给这个语句加上force index。
使用查询重写功能,给原来的语句佳航force index。

QPS突增问题

下掉一个功能,从数据库端处理:

  1. 由全新业务的bug导致的,确定业务方会下掉这个功能,从数据库端直接把白名单去掉。
  2. 新功能使用的单独的数据库用户,用管理员账号把这个用户删掉,然后断开现有连接。这样新功能的连接不成功,由它引发的QPS就会变成0。

    方案1、2依赖于规范的运维提下:虚拟化、白名单机制、业务账号分离。

  3. 新功能跟主体功能部署在一起,只能通过处理语句来限制,使用查询重写功能,把压力最大的SQL语句直接重写成”select 1”返回。

    如果别的功能也用到了这个SQL语句模板,会有误伤。
    很多业务并不是靠这一个语句就能完成逻辑的,单独改这一个语句,可能会导致后面的业务逻辑一起失败。