MySQL相关(终结篇一)- 性能优化(配置及架构)

前言

关于前面讲过的知识点我就再也不赘述了,还没看过的朋友能够进入个人首页进行查阅(前言部分附赠飞机票)。这篇文章将是整个专题的总结,并且也是面试官会问到的最高频率的一个问题——“你对 MySQL 的性能优化有什么想法?”php

不少出去面试的朋友应该基本上都会被问到这个问题,可是可能可以回答得尽善尽美的比较少,看过我专题且可以消化成本身肚子里的东西的朋友应该能够吊打面试官了哈哈哈哈(针对中高级),但愿今天这篇文章以后你们可以对本身脑海中零散知识点进行整合整理,我盼着大家能回来给我报喜(固然吐苦水也能够),也盼着能跟你们一块儿不断进步。html

回到正题,关于此次的 MySQL 性能优化的知识点,我会分红两篇幅的文章来输出,关于 SQL 语句的性能优化我会以单独的篇幅来进行编写,语句优化在实操中是属于性能优化的最高级别的优化点,但愿你们也能好好消化。mysql

这个 MySQL 专题是我从年前就一直在准备的,恰好过年在家也没事就一直在思考着要怎么去发表这部分的文章,让你们可以看的时候思路比较清晰,记忆可以更加深入,最后我是经过先发布脑图,而后再根据脑图的方向进行专题知识点的发表,以后应该也会是这种形式,毕竟,这样我写文章思路清晰,你们看文章的时候思路也清晰嘛,复习知识点的时候也能够根据脑图来。git

博文是我从去年开始写的,以前是本身在云笔记上作笔记比较多。我以为作笔记写总结对自我提高有很大的帮助,而分享出来,也是但愿你们可以从中学到新的知识,同时也能帮助我一块儿不断改进,给我提一些建议,让我在给你们分享总结的东西的同时本身也能查缺补漏(再次感谢一直以来支持个人朋友们 Thanks♪(・ω・)ノ)程序员

关于下一个专题我还没想好要写什么,你们若是有什么想法的话能够在公众号给我留言。github

老规矩,先上飞机票:面试

  1. MySQL相关(一)- 一条查询语句是如何执行的
  2. MySQL相关(二)- 一条更新语句是如何执行的
  3. MySQL相关(番外篇)- innodb 逻辑存储结构
  4. MySQL相关(三)- 索引数据模型推演及 B+Tree 的详细介绍
  5. MySQL相关(四)- 性能优化关键点索引
  6. MySQL相关(五)- 事务特性及隔离级别的详细介绍
  7. MySQL相关(六)- 事务隔离级别的实现方案(MVCC)
  8. MySQL相关(七)- innodb 锁的介绍及使用
  9. MySQL相关(八)- innodb行级锁深刻剖析
  10. MySQL相关(九)- 死锁的发生和避免

前面提到的脑图以下,想要完整高清图片能够到微信个人公众号下【6曦轩】下回复 MySQL 脑图获取: sql

在这里插入图片描述

正文

优化思路

容我再絮絮不休

做为架构师或者开发人员,说到数据库性能优化,你的思路是什么样的?shell

或者具体一点,若是在面试的时候遇到这个问题:你会从哪些维度来优化数据库,你会怎么回答?数据库

经过前面的篇章,相信你们已经慢慢创建了数据库的知识体系,和正确的调优的思路。

咱们说到性能调优,大部分时候想要实现的目标是让咱们的查询更快。一个查询的动做又是由不少个环节组成的,每一个环节都会消耗时间,第一篇章里讲 SQL 语句的执行流程的时候已经分析过了。

咱们要减小查询所消耗的时间,就要从每个环节入手(先上你们比较熟悉的一张图)。

在这里插入图片描述

链接——配置优化

第一个环节是客户端链接到服务端,链接这一块有可能会出现什么样的性能问题?

有多是服务端链接数不够致使应用程序获取不到链接。好比报了一个 Mysql: error 1040: Too many connections的错误。

咱们能够从两个方面来解决链接数不够的问题:

  1. 从服务端来讲,咱们能够增长服务端的可用链接数。

