MySQL调优

参考博客

MySQL调优篇:单机数据库如何在高并发场景下健步如飞?

优化类型大概分类三个类型

  1. 结构/架构优化:读写分离、集群热备、分布式架构、引入缓存/消息/搜索中间件、分库分表、中台架构(大数据中台、基础设施中台)等
  2. 配置/参数优化:调整应用系统中各层面的配置文件、启动参数达到优化性能的目标
  3. 代码/操作优化:从代码、操作方面进行调节,达到效率更高的初衷

优化思想

单个节点调优核心思想

CPU,内存,磁盘三者任何一个达到一个瓶颈都会影响 线程越多,需要的CPU调度能就越强,需要的内存也越大,磁盘IO速率也会要求越快

在考虑性能优化时,必然不能让CPU、内存、磁盘等资源的使用率达到95%+,一般而言,最大利用率控制在80-85%左右的最佳状态

优秀且适用的系统架构胜千万次调优

一台正常部署的MySQL极致调优也很难做到一秒5000+的QPS

读写参半:项目集群,双主热备

写大于读:消息中间件,DB分库分表,项目集群

读大于写:缓存/搜素中间件,动静分离,读写分离

预防大于一切,调优并非“临时抱佛脚”

提前防范问题主要在两个阶段

  1. 项目初期预测未来的流量压力,提前根据业务设计出合适的架构,确保上线后可以承载业务的正常增长
  2. 项目上线后,配备完善的监控系统,在性能瓶颈来临前设好警报线,确保能够在真正的性能瓶颈到来之前解决问题
  3. 不要“卡点”设计,也不能过度设计造成性能过剩

  • 监控系统的作用并不是用来提醒你项目“嗝屁”了的,而是用来提醒你:线上部署的应用系统可能会“嗝屁”或快“嗝屁”了
  • 通常情况下,在监控系统上面设置的性能阈值都会比最大极限值要低5~15%
  • 最大极限值是85%,那设置告警值一般是75%左右就会告警,不会真达到85%才告警,只有这样做才能留有足够的时间让运维和开发人员介入排查


 

无需追求完美,理性权衡利弊

在处理棘手问题或优化性能时,无需刻意追求完美,理性权衡利弊后,适当的做出一些决断,抛弃掉一部分不重要的,起码比整个系统挂掉要好,何况之后也同样可以恢复

性能调优通用步骤

性能优化永远是建立在性能瓶颈之上的,如果你的系统没有出现瓶颈,那则无需调优,过早的调优是一个很错误的选择,调优之前需要牢记的一点是:不要为了调优而调优,而是需要调优时才调

发现瓶颈有两种方式

  1. 你的应用中具备完善的监控系统,能够提前感知性能瓶颈的出现
  2. 应用中没有搭载监控系统,性能瓶颈已经发生,从而导致应用频繁宕机

性能优化步骤如下

  1. 发现性能瓶颈:如有监控系统,那它会主动发出警报;如若没有,那出现瓶颈时应用肯定会出问题,如:无响应、响应缓慢、频繁宕机等。
  2. 排查瓶颈原因:排查瓶颈是由于故障问题导致的,还是真的存在性能瓶颈。
  3. 定位瓶颈位置:往往一个系统都会由多个层面协同工作,然后对外提供服务,当发现性能瓶颈时,应当确定瓶颈的范围,如:网络带宽瓶颈、Java应用瓶颈、数据库瓶颈等。
  4. 解决性能瓶颈:定位到具体的瓶颈后对症下药,从结构、配置、操作等方面出发,着手解决瓶颈问题


 

MySQL性能优化实践

MySQL调优的五个维度

  1. 客户端与连接层的优化:调整客户端DB连接池的参数和DB连接层的参数。
  2. MySQL结构的优化:合理的设计库表结构,表中字段根据业务选择合适的数据类型、索引。
  3. MySQL参数优化:调整参数的默认值,根据业务将各类参数调整到合适的大小。
  4. 整体架构优化:引入中间件减轻数据库压力,优化MySQL架构提高可用性。
  5. 编码层优化:根据库表结构、索引结构优化业务SQL语句,提高索引命中率

五个性能优化项中,通常情况下,带来的性能收益排序为4 > 2 > 5 > 3 > 1


 

MySQL连接层优化策略

一个用户最终请求会在Java程序中分配一条线程处理, 最终变成一条SQL发往MySQL

首先明确, 理想情况下是数据库连接数越大越好, 当实际上硬件的性能有限, 没有满足, 所以这个命题就改成在一定的条件的情况下, 数据库的连接数越大越好

数据库连接数越大,也就意味着内部创建出的工作线程会越多,线程越多代表需要的CPU配置得更高,比如现在有300个客户端连接,内部创建了300条工作线程处理,但服务器的CPU仅有32个核心,那当前服务器最多只能支持32条线程同时工作,那其他268条线程怎么办呢?为了其他线程能够正常执行,CPU就会以时间片调度的模式工作,不同核心在不同线程间反复切换执行,但由于线程数远超核心数,因此会导致线程上下文切换的开销,远大于线程执行的开销

如果这里的时间片和线程上下文开销不理解, 那么请去借助AI获取其他的博客了解

由于硬件的资源限制, 所以数据库连接数如何配置成一个合适的值有讲究

连接池的种类

  1. 客户端连接池: 指Java自身维护的数据库连接对象, 例如Druid, C3P0等
  2. 服务端连接池: MySQL-Server的连接层中,自身维护的一个连接池,用来实现线程复用的目的

面试题: 为什么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执行时长, 大事务等


 

场景调优

