MySQL主从复制(六):数据库是否可用

select 1判断


场景示例:

-- 设置innodb并发度为3,从而限制并发查询
set global innodb_thread_concurrency=3;

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

-- 插入1行数据 insert into t values(1,1)
insert into t values(1,1);

查询blocked:

设置innodb_thread_concurrency参数的目的是, 控制InnoDB的并发线程上限。 也就是说,一旦并发线程数达到这个值, InnoDB在接收到新请求的时候, 就会进入等待状态, 直到有线程退出。

上图中,前三个session 中的sleep(100), 使得这三个语句都处于“执行”状态, 以此来模拟大查询。

session D里面, select 1是能执行成功的, 但是查询表t的语句会被堵住。 也就是说, 如果这时候我们用select 1来检测实例是否正常的话, 是检测不出问题的。

通常情况下, 建议把innodb_thread_concurrency设置为64~128之间的值。

问1:并发线程数设置为128够干啥, 线上的并发连接数动不动就上千了?

答:产生这个疑问的原因, 是搞混了并发连接和并发查询。

并发连接和并发查询, 并不是同一个概念。 你在show processlist的结果里, 看到的几千个连接, 指的就是并发连接。 而“当前正在执行”的语句, 才是我们所说的并发查询。

并发连接数达到几千个影响并不大, 就是多占一些内存而已。 我们应该关注的是并发查询, 因为并发查询太高才是CPU杀手。 这也是为什么我们需要设置innodb_thread_concurrency参数的原因。

问2:如果把innodb_thread_concurrency设置为128的话, 那么出现同一行热点更新的问题时, 是不是很快就把128消耗完了, 这样整个系统是不是就挂了呢?

答:在线程进入锁等待以后,并发线程的计数会减一,也就是说等行锁(也包括间隙锁)的线程是不算在128里面的。MySQL这样设计是非常有意义的。 因为, 进入锁等待的线程已经不吃CPU了。

问3:为什么进入锁等待的线程不吃CPU可以避免整个系统锁死?

答:假设处于锁等待的线程也占并发线程的计数,可以设想一下这个场景:

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

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

3)如果处于锁等待状态的线程计数不减一, InnoDB就会认为线程数用满了, 会阻止其他语句进入引擎执行(包括commit), 这样线程1不能提交事务。 而另外的128个线程又处于锁等待状态, 整个系统就堵住了。

系统锁死状态图:

这时候InnoDB不能响应任何请求, 整个系统被锁死。 而且, 由于所有线程都处于等待状态, 此时占用的CPU却是0, 而这明显不合理。 所以, 我们说InnoDB在设计时, 遇到进程进入锁等待的情况时, 将并发线程的计数减1的设计, 是合理而且是必要的。

注:虽然说等锁的线程不算在并发线程计数里, 但如果它在真正地执行查询, 就比如我们上面例子中前三个事务中的select sleep(100) from t, 还是要算进并发线程的计数的。

因此上述select 1示例中,同时在执行的语句超过了设置的innodb_thread_concurrency的值, 这时候系统其实已经不行了, 但是通过select 1来检测系统, 会认为系统还是正常的。

总结:

  • Select 1判断:定期执行select 1成功返回,表示DB是连通的。
  • 使用场景:主要用于检测MySQL连通性。
  • 存在问题:不完善,如果使用select 1检测DB的可用性,可能出现DB连接畅通,但引擎层并发压力已达上限,导致DB不可用。
  • 解决办法:使用查表判断方案可解决该问题。

查表判断


为了能够检测InnoDB并发线程数过多导致的系统不可用情况, 我们需要找一个访问InnoDB的场景。 一般的做法是, 在系统库(mysql库) 里创建一个表, 比如命名为health_check, 里面只放一行数据, 然后定期执行:

select * from mysql.health_check;

使用这个方法, 我们可以检测出由于并发线程过多导致的数据库不可用的情况。

问:如果空间满了,使用该方法检测数据库是否可用,是否有问题?

答:更新事务要写binlog, 而一旦binlog所在磁盘的空间占用率达到100%, 那么所有的更新语句和事务提交的commit语句就都会被堵住。 但是, 系统这时候还是可以正常读数据的。也就是说,此时该方法无法检测数据库是否是可用的。

因此,需要把查询语句改为更新语句。

总结:

  • 查表判断:在系统库(mysql库)里创建一个表,如health_check,插入一行数据,定期执行查询语句:select * from mysql.health_check;
  • 使用场景:能够检测InnoDB并发线程数过多导致的系统不可用情况。
  • 存在问题:不完善,磁盘问题无法检测到,可能读请求正常,但写请求已阻塞;比如:写binlog,磁盘满了以后,所有事务更新语句都被阻塞;但读请求正常。此时无法检测到DB不可用。
  • 解决方案:使用更新判断方案可解决该问题。

更新判断(推荐使用)


既然要更新, 就要放个有意义的字段, 常见做法是放一个timestamp字段, 用来表示最后一次执行检测的时间。 这条更新语句类似于:

update mysql.health_check set t_modified=now();

注1:节点可用性的检测应该包含主库和备库。 如果用更新来检测主库的话, 那么备库也要进行更新检测。

