参考博客
MySQL调优篇:单机数据库如何在高并发场景下健步如飞?
优化类型大概分类三个类型
CPU,内存,磁盘三者任何一个达到一个瓶颈都会影响 线程越多,需要的CPU调度能就越强,需要的内存也越大,磁盘IO速率也会要求越快
在考虑性能优化时,必然不能让CPU、内存、磁盘等资源的使用率达到95%+,一般而言,最大利用率控制在80-85%左右的最佳状态
一台正常部署的MySQL极致调优也很难做到一秒5000+的QPS
读写参半:项目集群,双主热备
写大于读:消息中间件,DB分库分表,项目集群
读大于写:缓存/搜素中间件,动静分离,读写分离
提前防范问题主要在两个阶段
- 监控系统的作用并不是用来提醒你项目“嗝屁”了的,而是用来提醒你:线上部署的应用系统可能会“嗝屁”或快“嗝屁”了
- 通常情况下,在监控系统上面设置的性能阈值都会比最大极限值要低5~15%
- 最大极限值是85%,那设置告警值一般是75%左右就会告警,不会真达到85%才告警,只有这样做才能留有足够的时间让运维和开发人员介入排查
在处理棘手问题或优化性能时,无需刻意追求完美,理性权衡利弊后,适当的做出一些决断,抛弃掉一部分不重要的,起码比整个系统挂掉要好,何况之后也同样可以恢复
性能优化永远是建立在性能瓶颈之上的,如果你的系统没有出现瓶颈,那则无需调优,过早的调优是一个很错误的选择,调优之前需要牢记的一点是:不要为了调优而调优,而是需要调优时才调
发现瓶颈有两种方式
性能优化步骤如下
五个性能优化项中,通常情况下,带来的性能收益排序为4 > 2 > 5 > 3 > 1
一个用户最终请求会在Java程序中分配一条线程处理, 最终变成一条SQL发往MySQL
首先明确, 理想情况下是数据库连接数越大越好, 当实际上硬件的性能有限, 没有满足, 所以这个命题就改成在一定的条件的情况下, 数据库的连接数越大越好
数据库连接数越大,也就意味着内部创建出的工作线程会越多,线程越多代表需要的CPU配置得更高,比如现在有300个客户端连接,内部创建了300条工作线程处理,但服务器的CPU仅有32个核心,那当前服务器最多只能支持32条线程同时工作,那其他268条线程怎么办呢?为了其他线程能够正常执行,CPU就会以时间片调度的模式工作,不同核心在不同线程间反复切换执行,但由于线程数远超核心数,因此会导致线程上下文切换的开销,远大于线程执行的开销
如果这里的时间片和线程上下文开销不理解, 那么请去借助AI获取其他的博客了解
由于硬件的资源限制, 所以数据库连接数如何配置成一个合适的值有讲究
连接池的种类
面试题: 为什么MySQL要设计两个连接池?
客户端连接池: 为了减少应用程序与MySQL服务器之间建立连接和断开连接开销 服务端连接池: 限制同时能连接的客户端数量
todo
优化点
服务端连接池通常不关心, 一般来说优化的客户端连接池的连接数
面试题: 为什么不将应用程序的连接数设置成MySQL连接数一样大?
通常来说数据库实例不只为单体项目提供服务(特别是在这个微服务泛滥的时代), 而且平常我们使用navcate连接到数据库也是会占用连接数的
如果两个相等的情况, 如果此时还有一些其他的连接加入, 那么就可能导致MySQL连接数爆满, 然后就会出现那么连接不上数据库的现象
对于Linux的连接也是同理, 如果同时的连接人数太多, 那么后续的人就可能连接到Linix服务器
如何优化
公式: 最大连接数 = (CPU核心数 * 2) + 有效磁盘数
这里的有效磁盘数指的是SSD固态硬盘个数, SSD固态硬盘没有磁片, 没有旋转磁面寻址, 发生磁盘IO时通常不会阻塞, 所以有SSD磁盘情况下该数值可以+1
通常来说一颗CPU在2-3线程之间的切换不会存在太大的开销, 如果大于这个值, 那么线程切换的开销就会超过线程的本身的开销
场景描述
比如目前服务器的硬件配置为CPU:16core、硬盘类型:SSD,此时最佳的最大连接数则为16*2+1=33,而常驻连接数可以调整到30左右,这组配置绝对是当前场景下的最佳配置,如若再调大连接数,这反而会让性能更慢,因为会造成线程上下文切换的额外开销
需要说明的是, 上述的公式不适用所有场景, 实际中还需要考虑很多因素, SQL执行时长, 大事务等
场景调优
对于最佳连接数的计算,首先要把CPU核数放首位考虑,紧接着是磁盘,最后是网络带宽,因为带宽会影响SQL执行时间,综合考虑后才能计算出最合适的连接数大小
这里对B+树一定熟悉, 不然是云里雾里, 如果不熟悉的, 那么请去了解后再来, 该文不会进行细致的讲解
结构优化指的是
InnoDB缓冲区的最佳大小比例应该控制在70~75%
当InnoDB缓冲区大于1G的时, 会将缓冲区划分为多个实例空间, 多线程并发执行时, 减少并发冲突
MySQL官方的建议是每个缓冲区实例必须大于1GB
如果是8/16G, 那么建议设置1G大小
如果是32GB/64GB甚至更高, 那么建议设置成2GB或者更大
缓冲实例个数建议设置成物理内存的总量的1/3或1/2之间
对于这些区域,最好根据机器内存来设置为一到两倍MB,啥意思呢?比如4GB的内存,建议将其调整为4/8MB、8GB的内存,建议将其调整为8/16MB.....,但这些区域的大小最好控制在64MB以下,因为线程每次执行完一条SQL后,就会将这些区域释放,所以再调大也没有必要了。
文件排序触发的阈值: max_length_for_sort_data, 排序字段值的最大长度小于该值,则会将所有要排序的字段值载入内存排序,但如果大于该值时,则会一批一批的加载排序字段值进内存,然后一边加载一边做排序, 可以适当调高该值
tmp_table_size: 当创建的临时表空间占用超过tmp_table_size时,就会将其他新创建的临时表转到磁盘中创建
max_heap_table_size: : 当创建的临时表空间占用超过max_heap_table_size时, MySQL将会将该临时表转换为磁盘上的MyISAM表。这个转换过程会导致性能下降,因为磁盘I/O通常比内存访问慢得多
那么这两个参数该设置多大呢?这要根据show global status like 'created_tmp%';的统计信息来决定,用统计出来的信息:Created_tmp_disk_tables / Created_tmp_tables * 100% = 120%,达到这个标准就比较合适,但调整这个区域的值需要反复重启MySQL以及压测,因此比较费时间,如果你在项目中很少使用临时表,哪也可以不关心这块参数的调整。
wait_timeout: 命令行或脚本链接的超时时间, 默认8小时
interactive_timeout: 交互式链接超时时间, 默认8小时
这两个参数必须同时设置, 否则不生效
手动调低连接时间, 可以让性能及时释放, 减少资源占用
在项目中引入Redis作为缓存后,在缓存Key设计合理的情况下,至少能够为MySQL分担**70%**以上的读压力,查询MySQL之前先查询一次Redis,Redis中有缓存数据则直接返回,没有数据时再将请求交给MySQL处理,从MySQL查询到数据后,再次将数据写入Redis,后续有相同请求再来读取数据时,直接从Redis返回数据即可。
将实行要求没有这高的写请求放入MQ中, 其他线程按需拉去消费, 减少写压力
架构模式