目录
- select1判断
- 查表判断
- 更新判断
- 外部检测弊端
- 内部统计
一主一备的双M架构里,主备切换只需要把客户端流量切换到备库。
在一主多从的架构里,主备切换要把客户端流量切换到备库,也需要把从库接到新主库上。
切换有两种场景:1、主动切换 2、被动切换。
被动切换是由于主库出问题了,下面是几种判断主库出问题的方法:
select1判断
select 1 成功返回,说明这个库的进程孩子啊,但是不能说明主库没问题。
如果在执行语句超过了设置的innodb_thread_concurrency
,此时select 1语句是成功的,但是CPU不能满足线程查询要求了,新的查询要求进来只会阻塞。
查表判断
在系统库(mysql库)里创建一个表,命名为health_check,里面只放一行数据,然后定期执行语句:
select * from mysql.headlth_check;
使用这个方法,可以检测出由于并发线程过多导致的数据库不可用的情况。
但是这个方法也会有一个问题:空间满了,失效
更新事务要写binlog,一旦binlog所在的磁盘空间占用率达到100%,那么所有的更新语句和事务提交commit语句就会堵住。但是系统此时是可以正常读数据的。
更新判断
放一个timestamp字段,用来表示最后一次执行检测的时间:
update mysql.health_check set t_modified = now();
这种节点可用性检测应该包含主库和备库。
备库检测也要写binlog, 一般只能主库写,备库不能写。这里是为了检测备库,所以才能写,但是binlog是双M结构,所以会互相同步这一个检测sql,那么有可能会造成主从数据不一致(t_modified内容不同)
如果主库A和备库B都用相同的更新命令,就可能出现行冲突,从而导致主备同步停止。
我们可以在表上存入多行数据,用A,B的server_id做主键:
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();
MySQL规定了主库和备库的server_id必须不同,就可以保证主、备库各自的检测命令不会发生冲突
这种方法仍然存在问题:判定慢
所有的检测逻辑都需要一个超时时间N。即执行一条update语句,超过N秒后还不返回就认为系统不可用。
如果一个日志盘的IO利用率已经是100%的场景,此时系统响应非常慢,已经需要做主备切换了。
我们检测使用的update命令由于需要的资源比较少,很可能在拿到IO资源的时候就可以提交成功,并且在超时时间N秒未到达之前就返回给检测系统。所以update命令没有超时,然后得到系统正常的错误结论。
外部检测弊端
上面的三种方法都是基于外部检测的。外部检测天然有个问题,就是随机性。
外部检测都需要定时轮询,所以系统可能已经出现问题了,但是却要等到下一个检测语句执行的时候才能发现问题。运气不好,第一次轮询还不能发现。
内部统计
MySQL5.6版本后提供了performance_schema
库,在file_summary_by_event_name
表里统计每次IO请求的时间。
该行数据统计的是redo log的写入时间。
COUNT_STAR是所有IO总次数。
前缀SUM、MIN、AVG、MAX,指统计项的总和、最小值、平均值、最大值。
SUM_NUMBER_OF_BYTES_READ 统计总共从redo log里读了多少字节
需要注意的是,每进行一次统计,是由性能损耗的,所以建议只打开自己需要的项进行统计。
如打开 redo log 的时间监控:
mysql> update setup_instruments set ENABLED='YES', Timed='YES' where name like '%wait/io/file/innodb/innodb_log_file%';
然后通过MAX_TIMER值来判断数据库是否出现问题。可以设定阈值,单次IO请求时间超过200ms属于异常,然后使用类似于下面的语句检测:
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;
发生异常后,取得需要数据后再:
mysql> truncate table performance_schema.file_summary_by_event_name;
把之前的统计信息清空。