若是有多个应用或者不少请求同时访问数据库,链接数不够的时候,咱们能够: (1)修改配置参数增长可用链接数,修改 max_connections 的大小:
show variables like 'max_connections'; -- 修改最大链接数,当有多个应用链接的时候
(2)或者,或者及时释放不活动的链接。交互式和非交互式的客户端的默认超时时间都是 28800 秒,8 小时,咱们能够把这个值调小。
show global variables like 'wait_timeout'; --及时释放不活动的链接,注意不要释放链接池还在使用的链接

  1. 从客户端来讲,能够减小从服务端获取的链接数,若是咱们想要不是每一次执行 SQL 都建立一个新的链接,应该怎么作?

这个时候咱们能够引入链接池,实现链接的重用。
咱们能够在哪些层面使用链接池?ORM 层面(MyBatis 自带了一个链接池);或者使用专用的链接池工具(阿里的 Druid、Spring Boot 2.x 版本默认的链接池 Hikari、老牌的 DBCP 和 C3P0)。
当客户端改为从链接池获取链接以后,链接池的大小应该怎么设置呢?你们可能会有一个误解,以为链接池的最大链接数越大越好,这样在高并发的状况下客户端能够获取的链接数更多,不须要排队。
实际状况并非这样。链接池并非越大越好,只要维护必定数量大小的链接池,其余的客户端排队等待获取链接就能够了。有的时候链接池越大,效率反而越低。

  • Druid 的默认最大链接池大小是 8。Hikari 的默认最大链接池大小是 10。为何默认值都是这么小呢?

在 Hikari 的 github 文档中,给出了一个 PostgreSQL 数据库建议的设置链接池大小的公式: github.com/brettwooldr…
它的建议是机器核数乘以 2 加 1。也就是说,4 核的机器,链接池维护 9 个链接就够了。这个公式从必定程度上来讲对其余数据库也是适用的。这里面还有一个减小链接池大小实现提高并发度和吞吐量的案例。

  • 为何有的状况下,减小链接数反而会提高吞吐量呢?为何建议设置的链接池大小要跟 CPU 的核数相关呢?

每个链接,服务端都须要建立一个线程去处理它。链接数越多,服务端建立的线程数就会越多。
CPU 是怎么同时执行远远超过它的核数大小的任务的?时间片。上下文切换。
而 CPU 的核数是有限的,频繁的上下文切换会形成比较大的性能开销。

咱们这里说到了从数据库配置的层面去优化数据库。无论是数据库自己的配置,仍是安装这个数据库服务的操做系统的配置,对于配置进行优化,最终的目标都是为了更好地发挥硬件自己的性能,包括 CPU、内存、磁盘、网络。

在不一样的硬件环境下,操做系统和 MySQL 的参数的配置是不一样的,没有标准的配置。前面的篇章中咱们也接触了不少的 MySQL 和 InnoDB 的配置参数,包括各类开关和数值的配置,大多数参数都提供了一个默认值,好比默认的 buffer_pool_size,默认的页大小,InnoDB 并发线程数等等。

这些默认配置能够知足大部分状况的需求,除非有特殊的需求,在清楚参数的含义的状况下再去修改它。修改配置的工做通常由专业的 DBA 完成。

至于硬件自己的选择,好比使用固态硬盘,搭建磁盘阵列,选择特定的 CPU 型号这些,更不是咱们开发人员关注的重点,这个咱们就不作过多的介绍了。

若是想要了解一些特定的参数的含义,官网有一份系统的参数列表能够参考:

dev.mysql.com/doc/refman/…

  • 除了合理设置服务端的链接数和客户端的链接池大小以外,咱们还有哪些减小客户 端跟数据库服务端的链接数的方案呢?

咱们能够引入缓存。

缓存——架构优化

缓存

在应用系统的并发数很是大的状况下,若是没有缓存,会形成两个问题:一方面是会给数据库带来很大的压力。另外一方面,从应用的层面来讲,操做数据的速度也会受到影响。

咱们能够用第三方的缓存服务来解决这个问题,例如 Redis。

