[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;
- 在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)
- 在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;
# 此时当前窗口被阻塞
- 在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表中
来源:oschina
链接:https://my.oschina.net/chinaliuhan/blog/3224036