详细的MySQL优化—oldguo导师
一、优化介绍
准备一台MySQL的虚拟机环境,最好修改为4G内存,4核CPU
启动3306节点的 mysql,并导入t100w的库
t100w库 共享地址 提取密码:999
1. 环境准备
cpu:4 , mem:8G , mysql:5.7.26全新环境
2. 压力测试准备
/etc/init.d/mysqld start
mysql < t100w.sql
mysqladmin -uroot -p password 123
mysqlslap --defaults-file=/etc/my.cnf \
--concurrency=100 --iterations=1 --create-schema='test' \
--query="select * from test.t100w where k2='FGCD'" engine=innodb \
--number-of-queries=2000 -uroot -p123 -verbose
3. 优化哲学
3.1 为什么
`为了获得成就感?
为了证实比系统设计者更懂数据库?
为了从优化成果来证实优化者更有价值?
NO!NO!NO!NO!NO!NO!
但通常事实证实的结果往往会和您期待相反!
优化有风险,涉足需谨慎!`
3.2 优化风险
`优化不总是对一个单纯的环境进行!还很可能是一个复杂的已投产的系统。
优化手段本来就有很大的风险,只不过你没能力意识到和预见到!
任何的技术可以解决一个问题,但必然存在带来一个问题的风险!
对于优化来说解决问题而带来的问题控制在可接受的范围内才是有成果。
保持现状或出现更差的情况都是失败!
稳定性和业务可持续性通常比性能更重要!
优化不可避免涉及到变更,变更就有风险!
优化使性能变好,维持和变差是等概率事件!
优化不能只是数据库管理员担当风险,但会所有的人分享优化成果!
所以优化工作是由业务需要驱使的!!!`
3.3 谁参与
`DBA—数据库管理员(系统,硬件,网路,存储,代码,架构思维)
数据库管理员
业务部门代表
应用程序架构师
应用程序设计人员
应用程序开发人员
硬件及系统管理员
存储管理员
`
4.优化的方向
`安全方向优化(业务持续性)
性能方向优化(业务高效性)`
5. 优化的范围及思路
5.1 优化范围:
存储、主机和操作系统:
主机架构稳定性
I/O规划及配置
Swap
OS内核参数
网络问题
应用程序:(Index,lock,session)
应用程序稳定性和性能
SQL语句性能
串行访问资源
性能欠佳会话管理
数据库优化:(内存、数据库设计、参数)
内存
数据库结构(物理&逻辑)
实例配置
架构设计 :
性能 : 读写分离,分布式
安全 : 主备,多活
安全&性能 : 分布式,NewSQL
6. 优化工具和命令
6.1 系统层面
CPU:top
top命令:
%CPU %MEM COMMAND
400.0 4.3 mysqld
%Cpu(s): 99.0 us, 1.0 sy, 0.0 ni, 0.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
按1键 展开:
%Cpu0 : 0.0 us, 0.0 sy, 0.0 ni,100.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu1 : 0.0 us, 0.0 sy, 0.0 ni,100.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu2 : 0.0 us, 0.0 sy, 0.0 ni,100.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu3 : 0.0 us, 0.0 sy, 0.0 ni,100.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu(s): 99.0 us, 1.0 sy, 0.0 id, 0.0 wa
0.0 id : CPU空闲的百分比
5% 以上,还是有空闲的.
99.0 us : 用户程序占用的CPU百分比
对于mysql程序,处理数据.
1.0 sy : 内核程序占用的CPU百分比
资源管理,资源调度等,计算.
sys过高: 锁,并发连接数较高,数据处理分析应用
0.0 wa : 花在等待的CPU时间
IO方面出了问题
锁.大事务
IO本身: IOPS达到峰值
[root@db01 ~]# top -H -p 9846
MEM
Swap: 1560568 total, 1560568 free, 0 used. 7386000 avail Mem
IO:
[root@db01 ~]# iostat -dk 1
Linux 3.10.0-957.el7.x86_64 (db01) 08/23/2019 _x86_64_ (4 CPU)
Device: tps kB_read/s kB_wrtn/s kB_read kB_wrtn
sda 1.65 36.24 115.44 223266 711168
Device: tps kB_read/s kB_wrtn/s kB_read kB_wrtn
sda 0.00 0.00 0.00 0 0
6.2 数据库层面
基础优化:
show status
show variables
show index
show processlist
show slave status
show engine innodb status
desc /explain
slowlog
扩展类深度优化:
pt系列
mysqlslap
sysbench
information_schema
performance_schema
sys
7. 优化思路分解
7.1.1 硬件优化
真实的硬件(PC Server): DELL R系列 ,华为,浪潮,HP,联想
云产品:ECS、数据库RDS、DRDS
IBM 小型机 P6 570 595 P7 720 750 780 P8
7.1.2 CPU根据数据库类型
OLTP
OLAP
IO密集型:线上系统,OLTP主要是IO密集型的业务,高并发
CPU密集型:数据分析数据处理,OLAP,cpu密集型的,需要CPU高计算能力(i系列,IBM power系列)
CPU密集型: I 系列的,主频很高,核心少
IO密集型: E系列(至强),主频相对低,核心数量多
7.1.3 内存
建议2-3倍cpu核心数量 (ECC)
7.1.4 磁盘选择
SATA-III SAS Fc SSD(sata) pci-e ssd Flash
主机 RAID卡的BBU(Battery Backup Unit)关闭
7.2 存储
根据存储数据种类的不同,选择不同的存储设备
配置合理的RAID级别(raid5、raid10、热备盘)
r0 :条带化 ,性能高
r1 :镜像,安全
r5 :校验+条带化,安全较高+性能较高(读),写性能较低 (适合于读多写少)
r10:安全+性能都很高,最少四块盘,浪费一半的空间(高IO要求)
HDS
EMC2(DELL)
IBM
DELL 华为
7.3 网络
1、硬件买好的(单卡单口)
2、网卡绑定(bonding),交换机堆叠
主备模式: mode=1
负载均衡: mode=0
注意: 多交换机要做堆叠.
以上问题,提前规避掉。
7.4 操作系统优化
Swap调整
cat /proc/sys/vm/swappiness
临时
echo 0 >/proc/sys/vm/swappiness
永久
vim /etc/sysctl.conf
vm.swappiness=0
sysctl -p
这个参数决定了Linux是倾向于使用swap,还是倾向于释放文件系统cache。在内存紧张的情况下,数值越低越倾向于释放文件系统cache。
当然,这个参数只能减少使用swap的概率,并不能避免Linux使用swap。
修改MySQL的配置参数innodb_flush_method,开启O_DIRECT模式
这种情况下,InnoDB的buffer pool会直接绕过文件系统cache来访问磁盘,但是redo log依旧会使用文件系统cache。值得注意的是,Redo log是覆写模式的,即使使用了文件系统的cache,也不会占用太多
7.5 IO调度策略
centos 7 默认是deadline
cat /sys/block/sda/queue/scheduler
#临时修改为deadline(centos6)
echo deadline >/sys/block/sda/queue/scheduler
vi /boot/grub/grub.conf
更改到如下内容:
kernel /boot/vmlinuz-2.6.18-8.el5 ro root=LABEL=/ elevator=deadline rhgb quiet
IO :
raid
no lvm
ext4或xfs
ssd
IO调度策略
提前规划好以上所有问题,减轻MySQL优化的难度。
7.6 应用端
1. 开发过程规范,标准
2. 减少烂SQL:不走索引,复杂逻辑,切割大事务.
3. 避免业务逻辑错误,避免锁争用.
这个阶段,需要我们DBA深入业务,或者要和开发人员\业务人员配合实现
优化,最根本的是"优化"人.
----oldguo
7.7 MySQL参数优化测试
虚拟机vm12.5,OS centos 6.9(系统已优化),cpu*4(I5 4440 3.1GHZ),MEM*4GB ,HardDisk:SSD
8. 参数优化细节:
max_connections 最大连接数
key_buffer_size 临时表
innodb_flush_log_at_trx_commit 双一:redo log的刷写策略,每次事务提交时,首先刷写os buffer,立即刷写到磁盘.
innodb_buffer_pool_size 数据缓冲区大小,建议不要超过物理内存80%
max_allowed_packet 允许最大数据包的大小
sync_binlog 双一:二进制日志的刷写策略,每次事务提交理解刷写二进制日志
innodb_flush_method
刷写策略:
O_DIRECT :
刷写buffer pool 绕过OS buffer ,直接刷磁盘
刷写redo buffer 先到OS buffer ,再到磁盘
详细的参数优化请查看oldguo导师的链接
8.1. Max_connections *****
(1)简介
Mysql的最大连接数,如果服务器的并发请求量比较大,可以调高这个值,当然这是要建立在机器能够支撑的情况下,因为如果连接数越来越多,mysql会为每个连接提供缓冲区,就会开销的越多的内存,所以需要适当的调整该值,不能随便去提高设值。
(2)判断依据
show variables like 'max_connections';
+-----------------+-------+
| Variable_name | Value |
+-----------------+-------+
| max_connections | 151 |
+-----------------+-------+
show status like 'Max_used_connections';
+----------------------+-------+
| Variable_name | Value |
+----------------------+-------+
| Max_used_connections | 101 |
+----------------------+-------+
(3)修改方式举例
vim /etc/my.cnf
Max_connections=1024
补充:
1.开启数据库时,我们可以临时设置一个比较大的测试值
2.观察show status like 'Max_used_connections';变化
3.如果max_used_connections跟max_connections相同,
那么就是max_connections设置过低或者超过服务器的负载上限了,
低于10%则设置过大.
8.2 back_log ***
(1)简介
mysql能暂存的连接数量,当主要mysql线程在一个很短时间内得到非常多的连接请求时候它就会起作用,如果mysql的连接数据达到max_connections时候,新来的请求将会被存在堆栈中,等待某一连接释放资源,该推栈的数量及back_log,如果等待连接的数量超过back_log,将不被授予连接资源。
back_log值指出在mysql暂时停止回答新请求之前的短时间内有多少个请求可以被存在推栈中,只有如果期望在一个短时间内有很多连接的时候需要增加它
(2)判断依据
show full processlist
发现大量的待连接进程时,就需要加大back_log或者加大max_connections的值
(3)修改方式举例
vim /etc/my.cnf
back_log=1024
8.3 wait_timeout和interactive_timeout ****
(1)简介
wait_timeout:指的是mysql在关闭一个非交互的连接之前所要等待的秒数
interactive_timeout:指的是mysql在关闭一个交互的连接之前所需要等待的秒数,比如我们在终端上进行mysql管理,使用的即使交互的连接,这时候,如果没有操作的时间超过了interactive_time设置的时间就会自动的断开,默认的是28800,可调优为7200。
wait_timeout:如果设置太小,那么连接关闭的就很快,从而使一些持久的连接不起作用
(2)设置建议
如果设置太大,容易造成连接打开时间过长,在show processlist时候,能看到很多的连接 ,一般希望wait_timeout尽可能低
(3)修改方式举例
wait_timeout=60
interactive_timeout=7200
长连接的应用,为了不去反复的回收和分配资源,降低额外的开销。
一般我们会将wait_timeout设定比较小,interactive_timeout要和应用开发人员沟通长链接的应用是否很多。如果他需要长链接,那么这个值可以不需要调整。
另外还可以使用类外的参数弥补。
8.4 key_buffer_size *****
(1)简介
key_buffer_size指定索引缓冲区的大小,它决定索引处理的速度,尤其是索引读的速度
《1》此参数与myisam表的索引有关
《2》内存临时表的创建有关(多表链接、子查询中、union)
在有以上查询语句出现的时候,需要创建临时表,用完之后会被丢弃
临时表有两种创建方式:
内存中------->key_buffer_size
磁盘上------->ibdata1(5.6)
ibtmp1 (5.7)
(2)设置依据
通过key_read_requests和key_reads可以直到key_baffer_size设置是否合理。
mysql> show variables like "key_buffer_size%";
+-----------------+---------+
| Variable_name | Value |
+-----------------+---------+
| key_buffer_size | 8388608 |
+-----------------+---------+
1 row in set (0.00 sec)
mysql>
mysql> show status like "key_read%";
+-------------------+-------+
| Variable_name | Value |
+-------------------+-------+
| Key_read_requests | 10 |
| Key_reads | 2 |
+-------------------+-------+
2 rows in set (0.00 sec)
mysql>
一共有10个索引读取请求,有2个请求在内存中没有找到直接从硬盘中读取索引
控制在 5%以内 。
注:key_buffer_size只对myisam表起作用,即使不使用myisam表,但是内部的临时磁盘表是myisam表,也要使用该值。
可以使用检查状态值created_tmp_disk_tables得知:
mysql> show status like "created_tmp%";
+-------------------------+-------+
| Variable_name | Value |
+-------------------------+-------+
| Created_tmp_disk_tables | 0 |
| Created_tmp_files | 6 |
| Created_tmp_tables | 1 |
+-------------------------+-------+
3 rows in set (0.00 sec)
mysql>
通常地,我们习惯以 Created_tmp_tables/(Created_tmp_disk_tables + Created_tmp_tables)
Created_tmp_disk_tables/(Created_tmp_disk_tables + Created_tmp_tables)
或者已各自的一个时段内的差额计算,来判断基于内存的临时表利用率。所以,我们会比较关注 Created_tmp_disk_tables 是否过多,从而认定当前服务器运行状况的优劣。
Created_tmp_disk_tables/(Created_tmp_disk_tables + Created_tmp_tables)
控制在5%-10%以内
8.5 参数优化结果
vim /etc/my.cnf
[mysqld]
basedir=/data/mysql
datadir=/data/mysql/data
socket=/tmp/mysql.sock
log-error=/var/log/mysql.log
log_bin=/data/binlog/mysql-bin
binlog_format=row
skip-name-resolve
server-id=52
gtid-mode=on
enforce-gtid-consistency=true
log-slave-updates=1
relay_log_purge=0
max_connections=1024
back_log=128
wait_timeout=60
interactive_timeout=7200
key_buffer_size=16M
query_cache_size=64M
query_cache_type=1
query_cache_limit=50M
max_connect_errors=20
sort_buffer_size=2M
max_allowed_packet=32M
join_buffer_size=2M
thread_cache_size=200
innodb_buffer_pool_size=2048M
innodb_flush_log_at_trx_commit=1
innodb_log_buffer_size=32M
innodb_log_file_size=128M
innodb_log_files_in_group=3
binlog_cache_size=2M
max_binlog_cache_size=8M
max_binlog_size=512M
expire_logs_days=7
read_buffer_size=2M
read_rnd_buffer_size=2M
bulk_insert_buffer_size=8M
innodb_lock_wait_timeout=28800
[client]
socket=/tmp/mysql.sock
再次压力测试 :
mysqlslap --defaults-file=/etc/my.cnf --concurrency=100 --iterations=1 --create-schema='oldboy' --query="select * from oldboy.t_100w where k2='FGCD'" engine=innodb --number-of-queries=200000 -uroot -p123 -verbose
9. 锁的监控及处理
9.1 锁等待模拟
概念:
Record Lock
Next Lock
GAP Lock
X
IX
S
IS
tx1:
USE oldboy
UPDATE t_100w SET k1='av' WHERE id=10;
## tx2:
USE oldboy
UPDATE t_100w SET k1='az' WHERE id=10;
9.2 监控锁状态
vim /etc/my.cnf
innodb_lock_wait_timeout=28800
1. 看有没有锁等待
SHOW STATUS LIKE 'innodb_row_lock%';
2. 查看哪个事务在等待(被阻塞了)
USE information_schema
SELECT * FROM information_schema.INNODB_TRX WHERE trx_state='LOCK WAIT';
trx_query : 被阻塞的语句是谁
trx_mysql_thread_id: 被阻塞的会话ID
3.查看锁源,谁锁的我!
SELECT * FROM sys.innodb_lock_waits; ## ====>被锁的和锁定它的之间关系
## waiting_pid : 等待的会话线程号
## waiting_query : 等待的语句
## blocking_pid : 锁源的线程号 :2
## sql_kill_blocking_query : 处理建议
## sql_kill_blocking_connection: 处理建议
4. 找到锁源的thread_id
SELECT * FROM performance_schema.threads WHERE processlist_id=2;
## THREAD_ID = 27
5. 找到锁源的SQL语句
-- 当前在执行的语句
SELECT * FROM performance_schema.`events_statements_current` WHERE thread_id=27;
10. 主从优化:
## 5.7 从库多线程MTS
基本要求:
5.7以上的版本(忘记小版本)
必须开启GTID
binlog必须是row模式
gtid_mode=ON
enforce_gtid_consistency=ON
log_slave_updates=ON
slave-parallel-type=LOGICAL_CLOCK
slave-parallel-workers=16
master_info_repository=TABLE
relay_log_info_repository=TABLE
relay_log_recovery=ON
5.7 :
slave-parallel-type=LOGICAL_CLOCK
slave-parallel-workers=8
索引
简单SQL语句
max_connections 最大连接数
key_buffer_size 临时表
innodb_flush_log_at_trx_commit 双一:redo log的刷写策略,每次事务提交时,首先刷写os buffer,立即刷写到磁盘.
innodb_buffer_pool_size 数据缓冲区大小,建议不要超过物理内存80%
max_allowed_packet 允许最大数据包的大小
sync_binlog 双一:二进制日志的刷写策略,每次事务提交理解刷写二进制日志
innodb_flush_method
O_DIRECT :
刷写buffer pool 绕过OS buffer ,直接刷磁盘
刷写redo buffer 先到OS buffer ,再到磁盘