写在前面的话
不管是作为运维还是作为 DBA,我们的工作都不是写 SQL,搞业务。更多的还是如何实现又好又快的给开发提供一个数据库环境和保障数据的安全性。前面的文章中读写分离,高可用,建立索引,分库分表等都是类似改变业务的架构来提升数据库的性能和稳定性。本章节介绍如何通过修改 my.cnf 配置文件直接以最低的成本提升服务器的性能。
关于 MySQL 优化
优化的标准:
1. 优化有风险,配置需谨慎。
2. 优化永远不是一个人的问题,需要开发,运维,DBA 共同介入。
3. 稳定比性能重要,没有明显提升的优化是失败的。
4. 一个问题的解决往往伴随着另外一个问题的产生,优化不一定都是好的。
优化的方向:安全和性能
优化的范围:
1. 硬件和系统:例如机器配置,网络,系统优化等。
2. 业务程序优化:例如索引,锁,SQL 性能等。
3. 数据库优化:例如数据库架构,数据库配置参数等。
操作系统级别的优化
1. CPU 使用情况:top 命令 --> 按 1
我这里选的 4 核,所以可以看到 4 个 CPU。
主要指标:
us:use,用户程序运行中 CPU 占比,理想中是能达到 70% 最好,这样才能说明设备性能充分利用。
sy:sys,系统资源,一般内核调用,这个偏高可能是 BUG,中毒或者数据库锁住了。
id:idle,空闲时间占比。
wa:wait,等待时间,偏高原因可能是锁,IO,索引问题等。
MySQL 出现某颗 CPU 忙,其它闲,可能是 MySQL 并发参数问题。
2. 内存 / Swap 使用情况:
total:总共 / free:空闲 / userd:使用 / buffer/cache:缓冲缓存
对于 MySQL 而言,不建议配置 Swap 分区,内存配置建议为主机内存 70% - 80%。
在 Linux 默认的 buffer 回收策略中,可以查看内存使用到多少开始使用 Swap:
cat /proc/sys/vm/swappiness
结果:
这说明当内存使用到 70% 的时候,系统将使用 Swap 而不是立即开始回收 buffer/cache。
所以在 MySQL 中我们选择关闭 Swap 甚至不配置 Swap 分区。
# 配置 echo 0 >/proc/sys/vm/swappiness echo 'vm.swappiness = 0' >>/etc/sysctl.conf # 生效 sysctl -p
这个参数只能代表系统更高的权重来选择释放回收 cache,并不能避免 swap 的使用。
当 innodb_flush_method 设置 O_DIRECT 时,buffer pool 会绕过 cache 去访问磁盘。
3. IO 使用:
测试主机 IO 的方式:
【1】dd 命令:
dd if=/dev/zero of=/tmp/bigfile bs=1M count=4096
结果:
【2】iostat 命令:
yum -y install sysstat # 查看 iostat -dm 1
结果:
可以动态查看读写情况!
CPU / IO 都高属于正常现象。
CPU 高 IO 低可能是在执行函数,排序,分组等非增删查改操作。
CPU wa / sy 高,IO 低可能是 IO 出问题或者数据库锁住了。
IOPS:在购买云服务器磁盘的时候,往往不同的盘可以看到有个 IOPS 参数,代表每秒该磁盘能执行的读写次数,是硬件的上限。
优化调度策略:CentOS 7 默认 deadline
# CentOS 6 优化 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
另外就是系统建议使用 ext4 或者 xfs,不建议 lvm。最后就是用好点的硬件。
主机硬件级别优化
1. 设备供应商优化。选大厂,如 DELL 华为,IBM 等的产品而不是野鸡产品。
2. 云产品则是阿里的 RDS / DRDS。
3. CPU 选型:CPU 密集型,也就是运算较多的,可以用 I 系列 CPU,IO 密集型,高并发则可以选择至强系列 CPU。
4. 内存配置:一般建议是 CPU 核心数量的 2 - 3 倍。
5. 磁盘选择(IOPS 顺序):SAS(最大 900G,一般 600) / FC / SSD(SATA)/ PCI-E / SSD / Flash
6. 关闭 RAID 卡的 BBU(Battery Backup Unit)。
7. RAID 选型:R0 性能高,但不安全,R1 安全,R5 读性能高安全,写性能低,R10 安全和性能都高,使用高 IO。
8. 网络:选择好硬件,双网口进行网卡绑定(binding),交换机堆叠。
MySQL 参数优化
首先创建一个 1000 万数据的测试表:
create database testdb1 charset utf8mb4 collate utf8mb4_bin; use testdb1; create procedure rand_data(in num int) begin declare str char(62) default 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; declare str2 char(2); declare str4 char(4); declare i int default 0; while i<num do set str2=concat(substring(str,1+floor(rand()*61),1),substring(str,1+floor(rand()*61),1)); set str4=concat(substring(str,1+floor(rand()*61),2),substring(str,1+floor(rand()*61),2)); set i=i+1; insert into t_100w values (i,floor(rand()*num),str2,str4,now()); end while; end; // delimiter ; -- 生成数据 call rand_data(10000000); commit;
1. 最大连接数:max_connections
show variables like "%max_connections%";
结果:
MySQL 会为每一个连接都分配内存,所以这个数字并不是随便设置,而且一台服务器的性能也是有瓶颈的。
可以根据业务高峰期的最大使用的连接数进行调整:Max_used_connections
show status like "Max_used_connections";
结果:
最终修改 my.cnf,一开始可以设置一个相对大的值,后面再调。
max_connections = 1024
同时有另外一个参数:back_log
show variables like "back_log";
结果:
该参数的作用在于当请求数量达到 max_connections 的时候,我们允许的排队连接数量。如果排队超过该值,则请求被拒绝。
可以通过 show processlist; 查看,如果有很多 sleep 的请求,就需要增大该值或者增大最大连接数。
back_log = 1024
2. 超时时间:wait_timeout 和 interactive_timeout
show variables like "%timeout%";
结果:
wait_timeout:MySQL 在关闭一个非交互连接之前需要等待的时间。如服务连接数据库。不建议设置太长,这样会造成打开连接慢,show processlist 的时候连接很多。
interactive_timeout:MySQL 在关闭一个交互式连接之前需要等待的时间。如我们终端连接数据库。
在设置的时间没有执行任何操作则会关闭连接。
wait_timeout=60
interactive_timeout=1200
3. 索引缓冲区大小:key_buffer_size
show variables like "key_buffer_size";
结果:
该值指定索引缓冲区大小,决定了索引处理速度,尤其是索引读的速度。主要针对 MyISAM 表索引和临时表(jion union 子查询等)。
参数设置依据:
show status like "key_read%";
结果:
该结果表示:一共 10 个索引读取请求,有 5 个请求没有在内存中找到,选择从磁盘中直接读取。
虽然该值只针对 MyISAM 生效,但是系统内部的临时表也会用到。
show status like "created_tmp%";
结果:
我们通常会使用:Created_tmp_disk_tables / (Created_tmp_tables + Created_tmp_disk_tables)
计算临时表利用率,该结果建议在 5% - 10%,所以这意味着我们希望 Created_tmp_disk_tables 尽可能小一些。
该公式在 mysqldump 的时候不需要关注,配置方法:
key_buffer_size = 64M
4. 查询缓存:query_cache_size
show variables like "query_cache_size";
结果:
该值的作用在于,当第一个查询结束后,SQL 会被 hash 成一个 ID,并和查询结果一起存在缓存中,当下次一模一样的查询再次来的时候,就能直接使用缓存。
如果缓存中没有该 ID,则再去数据库中执行查询。
该值设置依据:
show status like "%Qcache%";
结果:
如果 Qcache_hits / (Qcache_inserts + Qcache_not_cached + Qcache_hits) 值太低,说明其实是没啥缓存必要的。因为命中几率低。缓存没意义。如果还需要缓存,建议使用 redis 等。
同时可以通过该结果查看内存够不够。
查看具体配置:
show variables like '%query_cache%';
结果:
通过 query_cache_type 可以看到在 MySQL 5.7 中其实是没有开启该功能的,也就是不建议的。
query_cache_limit:超过此大小的查询将不缓存。
query_cache_min_res_unit:默认 4K,值设置大对大查询有好处,但是小查询则浪费资源。
query_cache_size:查询缓存大小。
query_cache_type:缓存类型,0 关闭,不缓存。1 缓存所有,除非 SQL 中特意指定 SQL_NO_CACHE。2 只缓存指定 SQL_CACHE 的。
配置方法:
query_cache_size = 128M
query_cache_type = 1
5. 最大连接错误:max_connect_errors
该值是一个安全参数,防止别人暴力破解密码。当次数达到指定次数错误就拒绝该 host 访问。
知道 MySQL 重启或者 flush hosts 才能再次访问。配置方法:
max_connect_errors = 2000
6. 排序缓冲区大小:sort_buffer_size
show variables like "sort_buffer_size";
结果:
该值定义了每个需要排序(order by / group by / union / distinct)的线程需要缓冲区大小,用于加速查询。
但是该值并非越大越小,该值 x 最大连接数等于会消耗的内存,太大会导致内存耗尽。配置方法:
sort_buffer_size = 1M
7. server 能接受的最大数据包大小:max_allowed_packet
show variables like "max_allowed_packet";
结果:
有时候大的插入会受到该值的影响,正如前面说到的插入 1000 万数据。设置方式:
max_allowed_packet = 32M
8. 设置关联查询缓存大小:join_buffer_size
show variables like "join_buffer_size";
结果:
和 sort_buffer_size 类似,只是这个是设置 join 关联查询。应该尽量在关联列建立索引来优化 SQL。
9. 服务器线程缓存:thread_cache_size
show variables like "thread_cache_size";
结果:
该值用于配置服务器线程在缓存中能够被重新利用的数量。当连接断开时,并不是马上被回收,而是被存在缓存中,等待下一次连接。
如果缓存中不存在或者是新的请求的时候,则新建立线程。如果数据库线程多,增加这个值可以提升性能。
配置依据:查看连接线程数:
show status like 'threads_%';
结果:
Threads_cached:此刻缓存中空闲的线程数量。
Threads_connected:代表当前已经建立的线程数量。
Threads_created:代表当前建立的线程数量,如果该值过大,说明系统创建新线程多,比较消耗 CPU和系统资源,可以调大 thread_cache_size。
Threads_running:代表激活状态的线程数,不是线程总数,因为还有 sleep 的。
配置方法:
thread_cache_size = 32
10. 指定 innodb 缓存大小:innodb_buffer_pool_size
show variables like "innodb_buffer_pool_size";
结果:
该值的功能和 key_buffer_size 类似,但是这个针对 innodb,那个则是 MyISAM。用于指定缓存区和索引的内存大小。
该值一般设置为服务器内存的 80% 左右。配置方法:
innodb_buffer_pool_size = 2048M
11. flush log 到磁盘的时间:innodb_flush_log_at_trx_commit
show variables like "innodb_flush_log_at_trx_commit";
结果:
双一标准的一个一,用于控制 innodb 将 log buffer 中的数据写入日志并 flush 到磁盘的时间点。
0:事务提交,不做写入操作,而是按照每秒钟自动写入一次。
1:每次提交自动日志写入并写入磁盘。
2:每次提交立即写日志,但是每秒钟才一次将数据写入磁盘。
该值非常影响数据插入,当一次性插入 1000 条数据,0 可能需要 1 秒,2 可能需要 2 秒,1 则可能需要几百秒。
所以 MySQL 建议将多个插入合并成一个事务来操作,这样可以提升速度,在大数据量导入的时候,可以通过改变这个配置来提升导入速度。
当然,这样存在一定的风险,但是导入数据的时候我们能够校验完整性,所以不用担心。配置方法:
innodb_flush_log_at_trx_commit = 1
12. innodb 并发线程数量:innodb_thread_concurrency
show variables like "innodb_thread_concurrency";
结果:
该参数用于设置 innodb 线程并发数量,0 表示不限制。
官方的建议是在并发用户数小于 64 时,建议设置为 0,当工作负载很高的时候,则建议先设置为 128,然后慢慢的根据实际调整。
设置标准:
show status like 'threads_%';
和 top 等查看线程,CPU 使用情况,然后对该值进行调整。
使用 top 查看每个 CPU 使用情况,如果分配不均,可以将值设置为 CPU 数量,然后成倍增加测试。知道合适。
配置方法:
innodb_thread_concurrency = 8
13. 日志缓存大小:innodb_log_buffer_size
show variables like "innodb_log_buffer_size";
结果:
缓冲区更大能提高性能,对于较大的事务,可以增大缓存大小。设置方法:
innodb_log_buffer_size = 128M
14. 数据日志文件的大小:innodb_log_file_size
show variables like "innodb_log_file_size";
结果:
也就是定义单个 ib_logfile0 文件大小。更大的文件有利于提升性能。配置方法:
innodb_log_file_size = 100M
15. 数据日志文件个数:innodb_log_files_in_group
show variables like "innodb_log_files_in_group";
结果:
默认就是 ib_logfile0 和 ib_logfile1,可以适当增加,可以提升写的性能。配置方法:
innodb_log_files_in_group = 3
16. 读取缓冲区大小:read_buffer_size
show variables like "read_buffer_size";
结果:
该配置每个连接独享的,当对表进行顺序扫描的请求分配到缓冲区,提升性能。如果请求非常频繁,可以增加该值。配置方法:
read_buffer_size = 1M
17. 随机读缓冲区大小:read_rnd_buffer_size
show variables like "read_rnd_buffer_size";
结果:
当任意顺序读取行时,线程将分配一个随机读缓存区,查询时,会先扫描缓冲,以避免磁盘搜索,提高速度,如需排序大量数据,可适当调高该值。
设置方法:
read_rnd_buffer_size = 1M
18. 批量插入缓冲区大小:bulk_insert_buffer_size
show variables like "bulk_insert_buffer_size";
结果:
批量插入数据缓存大小,可以有效提高插入效率。配置方法:
bulk_insert_buffer_size = 8M
19. 二进制日志优化:
# 二进制日志保存位置 log-bin = /data/logs/mysql/bin-log/mysql-bin # 每个 session 分配的内存,在事务过程中用来存储二进制日志的缓存 binlog_cache_size = 2M # binlog 能使用的最大 cache 内存大小 max_binlog_cache_size = 8M # 指定binlog日志文件的大小 max_binlog_size = 512M # 二进制日志自动删除的天数,默认 0 不自动删除 expire_logs_days = 7 # 格式方式 binlog_format = row # 双1标准,什么时候刷新binlog到磁盘,每次事务commit sync_binlog = 1
20. 安全优化:
Innodb_flush_method:
当值为 fync 时,数据页持久化时,先写到 os buffer,在由 os 决定啥时候写入磁盘。
当值为 O_DIRECT 时,数据页持久化直接写入磁盘,但是 redo buffer 持久化则需要先写 os buffer,再由 os 决定啥时候写磁盘。
当然,如果设置了 innodb_flush_log_at_trx_commit = 1,则会 commit 就直接写磁盘。
最安全模式:
innodb_flush_log_at_trx_commit = 1
innodb_flush_method = O_DIRECT
最高性能模式:
innodb_flush_log_at_trx_commit = 0
innodb_flush_method = fsync
一般我们更偏向于安全,毕竟是数据库(双一标准):
innodb_flush_log_at_trx_commit = 1 sync_binlog = 1 innodb_flush_method = O_DIRECT
21. 主从优化:
从库添加配置:
gtid_mode=ON enforce_gtid_consistency=1 log_slave_updates=1 slave-parallel-type=LOGICAL_CLOCK # CPU 数量 slave-parallel-workers=4 master_info_repository=TABLE relay_log_info_repository=TABLE relay_log_recovery=1
优化配置文件模板
可以根据自己的实际需求进行修改:
[mysqld] ############################################################### # 基础配置 ############################################################### port=3306 server-id=111 skip-name-resolve gtid-mode=on enforce-gtid-consistency=true max_connections=1024 back_log=128 ############################################################### # 基础目录定义 ############################################################### basedir=/data/services/mysql datadir=/data/data/mysql socket=/data/logs/mysql/mysql.sock log-error=/data/logs/mysql/error-log/mysql.log ############################################################### # bin log 配置 ############################################################### binlog_format=row log-slave-updates=1 binlog_cache_size=2M max_binlog_cache_size=8M max_binlog_size=512M log_bin=/data/logs/mysql/bin-log/mysql-bin ############################################################### # slow log 配置 ############################################################### slow_query_log=1 long_query_time=2 log_queries_not_using_indexes=1 slow_query_log_file=/data/logs/mysql/slow-log/slow.log ############################################################### # relay log 配置 ############################################################### relay_log_purge=0 relay_log=/data/logs/mysql/relay-log/relay-bin ############################################################### # 优化配置 ############################################################### # 非交互 / 交互超时时间 / 连接错误次数 wait_timeout=60 interactive_timeout=7200 max_connect_errors=20 # MyISAM和临时表缓冲区 / 排序缓冲区 / 关联查询缓冲区大小等 key_buffer_size=16M sort_buffer_size=2M join_buffer_size=2M read_buffer_size=2M read_rnd_buffer_size=2M bulk_insert_buffer_size=8M # 查询缓存 query_cache_size=64M query_cache_type=1 query_cache_limit=50M # 最大数据包 / 线程缓存数量 max_allowed_packet=32M thread_cache_size=200 # innodb 配置 innodb_buffer_pool_size=1024M innodb_flush_log_at_trx_commit=1 innodb_log_buffer_size=32M innodb_log_file_size=128M innodb_log_files_in_group=3 # 事务等待获取资源等待的最长时间,超时返回失败,默认 50s innodb_lock_wait_timeout = 30 # 将所有的死锁记录到 error_log 中 innodb_print_all_deadlocks = 1 # 日志保留时间 expire_logs_days=7 [client] port=3306 socket=/data/logs/mysql/mysql.sock prompt=3306/\u [\d]>
小结
这里的优化参数只是其中的一部分,还要一些后学才能加上。