MySQL打造扛得住的数据库架构.md-目前只有监控篇

怎甘沉沦 提交于 2020-04-08 04:53:26

[TOC]

MySQL性能管理及架构设计笔记

数据库监控

要监控的内容

  • 对数据库的可用性进行监控: 不是仅仅监控数据库进程是否存在,要通过网络连接到数据库并确定是可用的
  • 对数据库性能进行监控:
  • QPS
  • TPS,
  • 并发线程数量,
  • innnoDB阻塞和死锁
  • 对主从复制进行监控:
  • 主从链路状态,
  • 主从延迟,
  • 主从数据一致性
  • 对服务器资源监控:
  • 磁盘: 且并不意味着磁盘空间大,MySQL能用的就大,因为可能分区分配的不够大.
  • CPU使用率
  • 内存使用情况
  • swap分区使用情况
  • 网络IO使用情况

数据库可用性监控

确认数据库是否可用通过网络正常连接

要注意,如果我们在MySQL本机使用SQL来连接MySQL,这并不意味着外部也能通过tcp/ip协议来访问MySQL,因为外部面临的环境更为复杂.

比如tcp/ip被占满之类的, 所以我们必须通过远程服务器来实际的连接请求:

  • 使用mysqladmin:
# 如果数据库存活,该命令会返回mysqld is alive
~ ⌚ 23:30:42
$ mysqladmin -u root -p -h localhost ping
Enter password:
mysqld is alive
  • 使用Telnet(一般作为手动使用)
# telnet连接成功后,都懂得,只要不是提示连接失败,同时提供给我们可以交互式命令行,那就是成功了
~ ⌚ 23:42:01
$ telnet localhost 3306
Trying ::1...
telnet: connect to address ::1: Connection refused
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
N
5.6.47-log�+_#,Q4Pv!�"Cjl^uPqe*=4mysql_native_password
  • 使用应用程序通过网络建立数据库连接(推荐)
# 比如,如果我们使用PHP来做的话,可以使用PDO来做,连接到MySQL之后,做一下select 1 之类的操作
<?php
$pdo = new PDO('mysql:host=localhost;dbname=mx_bitz_dev', 'root', '');//发送连接
$result = $pdo->query('select 1')->fetchAll();//心跳检测
//$result = $pdo->query('select @@version;')->fetchAll();//也可以这样
$pdo = null;//释放连接
var_dump($result);

/**
[Running] php "/Users/liuhao/Desktop/tmp/pdo.php"
array(1) {
[0]=>
array(2) {
[1]=>
string(1) "1"
[2]=>
string(1) "1"
}
}
[Done] exited with code=0 in 0.103 seconds
*/
  • 确认MySQL是否可以读写

因为说,有可能因为错误的配置,导致打开了MySQL的read_only参数不为off,为on,这时候这个数据库只能读.这种情况一般出现在主从切换的时候,没整好,没有取消从库的只读功能,这就完犊子了.业务程序写不进去,监控也只是select 1;

而像那种单机服务,没有主从的,可以不考虑读写检验这个问题.

这时候我们可以专门为MySQL创建一个简单监控表,比如只有两个字段,一个ID一个time就得了.然后使用我们的监控程序来对该表进行读写,以监控数据库是否可以读写

# 这里就不赘述了,把上面的代码随便一改即可

监控数据库的连接数

有很多情况都会导致打满MySQL连接,比如出现阻塞,缓存穿透之类的.都会导致连接数暴增

所以必须时刻关注连接数的变化

  • 获取MySQL最大连接数
show variables like 'max_connections';
  • 获取当前MySQL的连接数
show global status like 'Threads_connected'
  • 计算报警值
# 如果当前连接数÷最大连接数>0.8,此时必须要报警, 其实以我个人经验来看,该数值>0.5就应该报警,>0.6一般已经完犊子了
Threads_connected / max_connections > 0.8

数据库性能监控

性能监控不用于可用性监控,性能监控更多的是了解性能的变化趋势.

先比执行,性能的监控就没有可用性监控那么的重要.