偶发性高峰业务的连接数配置
  1. 公式: 最大连接数 = (CPU核心数 * 2) + 有效磁盘数,常驻连接数 = CPU核数 + 1
  2. 缩短连接的存活时长
  3. 及时释放空闲数据库连接


 

分库分表的连接数配置
  1. 根据节点的硬件配置, 硬件的不同就会导致所能承受的压力也不同

连接数优化注意事项

对于最佳连接数的计算,首先要把CPU核数放首位考虑,紧接着是磁盘,最后是网络带宽,因为带宽会影响SQL执行时间,综合考虑后才能计算出最合适的连接数大小


 

MySQL结构优化方案

这里对B+树一定熟悉, 不然是云里雾里, 如果不熟悉的, 那么请去了解后再来, 该文不会进行细致的讲解

结构优化指的是

  1. 表结构
  2. 字段结构
  3. 索引结构

表结构
  1. 表的字段不要太多(重中之重)
    • 原因: InnoDB会将部分数据加载到内存中再操作, 如果一个表的字段很多, 那么能加载的数据就少
    • 一张表最多最多只能允许设计30个字段左右,否则会导致查询时的性能明显下降
    • 实际中一个表的字段通常由冷热字段之分, 可以考虑划分成两张表, 一个热字段的表, 一个冷字段的表(冷热数据思想)
  2. 如果某个字段连表查询很频繁, 那么可以考虑冗余该字段
  • 减少了连表查询次数(空间换时间思想), 查询数据提高了, 但是需要额外的维护成本, 适用查多改的表
  • 不能无法冗余, 否则会造成之前提到的表字段过多的情况
  1. 必须要有主键, 并且是顺序递增, 主键必须和业务无关系
  2. 实时性要求不过的建立中间表
    • 例如统计数据(通常连表并且数据量不小), 提前查询好, 然后定时更新数据即可, 减少频繁连表查询开销
  3. 根据业务特性为每张不同的表选择合适的存储引擎
    • 查多该少的使用MyISAM, 例如字典表, 权限表等
    • 其余表使用InnoDB

字段结构
  1. 在保证足够使用的范围内,选择最小数据类型, 因为它们会占用更少的磁盘、内存和CPU缓存,同时在处理速度也会更快
    • 对于姓名字段,一般都会限制用户名长度,这时不要无脑用varchar,使用char类型更好。
    • 对于一些显然不会拥有太多数据的表,主键ID的类型可以从int换成tinyint、smallint、mediumit, 减少聚簇索引的大小, 二级索引的大小
    • 对于日期字段,不要使用字符串类型,而更应该选择datetime、timestamp,一般情况下最好为后者。
      • datetime: 8字节, 表示范围'1000-01-01 00:00:00'到'9999-12-31 23:59:59'
      • timestamp: 4字节, 表示范围'1970-01-01 00:00:01'到'2038-01-19 03:14:07'
    • 对于一些固定值的字段,如性别、状态、省份、国籍等字段,可以选择使用数值型代替字符串,如果必须使用字符串类型,最好使用enum枚举类型代替varchar类型。
  2. 尽量避免索引字段值为NULL,定义字段时应尽可能使用NOT NULL关键字,因为字段空值过多会影响索引性能
  3. 在条件允许的情况下,尽量使用最简单的类型代替复杂的类型,如IP的存储可以使用int而并非varchar,因为简单的数据类型,操作时通常需要的CPU资源更少

索引优化
  1. 索引字段组成尽量选择多个, 当有多个索引时考虑合并成联合索引
    • 减少磁盘空间, 以及利用索引覆盖减少回表操作
  2. 较长字符串建立索引时, 可以去前N个字符建立前缀索引
    • 注意了这个N个字符区分度一定要高
  3. 经常模糊查询的索引可以建立全文索引代替普通索引
  4. 某些不做范围查询的字段上建立所以可以采取hash结构


 

MySQL参数调优选项

调整InnoDB缓存区(buffer pool)

InnoDB缓冲区的最佳大小比例应该控制在70~75%

当InnoDB缓冲区大于1G的时, 会将缓冲区划分为多个实例空间, 多线程并发执行时, 减少并发冲突

MySQL官方的建议是每个缓冲区实例必须大于1GB

如果是8/16G, 那么建议设置1G大小

如果是32GB/64GB甚至更高, 那么建议设置成2GB或者更大

缓冲实例个数建议设置成物理内存的总量的1/3或1/2之间

调整工作线程的缓冲区
  1. sort_buffer, read_buffer, join_buffer都属于线程私有区域, 每一条线程都有这么一片区域
    • sort_buffer_size:排序缓冲区大小,影响**group by、order by...**等排序操作
    • read_buffer_size:读取缓冲区大小,影响**select...**查询操作的性能
    • join_buffer_size:联查缓冲区大小,影响join多表联查的性能

对于这些区域,最好根据机器内存来设置为一到两倍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中间件解决写压力

将实行要求没有这高的写请求放入MQ中, 其他线程按需拉去消费, 减少写压力

主从读写分离

架构模式

  1. 主从: 主负责读写, 从负责读, 适用于读多写少
    • 主从复制基于的是bin-log日志实现
  2. 双主双写热备: 两个节点互为主从, 双节点都具有读写能力, 并且都可以互相同步数据
    • 麻烦之处: 但是为了兼容两者之间的数据,对于每张表的主键要处理好,如果表的主键是int自增类型的,请一定要手动设置一下自增步长和起始值, 否则出现两条相同主键的数据情况, 通常可以设置一台只使用计数, 一台只使用偶数
  3. 分库分表
    • 垂直分库
    • 水平分表

单机优化MySQL的手段就是将版本升级到5.7版本

你可能感兴趣的:(MySQL,mysql,数据库,性能优化)