在这里插入图片描述
运行独立的缓存服务,属于架构层面的优化。

为了减小单台数据库服务器的读写压力,在架构层面咱们还能够作其余哪些优化措施?

主从复制

若是单台数据库服务知足不了访问需求,那咱们能够作数据库的集群方案。

集群的话必然会面临一个问题,就是不一样的节点之间数据一致性的问题。若是同时读写多台数据库节点,怎么让全部的节点数据保持一致?

这个时候咱们须要用到复制技术(replication),被复制的节点称为 master,复制的节点称为 slave。slave 自己也能够做为其余节点的数据来源,这个叫作级联复制。

在这里插入图片描述

主从复制是怎么实现的呢?更新语句会记录 binlog,它是一种逻辑日志。

有了这个 binlog,从服务器会获取主服务器的 binlog 文件,而后解析里面的 SQL 语句,在从服务器上面执行一遍,保持主从的数据一致。

这里面涉及到三个线程,链接到 master 获取 binlog,而且解析 binlog 写入中继日志,这个线程叫作 I/O 线程。

Master 节点上有一个 log dump 线程,是用来发送 binlog 给 slave 的。

从库的 SQL 线程,是用来读取 relay log,把数据写入到数据库的。

在这里插入图片描述

作了主从复制的方案以后,咱们只把数据写入 master 节点,而读的请求能够分担到 slave 节点。咱们把这种方案叫作读写分离。

在这里插入图片描述

读写分离能够必定程度低减轻数据库服务器的访问压力,可是须要特别注意主从数据一致性的问题。若是咱们在 master 写入了,立刻到 slave 查询,而这个时候 slave 的数据尚未同步过来,怎么办?

因此,基于主从复制的原理,咱们须要弄明白,主从复制到底慢在哪里?

单线程

在早期的 MySQL 中,slave 的 SQL 线程是单线程。master 能够支持 SQL 语句的并行执行,配置了多少的最大链接数就是最多同时多少个 SQL 并行执行。

而 slave 的 SQL 却只能单线程排队执行,在主库并发量很大的状况下,同步数据确定会出现延迟。

为何从库上的 SQL Thread 不能并行执行呢?举个例子,主库执行了多条 SQL 语句,首先用户发表了一条评论,而后修改了内容,最后把这条评论删除了。这三条语句在从库上的执行顺序确定是不能颠倒的。

insert into user_comments (10000009,'nice');
update user_comments set content ='very good' where id =10000009; 
delete from user_comments where id =10000009;
复制代码

那么怎么解决这个问题呢?怎么减小主从复制的延迟?

异步与全同步

首先咱们须要知道,在主从复制的过程当中,MySQL 默认是异步复制的。也就是说,对于主节点来讲,写入 binlog,事务结束,就返回给客户端了。对于 slave 来讲,接收到 binlog,就完事儿了,master 不关心 slave 的数据有没有写入成功。

在这里插入图片描述
若是要减小延迟,是否是能够等待所有从库的事务执行完毕,才返回给客户端呢?这样的方式叫作全同步复制。从库写完数据,主库才返回给客户端。

这种方式虽然能够保证在读以前,数据已经同步成功了,可是带来的反作用你们应该能想到,事务执行的时间会变长,它会致使 master 节点性能降低。

有没有更好的办法呢?能够既减小 slave 写入的延迟,又不会明显增长 master 返回给客户端的时间?

半同步复制

介于异步复制和全同步复制之间,还有一种半同步复制的方式。

半同步复制是什么样的呢?

主库在执行完客户端提交的事务后不是马上返回给客户端,而是等待至少一个从库接收到 binlog 并写到 relay log 中才返回给客户端。master 不会等待很长的时间,可是返回给客户端的时候,数据就即将写入成功了,由于它只剩最后一步了:就是读取 relay log,写入从库。

在这里插入图片描述
若是咱们要在数据库里面用半同步复制,必须安装一个插件,这个是谷歌的一位工程师贡献的。这个插件在 mysql 的插件目录下已经有提供:

cd /usr/lib64/mysql/plugin/