记录性能监控过程中所采集到的数据库状态

下面的queries,uptime_since_flush_status,Com_Insert,Com_delete都可以通过show global status like 'XXX'来得到,具体干嘛的看名字就知道了,比较特殊的是: uptime_since_flush_status用来获取,代表最近一次使用FLUSH STATUS 的时间(以秒为单位)。FLUSH STATUS 命令主要是用来重置服务器启动后已经同时使用的连接的最大数量(Max_used_connections)

  • 计算QPS(每秒查询次数)
Qps = (queries2 - queries1) / (uptime_since_flush_status2 - uptime_since_flush_status1)

MySQL的qps并不是指的是每个select语句,而是要包括所有的SQL语句,所以我们要对queries进行两次采样,然后利用他们的差值÷两次采样的时间间隔来计算MySQL的QPS

  • 计算TPS(每秒事务数)
TPS=((Com_Insert2+Com_update2+Com_delete2) - 
(Com_Insertl+Com_updatel+Com_deletel)) / 
(Uptime_since_flush_status2-Uptime_since_flush_statusl)

这里我为什么没有记录网上那种通过获取Com_commit的方式来计算,因为显而易见,事务并不可靠.

监控数据库的并发数量

数据库的性能通常会随着并发处理请求数量的增加而下降, 我们在计算的时候也要把cpu的使用率计算在内

  • 数据库并发数量(查看当前数据库正在运行的线程数量)
show global status like 'Threads_connected'

并发处理的数量通常会远小于同一时间连接到数据库的线程数量

要特别注意,比如数据出现了大量的阻塞,就会导致并发数量的激增,高不会瞬间会把可用连接数打满.

  • 监控innodb的阻塞

    Myisam用的是表级锁,一般不会阻塞,这里不考虑,而是通过在优化表查询的时候监控慢查询日志来获取myisam的阻塞情况

    下面就是查看方式:

    注意这里需要root权限information_schema这个库是MySQL自带的库不能授权,且对myisam无效,仅对innodb有效.

    可以准确的抓取到阻塞的线程ID,但是有可能无法抓取到阻塞线程的SQL,因为有可能线程执行了多条SQL,我们去查询的时候,此时SQL已经执行完了.那我们就肯定抓不到了.

    下面的>10秒可以根据自己的业务需要来做,个人建议30秒已经很长了!推荐30秒或者60秒

    SELECT b.trx_mysql_thread_id                              AS '被阻塞线程',
           b.trx_query                                        AS '被阻塞SQL',
           c.trx_mysql_thread_id                              AS '阻塞线程',
           c.trx_query                                        AS '阻塞SQL',
           (unix_timestamp() - unix_timestamp(c.trx_started)) AS '阻塞时间'
    FROM `information_schema`.INNODB_LOCK_WAITS AS a
             join `information_schema`.INNODB_TRX AS b ON a.requesting_trx_id = b.trx_id
             join `information_schema`.INNODB_TRX AS c ON a.blocking_trx_id = c.trx_id
    WHERE (unix_timestamp() - unix_timestamp(c.trx_started)) > 10;
    
  • 验证阻塞查询语句的准确性

需要用到的SQL

# 查看当前session的ID
select connection_id();
# 设置InnoDB事务在放弃前等待行锁的时间(秒)
set global innodb_lock_wait_timeout=120;
# 选择库
use test;
# 开启事务
begin;
# 加锁
select * from user for update;
# 回滚
rollback;
  1. 在session1窗口中执行加锁
mysql> select connection_id();
+-----------------+
| connection_id() |
+-----------------+
|             191 |
+-----------------+
1 row in set (0.00 sec)

mysql> use test;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed

mysql> set global innodb_lock_wait_timeout=120;
Query OK, 0 rows affected (0.00 sec)

mysql> begin;
Query OK, 0 rows affected (0.01 sec)


