MySQL相关知识点
一、MySQL优化
目录
- 1.1 慢查询
- 1.2 索引
- 1.3 SQL优化的经验
- 1.4 事务相关 ACID
- 1.5 并发事务带来的问题
- 1.6 undo log 和 redo log的区别
- 1.7 MVCC
- 1.8 主从同步原理
- 1.9 分库分表
1.1 慢查询
1.1.1 如何定位慢查询?
慢查询表象:页面加载过慢
解决方案一 开源工具
调试工具:Arthas
运维工具:Prometheus、Skywalking
解决方案二 MySQL自带慢日志
慢查询日志记录了所有执行时间超过指定参数(long_query_time,单位:秒,默认10秒)的所有SQL语句的日志如果要开启慢查询日志,需要在MySQL的配置文件(/etc/my.cnf)中配置如下信息:
答:
- 介绍当时问题场景(压力测试时 某个接口响应时间很慢)
- 在mysql中开启了慢日志查询,设置2秒,一旦sql执行超过2秒就会被记录在日志中(调试阶段)
1.1.2 sql语句执行很慢 如何分析呢?
- 聚合查询(新增临时表)
- 多表查询(优化sql语句结构)
- 表数据量过大查询(添加索引)
- 深度分页查询
解决方案一 采用EXPLAIN 或者 DESC命令获取sql执行计划
- EXPLAIN 命令


type 越往左效率越高
答:
- 通过key和key len检查是否命中了索引(索引本身存在是否有失效的情况)
- 通过type字段查看sql是否有进一步的优化空间,是否存在全索引扫描或全盘扫描
- 通过extra建议判断,是否出现了回表的情况,如果出现了,可以尝试添加索引或修改返回字段来修复
1.2 索引
1.2.1 什么是索引?
- 索引(index)是帮助MSQL高效获取数据的数据结构(有序)。
- 提高数据检索的效率,降低数据库的IO成本(不需要全表扫描)
- 通过索引对数据进行排序,降低数据排序的成本,降低了CPU的使用
1.2.2 索引的底层数据结构
- 二叉树

- 红黑树

- B树

灰色是指针,蓝色是键值,绿色是数据 - B+树

1.非叶子节点只存储指针,叶子节点存储数据
2.B+树磁盘读写代价更低 查询效率B+树更稳定
3.B+树便于扫库和区间查询
答:
MySQL的InnoDB存储引擎是B+树结构
- 阶数更多,路径更短
- 磁盘读写代价B+树更低,非叶子节点只存储指针,叶子节点存储数据
- B+树便于扫库和区间查询
1.2.3 什么是聚簇索引(聚集索引)什么是非聚簇索引(二级索引)/什么是回表查询

聚集索引选取规则
- 如果存在主键 主键索引就是聚集索引
- 如果没有主键,则使用唯一索引
- 如果没有唯一索引,则使用普通索引 InnoDB会自动生成一个rowid作为隐藏聚集索引
答:
- 聚簇索引:数据和索引存储在同一个地方,索引的叶子节点存储了整行数据,有且只有一个。
- 非聚簇索引:数据与索引分开存储,B+树的叶子节点保存对应的主键,可以有多个
- 回表查询:查询数据时,通过二级索引找到对应主键值,到聚集索引中查找整行数据
1.2.4 什么是覆盖索引
覆盖索引是指查询使用了索引,并且需要返回的列,在该索引中已经全部能够找到
- 使用id查询 直接走聚集索引查询 一次索引扫描 直接返回数据 性能高
- 尽量避免使用select *
简单来说就是查询过程中不涉及回表查询
1.2.5 MySQL超大分页处理
数据量较大时,如果进行limit分页查询,越往后分页查询效率越低
优化思路:
一般分页查询时,通过创建覆盖索引能够比较好的提高性能,通过覆盖索引加子查询提高性能

先从分页的表中查询到表中需要的id,然后对表的id进行排序,就能筛选出分页后的id集合,因为id是覆盖索引,所以查询效率相对较高
1.2.6 创建索引的原则
- *数据量大,查询比较频繁的表建立索引 (单表超过10w)
- *对于常作为:查询条件,排序,分组操作的字段建立索引
- 选择区分度高的列作为索引,尽量建立唯一索引,区分度越高,使用索引效率越高
- 如果是字符串类型的字段,可以针对字段特点建立前缀索引
- *尽量使用联合索引,减少单列索引,查询时,联合索引很多时候可以覆盖索引,避免回表
- *控制索引数量
- 索引不能存储null值,创建表时用NOT NULL约束
答:
- 先陈述自己工作业务
- 超过10万条数据,建议建立索引
- 作为查询条件,排序字段或分组的字段
- 使用联合索引,返回值使用覆盖索引
- 字符串类型字段使用前缀索引
- 控制索引数量
1.2.7 什么时候索引会失效
带头大哥不能死,中间兄弟不能断,范围后面全失效,最佳左前缀,%放到最右边
答:
- 违反最左前缀法则
- 范围查询右边的列,不能使用索引
- 在索引上进行运算操作
- 字符串不加单引号(类型转换)
- %开头的模糊插叙导致索引失效
1.3 sql优化的经验
1.3.1 表的设计优化
- 主要参考阿里开发手册(嵩山版)
- 设置合适的数据(tinyint int bigint)
- 设置合适的字符串类型(char varchar)定长效率高
- SQL语句优化
- 避免直接使用select *
- sql语句要避免索引失效的写法
- 尽量使用union all代替union union会多一次过滤 效率低 (两条sql的并集)
- 避免在where上对字段进行表达式操作
- Join优化 能用innnerjoin就不用左连接/右连接,如必须使用一定要以小表为驱动 因为内连接会对两个表进行优化,优先把小表放在外面,大表放在里面。外连接不会重新调整顺序。
- 主从复制,读写分离
- 主库负责写,从库负责读
- 主从复制延迟问题
- 主从复制数据不一致问题
数据库读多写少,为避免写操作造成的性能影响,采用读写分离架构
- 索引优化(创建索引原则,索引失效的注意事项)
- 分库分表(详见1.6)数据量十分巨大时
1.4 事务相关 ACID
事务特性是什么:事务是一组操作的集合,它是一个不可分割的工作单位,事务会把所有的操作作为一个整体一起向系统提交或撤销操作请求,目即这些操作要么同时成功,要么同时失败。
(结合转账案例)
- 原子性(Atomicity)要么全部成功 要么全部失败
- 一致性(Consistency)事务完成时,所有数据保持一致(数据守恒)
- 隔离性(Isolation)不受外部影响 在独立环境下运行
- 持久性(Durability)事务一旦提交或回滚,对数据库中数据的改变是永久的
1.5 并发事务带来哪些问题? 怎么解决? Mysql默认隔离级别?
脏读,不可重复读,幻读
- 脏读:一个事务读取了另一个事务未提交的数据