注2:对于双M结构(A、B互为主备),由于备库B也要写binlog,如果主库A和备库B都用相同的更新命令,则可能出现行冲突,即可能导致主备同步停止。因此mysql.health_check 这个表就不能只有一行数据了。

为了让主备之间的更新不产生冲突, 我们可以在mysql.health_check表上存入多行数据, 并用A、 B的server_id做主键。

CREATE TABLE `health_check` (
 `id` int(11) NOT NULL,
 `t_modified` timestamp NOT NULL DEFAULTCURRENT_TIMESTAMP,
 PRIMARY KEY (`id`)
) ENGINE=InnoDB;

-- 检测命令
insert into mysql.health_check(id, t_modified) values (@@server_id, now()) onduplicate key update t_modified=now();

由于MySQL规定了主库和备库的server_id必须不同,这样就可以保证主、 备库各自的检测命令不会发生冲突。

虽然该方案是比较常用的一种方案,但仍存在“判定慢”问题。

问:更新语句, 如果失败或者超时, 就可以发起主备切换了, 为什么还会有判定慢的问题呢?

这个问题主要涉及的是服务器IO资源分配问题。

所有的检测逻辑都需要一个超时时间N。 执行一条update语句, 超过N秒后还不返回, 就认为系统不可用。

假设一个日志盘的IO利用率已经是100%的场景。 这时候, 整个系统响应非常慢, 已经需要做主备切换了。而我们的检测使用的update命令, 需要的资源很少, 所以可能在拿到IO资源的时候就可以提交成功, 并且在超时时间N秒未到达之前就返回给了检测系统。

检测系统一看, update命令没有超时, 于是就得到了“系统正常”的结论。

之所以会出现这个现象,根本原因是我们上面说的所有方法,都是基于外部检测的。而外部检测天然有一个问题:随机性。

因为, 外部检测都需要定时轮询, 所以系统可能已经出问题了, 但是却需要等到下一个检测发起执行语句的时候, 才有可能发现问题。 而且, 如果你的运气不够好的话, 可能第一次轮询还不能发现, 这就会导致切换慢的问题。

总结:

  • 更新判断:在系统库(mysql库)里创建一个表,如health_check,包含一个timestamp字段,表示最后一次执行检测的时间,插入一行数据,定期执行更新语句:update mysql.health_check set t_modified=now();
  • 使用场景:能够检测InnoDB磁盘无法写入的情况。
  • 存在问题:不完善,无法检测语句执行慢但不超时的场景;如磁盘IO打满,但仍然可以响应请求的场景。
  • 解决方案:使用MySQL内部统计判断方案即可解决该问题。

 内部统计(推荐使用)


MySQL 5.6版本以后提供的performance_schema库, 在file_summary_by_event_name表里统计了每次IO请求的时间。

file_summary_by_event_name表里有很多行数据, 我们先来看看event_name='wait/io/file/innodb/innodb_log_file’这一行。

图中这一行表示统计的是redo log的写入时间, 第一列EVENT_NAME 表示统计的类型。

接下来的三组数据, 显示的是redo log操作的时间统计:

  • 第一组五列,是所有IO类型的统计。其中,COUNT_STAR是所有IO的总次数, 接下来四列是具体的统计项, 单位是皮秒; 前缀SUM、 MIN、 AVG、 MAX, 顾名思义指的就是总和、 最小值、平均值和最大值。
  • 第二组六列,是读操作的统计。 最后一列SUM_NUMBER_OF_BYTES_READ统计的是, 总共从redo log里读了多少个字节。
  • 第三组六列,统计的是写操作。

最后的第四组数据, 是对其他类型数据的统计。 在redo log里, 你可以认为它们就是对fsync的统计。

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

注1:因为我们每一次操作数据库, performance_schema都需要额外地统计这些信息, 所以我们打开这个统计功能是有性能损耗的。

注2:如果打开所有的performance_schema项, 性能大概会下降10%左右。 所以,建议只打开自己需要的项进行统计。

打开或者关闭某个具体项的统计:如果要打开redo log和binlog的时间监控, 你可以执行这个语句:

update setup_instruments set ENABLED='YES', Timed='YES' where name like '%wait/io/file/sql/binlog%';
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毫秒属于异常, 然后使用类似下面这条语句作为检测逻辑。

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;

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

truncate table performance_schema.file_summary_by_event_name;

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

  • 28
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
MySQL数据库主从复制是一种用于解决MySQL单点故障和提高整体服务性能的技术。它的原理是通过将主数据库的更新操作复制到从数据库,使得从数据库可以保持与主数据库的数据一致性。[1] 在实际的生产环境中,主从复制被广泛应用于增加MySQL可用性和性能。通过配置主从复制,可以实现数据的备份和读写分离,从而提高整体的系统性能。 主从复制MySQL中具有很多优势,它可以提供高可用性、高性能和灵活性。通过主从复制,可以简化MySQL的部署过程,并根据不同的业务需求进行灵活的调整。 总结来说,MySQL数据库主从复制是一种解决单点故障和提高性能的技术,它通过复制主数据库的更新操作到从数据库来实现数据的备份和读写分离。这种技术可以提供高可用性、高性能和灵活性。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [MySQL数据库——主从复制](https://blog.csdn.net/qq_41808387/article/details/107009748)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* [MySQL主从复制](https://blog.csdn.net/m0_62473957/article/details/124140928)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

数据库内核

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值