22-MySQL有哪些“饮鸩止渴”提高性能的方法?
# 短连接风暴
Mysql 建立连接的过程开销是很大的,除了 3 次握手以外,还需要做登录判断和获取这个连接的数据读取权限。
- 在 MySQL 压力小的时候,这些开销无所谓。
- 如果 MySQL 压力大了,那么一旦处理的速度慢了,很快就会达到 max_connections 设置的参数而返回 Too Many Connetions 的报错。
一味的修改 max_connections 的值并无意义,随着这个值的增大也会使得硬件资源被快速消耗,可能会出现一个线程获取了连接但是审批不到 CPU 资源。
有两种解决方案,但是都是有损的。
- 先处理那些占着连接但是不工作的线程。
- 减少连接过程的消耗。
# 方案一
因为 max_connections 的计算是只要你连接成功了不管工没工作都 + 1。所以可以通过 kill connection
踢掉,这个和 wait_timeout 参数是一样的,一个线程空闲多少秒后就踢出。
Session A | Session B | |
---|---|---|
T | begin; insert into t values(1,1,1) | select * from t where id = 1; |
T+30S |
问题是如果说 kill 的是 A 的连接,那么因为没提交,数据库必须要按回滚来处理,所以数据丢失了,但是 kill B 却没什么关系,但如何准确找到 SessionB 呢?
通过 select * from information_schema.innodb_trx\G;
可以看到 2516 这个 ID 还在事务中,因为上面说了,如果是连接数过多,可以优先断开事务外空闲太久的连接;如果这样还不够,再考虑断开事务内空闲太久的连接。
通过命令断开后客户端并不会有什么提示,但是会在下次请求的是时候收到一个 ERROR 2013 (HY000): Lost connection to MySQL server during query 的错误提示,但是你也会发现,我在程序中并没收到错误啊,以 SpringBoot 来说,配置的数据库连接驱动或者连接池什么的会做重试的动作。
# 方案二
通过–skip-grant-tables 参数,可以跳过权限验证阶段包括连接过程和语句执行过程。但是在 MySQL8 的版本中如果开启了这个参数会自动打开 --skip-networking,这个参数意味着只能让本机客户端访问。
# 慢查询导致的性能问题
索引没有设计好
- 这种方式需要通过 alter table 来解决。
- 推荐通过主备来切换首先在备库上执行 set sql_log_bin=off 来关闭 binlog 的写入,执行 alter table;切换主从;然后在新备库中执行同样的操作。
语句没写好
- 在前面 18 章有过讲解。
- MySQL5.7 之后也提供了重写的方案。比如说 select * from t where c + 1 =10;
- 重写语句:
insert into query_rewrite.rewrite_rules(pattern, replacement, pattern_database) values ("select * from t where c + 1 =?;", "select * from t where c =10-?;", "db1");
。 - 生效:
call query_rewrite.flush_rewrite_rules();
。
MySQL 索引选错
- 在第 10 章有过提示。
- 通过加上 force index 来解决这个问题。
# QPS 突增问题
有时候业务突然遇到高峰、或者遇到程序 BUG 导致连接数突然增加。
如果是程序 BUG 导致的,而且这个是一个全新的业务,并且 DB 运维也是比较规范的,每个业务都是一个一个加入白名单的,那么可以直接把这个新的业务从白名单移除。
如果这个全新的业务的使用只针对了一部分新创建的用户,那么可以把这些用户从 MySQL 中移除。
如果揉在一起了,就只能用上面说的这种语句重写的方式,将语句重写成为
select 1;
。- 但是这个语句的风险很高
- 如果其他地方也用到了这个语句,重写会误伤。
- 这个才是最致命的,如果重写成
select 1;
很多业务是需要多个分支业务来支撑的,比如订单扣库存扣积分来说,一旦扣积分被重写,那么会其他的业务的不正常。
- 但是这个语句的风险很高