主库和从库是不一样的插件,安装以后须要启用:

--主库执行

INSTALL PLUGIN rpl_semi_sync_master SONAME 'semisync_master.so';
set global rpl_semi_sync_master_enabled=1;
show variables like '%semi_sync%';
复制代码

--从库执行

INSTALL PLUGIN rpl_semi_sync_slave SONAME 'semisync_slave.so'; 
set global rpl_semi_sync_slave_enabled=1; 
show global variables like '%semi%';
复制代码

相对于异步复制,半同步复制提升了数据的安全性,同时它也形成了必定程度的延迟,它须要等待一个 slave 写入中继日志,这里多了一个网络交互的过程。因此,半同步复制最好在低延时的网络中使用。

这个是从主库和从库链接的角度,来保证 slave 数据的写入。

另外一个思路,若是要减小主从同步的延迟,减小 SQL 执行形成的等待的时间,那有没有办法在从库上,让多个 SQL 语句能够并行执行,而不是排队执行呢?

多库并行复制

怎么实现并行复制呢?设想一下,若是 3 条语句是在三个数据库执行,操做各自的数据库,是否是确定不会产生并发的问题呢?执行的顺序也没有要求。固然是,因此若是是操做三个数据库,这三个数据库的从库的 SQL 线程能够并发执行。这是 MySQL 5.6 版本里面支持的多库并行复制。

在这里插入图片描述

可是在大部分的状况下,咱们都是单库多表的状况,在一个数据库里面怎么实现并行复制呢?或者说,咱们知道,数据库自己就是支持多个事务同时操做的;为何这些事务在主库上面能够并行执行,却不会出现问题呢?

由于他们自己就是互相不干扰的,好比这些事务是操做不一样的表,或者操做不一样的行,不存在资源的竞争和数据的干扰。那在主库上并行执行的事务,在从库上确定也是能够并行执行,是否是?好比在 master 上有三个事务同时分别操做三张表,这三个事务是否是在 slave 上面也能够并行执行呢?

异步复制之 GTID 复制

dev.mysql.com/doc/refman/…

因此,咱们能够把那些在主库上并行执行的事务,分为一个组,而且给他们编号,这一个组的事务在从库上面也能够并行执行。这个编号,咱们把它叫作 GTID(Global Transaction Identifiers),这种主从复制的方式,咱们把它叫作基于 GTID 的复制。

在这里插入图片描述

若是咱们要使用 GTID 复制,咱们能够经过修改配置参数打开它,默认是关闭的:

show global variables like 'gtid_mode';
复制代码

不管是优化 master 和 slave 的链接方式,仍是让从库能够并行执行 SQL,都是从数据库的层面去解决主从复制延迟的问题。

除了数据库自己的层面以外,在应用层面,咱们也有一些减小主从同步延迟的方法。

咱们在作了主从复制以后,若是单个 master 节点或者单张表存储的数据过大的时候,好比一张表有上亿的数据,单表的查询性能仍是会降低,咱们要进一步对单台数据库节点的数据分型拆分,这个就是分库分表。

分库分表

垂直分库,减小并发压力。水平分表,解决存储瓶颈。

垂直分库的作法,把一个数据库按照业务拆分红不一样的数据库:

在这里插入图片描述

在这里插入图片描述

水平分库分表的作法,把单张表的数据按照必定的规则分布到多个数据库。

在这里插入图片描述

经过主从或者分库分表能够减小单个数据库节点的访问压力和存储压力,达到提高数据库性能的目的,可是若是 master 节点挂了,怎么办?

因此,高可用(High Available)也是高性能的基础。

高可用方案

dev.mysql.com/doc/mysql-h…

主从复制

传统的 HAProxy + keepalived 的方案,基于主从复制。

NDB Cluster

dev.mysql.com/doc/mysql-c…

基于 NDB 集群存储引擎的 MySQL Cluster。

在这里插入图片描述

Galera

galeracluster.com/

一种多主同步复制的集群方案。

在这里插入图片描述

MHA/MMM

tech.meituan.com/2017/06/29/…

