主备切换有两种场景,一种是主动切换,一种是被动切换。被动切换,往往是因为主库出问题了,由 HA 系统发起的。那么怎么判断一个主库出问题了?
select 1
判断实际上,select 1 成功返回,只能说明这个库的进程还在,并不能说明主库没问题
设置 innodb_thread_concurrency 控制 InnoDB 的并发线程上限。也就是说,一旦并发线程数达到这个值,InnoDB 在接收到新请求的时候,就会进入等待状态,直到有线程退出。建议把 innodb_thread_concurrency 设置为 64~128 之间的值。
show processlist 的结果里,看到的几千个连接,指的就是并发连接。而“当前正在执行”的语句,才是我们所说的并发查询。
并发连接数大会多占一些内存,连接释放不掉,容易导致数据库内存不足,无法接收新的请求。
这里我们关注的是并发查询,因为并发查询太高是 CPU 杀手。这也是为什么我们需要设置 innodb_thread_concurrency 参数的原因。
InnoDB 在设计时,遇到进程进入锁等待的情况时,将并发线程的计数减 1 的设计,是合理而且是必要的。因为,进入锁等待的线程已经不吃 CPU 了;更重要的是,必须这么设计,才能避免整个系统锁死。
查表判断
为了能够检测 InnoDB 并发线程数过多导致的系统不可用情况,我们需要找一个访问 InnoDB 的场景。
一般的做法是,在系统库(mysql 库)里创建一个表,比如命名为 health_check,里面只放一行数据,然后定期执行查询。可以检测出由于并发线程过多导致的数据库不可用的情况。
更新事务要写 binlog,当binlog 所在磁盘的空间占用率达到 100%,所有的更新语句和事务提交的 commit 语句就都会被堵住。但是,系统这时候还是可以正常读数据的。
更新判断
既然要更新,就要放个有意义的字段,常见做法是放一个 timestamp 字段,用来表示最后一次执行检测的时间。
如果用更新来检测主库的话,那么备库也要进行更新检测。双 M 结构,如果主库 A 和备库 B 都用相同的更新命令,就可能出现行冲突,也就是可能会导致主备同步停止。所以 mysql.health_check 这个表就不能只有一行数据。可以mysql.health_check 表上存入多行数据,并用 A、B 的 server_id 做主键。
更新判断是一个相对比较常用的方案了,不过依然存在一些问题。其中,“判定慢”一直是让 DBA 头疼的问题。检测使用的 update 命令,需要的资源很少,所以可能在拿到 IO 资源的时候就可以提交成功,并且在超时时间 N 秒未到达之前就返回给了检测系统。
内部统计
MySQL 5.6 版本以后提供的 performance_schema 库,就在 file_summary_by_event_name 表里统计了每次 IO 请求的时间。每一次操作数据库,performance_schema 都需要额外地统计这些信息,这个统计功能是有性能损耗的。如果打开所有的 performance_schema 项,性能大概会下降 10% 左右。
先看看file_summary_by_event_name表里面的
event_name='wait/io/file/innodb/innodb_log_file’这一行。
第一列 EVENT_NAME 表示统计的类型。COUNT_STAR 是所有 IO 的总次数,接下来四列是具体的统计项, 单位是皮秒;前缀 SUM、MIN、AVG、MAX,顾名思义指的就是总和、最小值、平均值和最大值。
打开 redo log 的时间监控
mysql> update setup_instruments set ENABLED='YES', Timed='YES'
where name like '%wait/io/file/innodb/innodb_log_file%';
可以设定阈值,单次 IO 请求时间超过 200 毫秒属于异常,然后使用类似下面这条语句作为检测逻辑。
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;
把之前的统计信息清空。后面的监控再次出现这个异常,可以加入监控累积值了。