MySQL 29 如何判断一个数据库是不是出问题了?

之前的文章已经说过:在一主一备的双M架构里,主备切换只需要把客户端流量切到备库;而在一主多从架构里,主备切换除了要把客户端流量切到备库外,还需要把从库接到新主库上。

主备切换有两种场景,分别是主动切换和被动切换,其中被动切换往往是因为主库出问题由HA系统发起。那么怎么判断主库出问题了呢?

select 1判断

select 1成功返回,能说明这个库的进程还在,但不能说明主库没有问题。比如如下场景:

sql 复制代码
set global innodb_thread_concurrency=3;

CREATE TABLE `t` (
  `id` int(11) NOT NULL,
  `c` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB;

 insert into t values(1,1)

innodb_thread_concurrency参数的作用是控制InnoDB并发线程上限。当并发线程数达到这个值,InnoDB在接收新请求时就会进入等待状态,直到有线程退出。

所以场景里session D的select 1能执行成功,但实际实例已无法查询。

InnoDB将该参数的默认值设为0,但不限制并发线程数实际上是不行的,因为一个机器的CPU核数有限,线程全进来会有很大的上下文切换成本。通常情况下,建议把该值设为64-128之间的值。

这里可能会产生几个疑问。第一,线上的并发连接数动不动就上千了,设置为128有什么用?

这是搞混了并发连接和并发查询。在show processlist结果里,看到的几千个连接指的是并发连接,而当前正在执行的语句才是并发查询。并发连接数达到几千个影响并不大,只是多占一些内存,而并发查询高会造成CPU占用高,因此需要加以控制。

第二,当出现同一行热点更新,由于会等待锁,128是否会很快消耗完,系统是否会挂?

这也是不会的,在线程进入锁等待后,并发线程的计数会减1,即等行锁(包括间隙锁)的线程是不算在128里的。

这个设计是必须的,否则可能整个系统锁死。假设处于锁等待的线程也占并发线程的计数,设想:

  • 线程1执行begin;update t set c=c+1 where id=1,启动事务trx1,然后保持该状态。此时线程处于空闲状态,不算在并发线程里;

  • 线程2到线程129执行update t set c=c+1 where id=1,由于等行锁而进入等待状态,这样就有128个线程处于等待状态;

  • 如果处于锁等待状态的线程计数不减1,InnoDB会认为线程数用满,阻止其他语句进入引擎执行,那么线程1将不能提交事务,而其他128个线程又处于锁等待,整个系统堵住。

该例子里InnoDB不能响应任何请求,但CPU占用却是0,明显不合理。

而如果真正执行查询,比如之前例子三个事务的select sleep(100) from t,是要算进并发线程的计数的。

该部分主要说明,使用select 1会有问题,因此使用select 1进行判断的逻辑需要修改。

查表判断

为了能检测并发线程数过多导致的系统,需要找一个访问InnoDB的场景。可以在系统库里建一个表(比如命名为check),里面只放一行数据,然后定期执行:

sql 复制代码
select * from mysql.check;

使用该方法,可以检测出由于并发线程过多导致的数据库不可用的情况。但假如binlog所在磁盘的空间占用率达到100%,那么所有更新语句和事务提交的commit语句都会被堵住,但系统仍能正常读数据,此时这种方法失效。

更新判断

可以把上面的查询改为更新,比如:

sql 复制代码
update mysql.check set t_modified=now();

节点可用性检测包括主库和备库,由于一般会把主库A和备库B的主备关系设为双M结构,所以在备库B上执行的检测命令也要发回给主库A,此时如果都用相同的更新命令就可能出现行冲突,导致主备同步停止。所以,此时表里不能只有一行数据:

sql 复制代码
mysql> CREATE TABLE `check` (
  `id` int(11) NOT NULL,
  `t_modified` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB;

/* 检测命令 */
insert into mysql.check(id, t_modified) values (@@server_id, now()) on duplicate key update t_modified=now();

更新判断存在"判定慢"的问题。所有的检测逻辑都需要一个超时时间N,执行一条更新语句,超过N秒后还不返回,就认为系统不可用。设想一个日志盘的IO利用率已经是100%的场景,此时整个系统响应很慢,已经需要做主备切换了。但我们检测使用的update命令需要的资源很少,是有可能在拿到IO资源时提交成功,并且在超时时间N秒未到达之前就返回给检测系统的。

那么就会出现:业务系统上正常SQL已经执行很慢,但检测系统由于update命令没超时得到"系统正常结论的情况。

之所以出现这个现象,根本原因是上面说的方法都是基于外部检测的,天然有随机性的问题,因为外部检测都需要定时轮询,所以系统可能已经出问题了,但是却需要等到下一个检测发起执行语句的时候,才有可能发现问题。而且,如果运气不好,可能第一次轮询还不能发现,就会导致切换慢的问题。

内部统计

针对磁盘利用率的问题,如果MySQL可以告诉我们内部每一次IO请求的时间,那判断数据库是否出问题的方法就会可靠许多。

MySQL 5.6版本以后提供的performance_schema库,就在file_summary_by_event_name表里统计了每次IO请求的时间。该表里有很多行数据,先看event_name='wait/io/file/innodb/innodb_log_file'这一行:

该行表示统计的是redo log的写入时间,第一列EVENT_NAME表示统计的类型,接下来三组数据显示的是redo log操作时间统计。

第一组五列是所有IO类型的统计,COUNT_STAR是所有IO的总次数,下面四列是具体统计项。

第二组六列是读操作的统计,SUM_NUMBER_OF_BYTES_READ统计的是总共从redo log里读了多少字节。

第三组六列是写操作的统计,第四组是对其他类型数据的统计。

在performance_schema库的file_summary_by_event_name表里,binlog对应的是event_name = "wait/io/file/sql/binlog"这一行。各个字段的统计逻辑,与redo log的各个字段完全相同。

由于每次操作数据库,performance_schema都需要额外统计这些信息,所以打开这个统计功能是有性能损耗的,建议只打开自己需要的项进行统计。可以通过下面的方法打开或关闭某个具体项的统计,比如要打开redo log的时间监控:

sql 复制代码
mysql> update setup_instruments set ENABLED='YES', Timed='YES' where name like '%wait/io/file/innodb/innodb_log_file%';

假设已经开启了redo log和binlog两个统计信息,如何把这个信息用到实例状态诊断上呢?

可以通过MAX_TIMER的值来判断数据库是否出问题。比如设定阈值,单次IO请求时间超过200毫秒属于异常,然后使用类似下面这条语句作为检测逻辑:

sql 复制代码
mysql> select event_name,MAX_TIMER_WAIT  FROM performance_schema.file_summary_by_event_name where event_name in ('wait/io/file/innodb/innodb_log_file','wait/io/file/sql/binlog') and MAX_TIMER_WAIT>200*1000000000;

发现异常后,取到需要的信息,再通过下面的语句:

sql 复制代码
mysql> truncate table performance_schema.file_summary_by_event_name;

把之前的统计信息清空。这样如果后面的监控中,再次出现这个异常,就可以加入监控累积值了。

相关推荐
流火无心1 小时前
mysql索引 底层数据结构与算法
数据结构·mysql·索引
lhxcc_fly1 小时前
Mysql系列--9、事务
数据库·mysql·事务
XYiFfang4 小时前
【mysql】SQL 中 IS 与 = 的区别:一个 NULL 值引发的思考
数据库·sql·mysql
one year.5 小时前
MySQL表的约束
数据库·mysql
only_Klein6 小时前
mysql双机热备(主主模式)
数据库·mysql·adb
共享家95277 小时前
MySQL-内置函数
数据库·mysql
得意霄尽欢7 小时前
MySQL底层数据结构与算法浅析
数据库·mysql
1688red7 小时前
openEuler系统实现MySQL数据库主从复制
数据库·mysql
草履虫建模9 小时前
若依微服务一键部署(RuoYi-Cloud):Nacos/Redis/MySQL + Gateway + Robot 接入(踩坑与修复全记录)
redis·mysql·docker·微服务·云原生·nacos·持续部署