- 不可重复读:一个事务读取了另一个事务已提交的数据,导致两次读取结果不一致

- 幻读:一个事务两次查询中得到了不同的结果集

不可重复读和幻读的区别:
| 项目 | 不可重复读 | 幻读 |
|---|---|---|
| 侧重 | 同一条记录的值发生变化 | 查询结果集中出现了新记录或消失 |
| 原因 | 其他事务更新或删除了已有记录 | 其他事务插入了新记录或删除记录 |
| 解决方式 | 可重复读隔离级别 | 串行化隔离级别 |
隔离级别:
越往下安全性越高 性能越低
- 读未提交(Read Uncommitted)
- 读已提交(Read Committed)
- 可重复读(Repeatable Read) 默认
- 串行化(Serializable)

解决办法:
- 脏读:使用读已提交隔离级别
- 不可重复读:使用可重复读隔离级别
- 幻读:使用串行化隔离级别
1.6 undo log 和 redo log的区别
- 缓冲池(buffer pool):主内存中的一个区域,里面可以缓存磁盘上经常操作的真实数据,在执行增删改査操作时,先操作缓冲池中的数据(若缓冲池没有数据,则从磁盘加载并缓存),以一定频率刷新到磁盘,从而减少磁盘10,加快处理速度。
- 数据页(page):是InnoDB 存储引擎磁盘管理的最小单元,每个页的大小默认为 16KB。页中存储的是行数据
redo log (重做日志): 记录物理日志 当事务提交之后会把所有修改的信息都存到该日志文件里,在用于刷新脏页到磁盘,发生错误时,进行数据恢复使用
undo log (回滚日志): 存储逻辑日志
redo log:记录数据页的物理变化,服务宕机可以用来同步数据
undo log:记录数据页的逻辑变化,事务回滚时,通过逆操作来恢复原来的数据,比如 我们delete一条数据 undo log就会记录一条insert语句
redo log 保证了事务的持久性,undo log 保证了事务的原子性和一致性
1.7 事务隔离性怎么保证的?(解释mvcc)
- 锁:排他锁
- mvcc:多版本并发控制 指的是维护一个数据的多个版本,使读写操作没有冲突
mvcc实现原理
隐式字段

undo log
- 版本链 不同事物或相同事务对同一条记录进行修改,会导致该记录的undo log生成一条记录版本链表,链表的头部是最新的旧记录,链表尾部是最早的旧纪录

- 版本链 不同事物或相同事务对同一条记录进行修改,会导致该记录的undo log生成一条记录版本链表,链表的头部是最新的旧记录,链表尾部是最早的旧纪录
readview (读视图):解决的是一个事务查询选择版本的问题
- 当前读:读取的是记录的最新版本 读取时还要保证其他并发事务不能修改当前记录,会对读取的记录进行加锁
- 快照读:读取的是记录的快照 有可能是历史数据,不加锁
- 读已提交:每次select都会生成一个快照读
- 可重复读:开启事务后第一个select语句是快照读的地方,之后的select语句都是当前读
- 四个核心字段:

1.8 MySQL主从同步原理
主从复制核心是二进制日志
BINLOG日志,记录了DDL 和 DML (不包括 select show)

答:
- 主库在事务提交时,会把数据变更记录在二进制日志文件 Binlog 中。
- 从库读取主库的二进制日志文件 Binlog ,写入到从库的中继日志 Relay Log。
- 从库重做中继日志中的事件,将改变反映它自己的数据。
1.9 分库分表
- 垂直分库:(微服务项目 高并发下提高性能)
- 以表为依据,依据业务将不同的表拆分到不同库中。
- 提高磁盘IO和数据量连接数
- 垂直分表:
- 以字段为依据,依据字段属性将不同的字段拆分到不同表中。
- 把不常用字段单独放在一张表(冷热数据分离)
- text blob等大字段拆分出来放在附表中
- 水平分库:(解决海量数据存储 高并发)
- 将一个库的数据库拆分到多个数据库中
- 根据id节点取模或者根据id区间进行路由
- 水平分表:
- 将一个表拆分到多个表中
- 根据id节点取模或者根据id区间进行路由
分库之后的问题
- 分布式事务一致性问题
- 跨节点关联查询
- 跨节点分页、排序函数
- 主键避重
解决方式:中间件:MyCat