文章参考:https://joonwhee.blog.csdn.net/article/details/106893197
问题:如何做慢 SQL 优化?
首先要搞明白慢的原因是什么:是查询条件没有命中索引?还是 load 了不需要的数据列?还是数据量太大?所以优化也是针对这三个方向来的:
首先用 explain 分析语句的执行计划,查看使用索引的情况,是不是查询没走索引,如果可以加索引解决,优先采用加索引解决。
分析语句,看看是否存在一些导致索引失效的用法,是否 load 了额外的数据,是否加载了许多结果中并不需要的列,对语句进行分析以及重写。
如果对语句的优化已经无法进行,可以考虑表中的数据量是否太大,如果是的话可以进行垂直拆分或者水平拆分。
1. 水平拆分与垂直拆分
1.1 水平分表
例:QQ的登录表
假设QQ的用户有100亿,如果只有一张表,每个用户登录的时候数据库都要从这100亿中查找,会很慢很慢。如果将这一张表分成100份,每张表有1亿条,就小了很多,比如qq0,qq1,qq1...qq99表。
用户登录的时候,可以将用户的id%100,那么会得到0-99的数,查询表的时候,将表名qq + 取模的数连接起来,就构建了表名。比如123456789用户,取模的89,那么就到qq89表查询,查询的时间将会大大缩短。
1.2 垂直分表
垂直分割指的是:表的记录并不多,但是字段却很长,表占用空间很大,检索表的时候需要执行大量的IO,严重降低了性能。这时需要把大的字段拆分到另一个表,并且该表与原表是一对一的关系。
例如:学生答题表student_question,有如下5个字段:
Id,name,分数,题目,回答。其中题目和回答是比较大的字段,Id,name,分数比较小。
如果我们只想查询id为8的学生的分数:select 分数 from tt where id = 8;
虽然只是查询分数,但是题目和回答这两个大字段也是要被扫描的,很消耗性能。然而我们只需要关心分数,并不想查询题目和回答。这种情况下就可以使用垂直分割。
我们可以把题目单独放到一张表中,通过id与tt表建立一对一的关系,同样将回答单独放到一张表中。这样我们查询student_question中的分数的时候就不会扫描题目和回答这两个大字段了。
1.3 小结
水平分割是表中数据量过大,严重影响查询效率时,将1张数据量庞大的表按照某种条件进行拆分成N张名称不同字段和数据类型相同的表。
垂直分割是表中记录数不多,但是字段很多,且字段长,表占用空间大的情况下,把大的字段拆分到另一个表,并且该表与原表是一对一的关系。
2. 主从复制
参考文章:MySQL主从复制读写分离,看这篇就够了
2.1 MySQL主从复制介绍
MySQL主从复制涉及到三个线程,一个运行在主节点(Log Dump Thread),其余两个(I/O Thread,SQL Thread)运行在从节点,如下图所示
主从复制默认是异步的模式,具体过程如下:
从节点上的 I/O 线程连接主节点,请求读取主库的二进制日志文件(bin log 日志)的指定位置(bin log position)之后的日志内容;
主节点线程接收到来自从节点 I/O 线程的请求后,读取主节点中的二进制日志文件(bin log 日志)的指定位置之后的日志信息,返回给从节点。
返回给从节点的内容信息中除了日志所包含的信息之外,还包括本次返回的信息的 bin-log file(二进制日志文件) 以及 bin-log position(读取的位置);
从节点的 I/O 线程接收到内容后,将接收到的日志内容更新到 relay log (中继日志)中,并将读取到的 bin log file(文件名)和position(位置)保存到 master-info 文件中,以便在下一次读取的时候能够清楚的告诉 Master “我需要从某个bin-log 的哪个位置开始往后的日志内容”
从节点的 SQL 线程检测到 relay-log 中新增加了内容后,会解析 relay-log 的内容,并在本数据库中执行。
2.2 异步复制,主库宕机后,数据可能丢失?
可以使用半同步复制或全同步复制。
半同步复制:
修改语句写入bin log后,不会立即给客户端返回结果。而是首先通过log dump 线程将 binlog 发送给从节点,从节点的 I/O 线程收到 binlog 后,写入到 relay log,然后返回 ACK 给主节点,主节点 收到 ACK 后,再返回给客户端成功。
半同步复制的特点:
确保事务提交后 binlog 至少传输到一个从库,不保证从库应用完这个事务的 binlog。
性能有一定的降低,响应时间会更长。
网络异常或从库宕机,卡主主库,直到超时或从库恢复。
全同步复制:主节点和所有从节点全部执行了该事务并确认才会向客户端返回成功。因为需要等待所有从库执行完该事务才能返回,所以全同步复制的性能必然会收到严重的影响。
2.3 主库写压力大,从库复制很可能出现延迟?
可以使用并行复制(并行是指从库多个SQL线程并行执行 relay log),解决从库复制延迟的问题。
MySQL 5.7 中引入基于组提交的并行复制,其核心思想:一个组提交的事务都是可以并行回放,因为这些事务都已进入到事务的 prepare 阶段,则说明事务之间没有任何冲突(否则就不可能提交)。
判断事务是否处于一个组是通过 last_committed 变量,last_committed 表示事务提交的时候,上次事务提交的编号,如果事务具有相同的 last_committed,则表示这些事务都在一组内,可以进行并行的回放。