MMM(Master-Master replication manager for MySQL),一种多主的高可用架构,是一个日本人开发的,像美团这样的公司早期也有大量使用 MMM。

MHA(MySQL Master High Available)。 BM和 MHA 都是对外提供一个虚拟 IP,而且监控主节点和从节点,当主节点发生故障的时候,须要把一个从节点提高为主节点,而且把从节点里面比主节点缺乏的数据补上,把 VIP 指向新的主节点。

MGR

dev.mysql.com/doc/refman/… dev.mysql.com/doc/refman/…

MySQL 5.7.17 版本推出的 InnoDB Cluster ,也叫 MySQL Group Replicatioin (MGR),这个套件里面包括了 mysql shell 和 mysql-route。

在这里插入图片描述
总结一下:

高可用 HA 方案须要解决的问题都是当一个 master 节点宕机的时候,如何提高一个数据最新的 slave 成为 master。若是同时运行多个 master,又必需要解决 master 之间数据复制,以及对于客户端来讲链接路由的问题。

不一样的方案,实施难度不同,运维管理的成本也不同。

以上是架构层面的优化,能够用缓存,主从,分库分表。

第三个环节: 解析器,词法和语法分析,主要保证语句的正确性,语句不出错就没问题。由 Sever 本身处理,跳过。

第四步:优化器 这一节内容我将经过新的篇章来说述,主要是 SQL 语句性能优化的方面。

总结:优化体系

在这里插入图片描述

除了对于代码、SQL 语句、表定义、架构、配置优化以外,业务层面的优化也不能忽视。举几个例子:

1)在某一年的双十一,为何会作一个充值到余额宝和余额有奖金的活动(充 300 送 50)?

由于使用余额或者余额宝付款是记录本地或者内部数据库,而使用银行卡付款,须要调用接口,操做内部数据库确定更快。

2)在去年的双十一,为何在凌晨禁止查询今天以外的帐单?

这是一种降级措施,用来保证当前最核心的业务。

3)最近几年的双十一,为何提早一个多星期就已经有双十一当天的价格了?预售分流。

在应用层面一样有不少其余的方案来优化,达到尽可能减轻数据库的压力的目的,好比限流,或者引入 MQ 削峰,等等等等。

为何一样用 MySQL,有的公司能够扛住百万千万级别的并发,而有的公司几百个并发都扛不住,关键在于怎么用。因此,用数据库慢,不表明数据库自己慢,有的时候还要往上层去优化。

后话

MySQL 专题到这个篇章就正式结束了,基本上是按照脑图的方向来,因此你们能够对着脑图以及个人博文来进行 MySQL 的复习,基本上面试的问题都有涉及,编写一个专题确实是费脑又费精力费时间的事情,我仍是须要你们的关注和点赞来支撑一下哈哈哈~

若是你们以为写得还有点东西的话帮忙关注一下个人公众号,而且在后台给我留言但愿我写哪一个专题的东西(现学现卖的若是有什么不对的地方也请帮忙指正,万分感谢),人多的话立刻安排上~

仍是那样,修整一段时间后会先在公众号推送脑图,而后根据脑图来拟专题的提纲,这样我写得不会云里雾里,你们也会比较有方向地看个人博文,再次感谢你们的支持~

By the way

有问题?能够给我留言或私聊 有收获?那就顺手点个赞呗~

固然,也能够到个人公众号下「6曦轩」,

回复“学习”,便可领取一份 【Java工程师进阶架构师的视频教程】~

回复“面试”,能够得到: 【本人呕心沥血整理的 Java 面试题】

回复“MySQL脑图”,能够得到 【MySQL 知识点梳理高清脑图】

因为我咧,科班出身的程序员,php,Android以及硬件方面都作过,不过最后仍是选择专一于作 Java,因此有啥问题能够到公众号提问讨论(技术情感倾诉均可以哈哈哈),看到的话会尽快回复,但愿能够跟你们共同窗习进步,关于服务端架构,Java 核心知识解析,职业生涯,面试总结等文章会不按期坚持推送输出,欢迎你们关注~~~

在这里插入图片描述
相关文章
相关标签/搜索