mysql> select * from user for update;
+----+--------+-----+
| id | name   | age |
+----+--------+-----+
|  1 | liuhao |   0 |
|  2 |        |   0 |
+----+--------+-----+
2 rows in set (6.09 sec)
  1. 在session2中执行加锁,正常情况下session2应该会被session1阻塞
mysql> select connection_id();
+-----------------+
| connection_id() |
+-----------------+
|             192 |
+-----------------+
1 row in set (0.00 sec)

mysql> use test;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed

mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from user for update;
# 此时当前窗口被阻塞

  1. 在session3中查询阻塞情况
mysql> SELECT b.trx_mysql_thread_id                              AS '被阻塞线程',
    ->        b.trx_query                                        AS '被阻塞SQL',
    ->        c.trx_mysql_thread_id                              AS '阻塞线程',
    ->        c.trx_query                                        AS '阻塞SQL',
    ->        (unix_timestamp() - unix_timestamp(c.trx_started)) AS '阻塞时间'
    -> FROM `information_schema`.INNODB_LOCK_WAITS AS a
    ->          join `information_schema`.INNODB_TRX AS b ON a.requesting_trx_id = b.trx_id
    ->          join `information_schema`.INNODB_TRX AS c ON a.blocking_trx_id = c.trx_id
    -> WHERE (unix_timestamp() - unix_timestamp(c.trx_started)) > 10;
+-----------------+-------------------------------+--------------+-----------+--------------+
| 被阻塞线程      | 被阻塞SQL                     | 阻塞线程     | 阻塞SQL   | 阻塞时间     |
+-----------------+-------------------------------+--------------+-----------+--------------+
|             192 | select * from user for update |          191 | NULL      |          127 |
+-----------------+-------------------------------+--------------+-----------+--------------+
1 row in set (0.00 sec)

此时,可以看到正确获取到了阻塞情况,确实也符合我们的实验流程.

这里强调一下,可以准备抓取到阻塞的线程ID,但是有可能无法抓取到阻塞线程的SQL,因为有可能线程执行了多条SQL,我们去查询的时候,此时SQL已经执行完了.那我们就肯定抓不到了.

MySQL主从复制监控

监控主从复制链路

在从库下查看主从复制链路

# 查看从库状态
show slave status;
# 以下两个结果如果不为yes,则认为从库已经完犊子了,复制链路已经挂了
slave_io_running
slave_sql_running

监控主从复制的延迟

  • 简单获取主从延迟:

网络100%稳定的服务器可以这么干.

这种方式并不完全准备,因为这是主库根据主库上的binlog和同步到从库上的重新执行的binlog日志之间的时间差来获取.

当网络出现问题,就会不准确.

# 在主库中查询从库的状态
show slave status
# 通过下面字段来判断,这里会返回一个秒数
seconds_behind_master
  • 完善获取主从延迟:

此时我们需要一个多进程的程序来同时监控master和slave的binglog文件号和binlog偏移量

  • 主库监控
# 查看主库状态
show master 
# 通过下面的状态来判断
file: mysql-bin.00001 # binlog文件号
position: 300000 # binlog偏移量
  • 从库监控

查看当前已经从主传送到从上的二进制binlog日志的名字和偏移量

# 查看从库状态
show slave status
master_log_file: mysql-bin.0001 # binlog文件号
read_master_log_pos: 300000 # binlog偏移量
  • 已经完成的主上的二进制日志的名字和偏移量
exec_master_log_pos:300000
relay_log_space:300001

此时对比上面三个返回值, 如果他们的文件名相同,而且偏移量也一致的话说明主从不存在任何的延迟.

主从数据一致性检查

注意: 功能我没有测试验证, 仅仅就是学了一下而已

如果主从出现问题, 在我们修复完毕主从之后,需要对主从数据进行检测

使用pt-table-checksum工具来做自动检测

# 只需要在主库中运行即可,他会自动发现主库下的所有从库信息,并对所有的从库的指定的数据信息进行检测
pt-table-checksum u=用户名,p='密码'\
--databases 库名\
--replicate test.checksums # 在test库下,创建checksums表,并且将数据写入到checksum表中
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!