29 如何判断一个数据库是不是出了问题
在主备环境中,必不可少的是出现主从的切换,一种是主动切换,一种是被动切换,往往是因为主库出了问题,由HA系统发起的。
怎么判断一个主库出现了问题了呢
select 1 判断
实际上,select 1成功返回,只能说明这个库的进程还在,并不能说明主库没问题,下面一个场景
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)
SESSION A |
SESSION B |
SESSION C |
SESSION D |
select sleep(100) from t; |
select sleep(100) from t; |
select sleep(100) from t; |
|
select 1;(query ok) select *from t; (blocked) |
这里设置innodb_thread_concurrency参数的目的是,控制innodb的并发线程上限,也就是说,一旦并发线程达到这个值,innodb在接收到请请求的时候,就会进入等待状态,直到有线程退出。
这里设置为3,表示innodb只允许3个线程并行执行,前3个都是sleep(100),使得这3个语句都处于”执行状态”,依次来模拟大查询,在session d里面,select 1 是成功的,但是查询表t 却被堵塞了,就是说,这时候用select 1来检测数据库是否正常的话,是检测不出问题的。
在innodb中,innodb_thread_concurrency这个参数的值默认是0,表示不限制并发线程数量,但是,不限制是肯定不行的,因为,一个机器的cpu核数有限,线程全冲进来,上下文切换的成本就会太高。
通常我们建议,把这个参数设置为64-128之间,也就是64-128个并发线程,这里区分并发线程和并发连接
在show processlist中,看到的是连接,并发连接,而”当前正在执行”的语句,才是这里的并发查询。
并发连接数达到几千个影响并不大,就是多占一些内存而已,而并发查询高才是cpu的杀手,这就是要限制这个参数的原因。
实际上,在现成计入锁等待以后,并发线程的计数就会减1,也就是说等行锁(包括间隙锁)的线程是不算在这128个线程里面的。
查表判断
为了能够检测innodb并发线程数过多导致的系统不可用情况,我们需要访问innodb的场景,一般做法是在系统库(mysql)里创建一个表,里面只放一行数据,然后定期执行;
select *from mysql.health_check
使用这个方法,可以检测出由于并发线程数过多导致的数据库不可用的情况。
但是又会碰到另外的一个问题,即空间满了以后,当更新事务要写binlog,一旦binlog所在的磁盘的空间占用率达到100%,那么所有的更新语句和事务提交的commit语句就会被堵住,但是,这时候系统时可以正常查询
更新判断
常见做法是放一个timestamp字段,来表示更新执行检测的时间。
update mysql.health_check set t_modified=now();
节点可用性的检测应该包含主库和备库,如果用更新来检查主库,那么备库也要更新检测
CREATE TABLE `health_check` (
`id` int(11) NOT NULL,
`t_modified` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
/* 检测命令 */
insert into mysql.health_check(id, t_modified) values (@@server_id, now()) on duplicate key update t_modified=now();
更新判断是一个比较常用的方案,不过依然存在一些问题,其中,判断慢这个问题不好处理,更新语句,如果失败或者超时时,就可以发起主备切换,为什么还要判断慢的问题呢
这里涉及到服务器IO资源分配的问题。
首先,所有的检测逻辑都需要一个超时时间N,执行一条update语句,超过N秒后还不返还,就认为系统不可用
当一个日子盘的io利用率已经是100%的场景,这时候,整个系统响应就比较慢,已经需要做主备切换了。
但是要知道,io利用率100%表示系统的io是在工作的,每个请求都有机会获得io资源,执行自己的任务,而我们的检测使用的update命令,需要的资源很少,所以可能在拿到io资源的时候就可以提交成功,并且在超时时间n秒未达到就返回检测系统
检测系统一看,update命令没有超时,就得出系统时正常的结论。
上面说的都是基于外部的检测,随机性。
内部统计
针对磁盘利用率这个问题,如果mysql可以告诉我们,内部每一次io请求时间,就可以判断数据是否出问题的方法就可靠
在mysql 5.6版本,提供了performance_schema库,表
performance_schema.file_summary_by_event_name 表里统计了每次io请求的时间
event_name='wait/io/file/innodb/innodb_log_file'
图中这一行表示统计的是redo log的写入时间,event_name表示统计类型
接下来的三组数据,显示的是redo log操作的时间统计
COUNT_STAR所有io的总次数,单位皮秒,SUMMINAVGMAX,总和,最小值,平均值,最大值
COUNT_READ 读操作
COUNT_WRITE 写操作
COUNT_MISC 对其他类型数据的统计,在redo log里,可以认为就是对fsync的统计
表里binlog对应的是event_name=’wait/io/file/sql/binlog’这一行
因为每一次操作数据,performance_schema都需要额外的统计这些信息,所以打开这个功能是有损耗的
如果打开所有的performance_schema选项,性能大概会下降10%左右。
需要打开redo log的时间监控
mysql> update setup_instruments set ENABLED='YES', Timed='YES' where name like '%wait/io/file/innodb/innodb_log_file%';
假设已经开启 了redo log和binlog的统计信息,可以通过MAX_TIMER的值来判断数据库是否出现了问题,可以设定一个阈值,单次请求超过200毫秒就属于异常
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;
发现异常后,取到需要的信息,再
mysql> truncate table performance_schema.file_summary_by_event_name;
把之前的信息情况。
在MHA中,默认的方法就是select 1来检查
优先的方案是使用update更新系统表,在配合增加检测performance_schema的信息。