MySQL
优化
-
定位慢查询
-
SQL执行计划 :SQL分析能力
-
SQL优化经验:SQL优化
-
索引
- 存储引擎
- 索引底层数据结构
- 聚簇和非聚簇索引
- 索引创建原则
- 索引失效场景
其他
- 事务相关:事务特性 隔离级别 MVVC
- 主从同步原理:高并发
- 分库分表:海量数据存储
一.索引
1.什么是索引?
索引是MySQL 高效获取数据的数据结构(有序)
在数据之外,数据库系统还维护着满足特定查找算法的数据结构(B+树),这些数据结构以某种方式指向数据,这样就可以在这些数据结构上实现高级查找算法.
2.索引创建的原则
- 先陈述自己在工作中是如何运用的
- 主键索引
- 唯一索引
- 根据业务创建索引(复合索引)
1.针对数据量比较大,且查询比较频繁的表建立索引
单表如果超过10w数据 (增加用户体验)
目的就是查询表的时候速度更快
2.针对常作为 查询条件的 where order by 分组group by 操作的字段建立索引
3.尽量去选择区分度高的列作为索引,尽量建立唯一索引,区分度越高,使用索引的效率越高
这里的区分度就不高,所以尽量不使用这种字段作为索引
4.如果是字符串,字段的长度较长,可以针对于字段的特点,建立前缀索引
5.尽量使用联合索引,减少单列索引,查询时,联合索引很多时候可以覆盖索引(避免回表),节省存储空间,提高查询效率
6.需要控制索引的数量,索引并不是多多益善,索引越多,维护成本就越大,从而会影响增删改的效率
7.如果**索引列不能存储null值,请在创建表时使用NOT NULL 去约束它,**当优化器知道每列是否包含NULL值时,可以更好的确定哪个索引最有效地用于查询.
总结:
- 数据量大且查询比较频繁的表
- 常作为查询条件的 字段
- 字段内容区分度比较高
- 内容比较长 使用前缀索引
- 尽量联合索引
- 要控制索引的数量
- 如果索引不能存储null值,在创建表时使用NOT NULL去约束它
3索引的底层数据结构是什么?
B+树
MySQL的InnoDB引擎采用的是B+树的数据结构来存储索引
- 阶树更多,路径更短
- 磁盘读写代价B+树更低, 非叶子结点只存储指针,叶子结点存储数据
- B+树 便于扫库 和区间查询,叶子结点是一个 双向链表
数据结构对比
B树 是一种多叉路衡查找树,相当于二叉树,B树每个节点可以有多个分支,多叉.
4.B+树:
在B树的基础上的一种优化,更适合实现外存储索引结构,InnoDB存储引擎就是用B+Tree实现其索引结构
- 区别在于
非叶子结点只存储指针不存储数据
叶子结点才会真正的去存储数据
- 磁盘读写代价B+树更低
- 查询效率B+树更加稳定
- B+树便于扫库和区间查询
结点直接存在双向指针,相当于查询6 他可以一次性把所有的数据都拿到
总结:
- 索引是帮助MySQL高效获取数据的数据结构
- 是为了提高数据检索的效率,降低数据库的IO成本(不需要全表扫描)
- 通过索引对数据进行排序,降低数据排序的成本,降低了CPU的消耗
5.什么是聚簇索引什么是非聚簇索引? 什么是回表?
什么是聚集索引,什么是二级索引(非聚簇索引)
什么是回表?
- 聚餐索引
将 数据存储与 索引放到一块,索引结构的叶子节点保存了行数据,必须有,而且只有一个
- 二级索引
将 数据与 索引分开存储,索引结构的叶子节点关联的对应的主键, 可以存在多个
- 聚簇索引选取规则:
- 如果存在主键,主键索引就是聚集索引
- 如果不存在主键,将使用唯一 UNIQUE 索引 作为聚集索引
- 如果表没有主键,或者没有合适的唯一索引,InnoDB会自动生成一个rowid作为隐藏的聚集索引
- 回表查询
先通过二级索引找到对应的主键值,拿到主键再到聚集索引中查询出整行的数据,这个过程叫做回表查询
总结:
- 聚集索引(聚簇索引):数据与索引放到一块,B+树的叶子节点保存的整行的数据,有且只有一个
- 二级索引:数据与索引分开存储,B+树的叶子结点上存储的是对应的 主键,可以有多个
- 什么是回表查询?
- 通过二级索引找到对应的主键值,然后再到聚簇索引中查询出整行的数据,这个过程就是回表查询
6.什么情况下索引会失效?
7.如何去快速判断索引是否失效呢
执行计划explain
name status address
- 违法了最左前缀法则
失效的情况:
- 范围查询右边的列不能使用索引
- 不要在索引列上进行运算操作,索引将失效
这里使用的substring进行了运算操作,且在索引列上
- 字符串不加单引号,造成索引失效
查询的时候,没有对字符串加单引号,MySQL 查询优化器,会自动的进行类型转换, 造成索引失效
- 模糊查询有可能会导致索引失效
总结:
- 违反了最左前缀法则: 使用复合索引,不能跳过某一列去查询
- 范围查询右边的列,不能使用索引
- 不要在索引上进行运算操作,索引会失效
- 字符串不加单引号,造成索引失效(类型转换)
- 以百分号% 开头的Like模糊查询,索引失效
二.MySQL中,如何定位慢查询
- 聚合查询
- 多表查询
- 表数据量过大查询
- 深度分页查询
表象:页面加载过慢、接口压测响应时间过长
方案一:开源工具
- 调试工具:Arthas
- 运维工具:Skywalking Prometheus
方案二:MySQL自带慢日志查询
执行慢的SQL执行在日志文件中
默认MySQL没有开启我们只需要在配置文件中开启就行
/etc/my.cnf
# 开启MySQL慢日志查询开关
slow_query_log=1
# 设置慢查询的时间为2s SQL语句执行时间超过2s
long_query_time=2
# 配置完毕之后,通过指令重启MySQL服务器进行测试,查看慢日志文件中的记录
/var/lib/mysql/localhost-slow.log
总结:
- 介绍当时问题问题产生的场景
- 我们系统中采用了skywalking 可以检测出哪个接口,最终原因是sql的问题
- 在mysql中开启慢查询日志 ,一旦sql查询超过2s就会记录到日志中
三.sql语句执行的很慢,如何分析
SQL语句执行的很慢如何去分析呢?
- 聚合查询
- 多表查询
- 表数据量过大查询: 添加索引
- 深度分页查询
前三个可以通过 SQL执行计划找到慢的原因
EXPLAIN DESC命令获取到MySQL如何执行SELECT 语句信息
直接在select语句之前加上 explain/desc
这些字段不需要全部掌握
- possible_key :当前sql可能会使用到的索引
- key 当前sql 实际命中的索引
- key_len 索引占有的大小
- Extra 额外的优化建议
通过查看是否命中索引就是看 key 和key_len
出现了Using index condition 说明索引的使用是有优化的空间的
- type sql连接的类型 null system const eq_ref ref range index all
越往左边性能越好
system :查询系统中的表
const:根据主键查询
eq_ref:主键索引查询或者唯一索引查询
ref:索引查询
range :范围查询
index:索引树扫描
all:全盘扫描
总结:
如果 SQL执行很慢,如何分析
- 我们可以采用MySQL自带的分析工具Explain
- 通过查看key 和key_len检查是否命中了索引 (索引本身存在是否失效的问题)
- 通过查看 type字段查看sql是否有进一步的优化空间,是否存在全索引扫描或者全盘扫描
- 通过extra建议来判断,是否出现了回表的情况,如果出现了可以添加索引或者修改返回字段来修复
四.谈谈SQL优化经验
- 表的设计优化
- 索引优化 : 参考优化创建原则和索引失效的原则
- SQL语句优化
- 主从复制、读写分离
- 分库分表 后面专门章节介绍
1.表设计的优化
参考阿里开发手册 嵩山版本
2.SQL语句优化
-
避免使用
select*
有可能会造成回表查询 -
SQL语句要避免索引失效的写法
-
尽量 使用
union all
代替union
,union
会多一次过滤,效率低 -
避免在where子句中对字段进行表达式操作,例如substring 索引失效的时候讲到过 在索引字段上进行表达式操作
-
Join 优化 能用 inner join 就不用 left join right join,如果必须使用一定要以小表驱动,内连接会对两个表进行优化,优先把小表放到外边,把大表放到里面。 left join 或 right join ,不会重新调整顺序
比如说:
外面的是小循环是小表,里面大循环是大表 3次连接1000次操作 和1000次连接3次操作
3.主从复制、读写分离
如果数据库的使用场景读的操作比较多的时候,为了避免写的操作造成的性能影响,可以采用读写分离的架构,为了解决数据库的写入,影响了查询的效率
在生成环境下 经常主从复制 读写分离
总结:
- 表设计优化,数据类型选择 char varchar
- 索引优化
- sql语句优化,避免索引失效,避免使用select *
- 主从复制,读写分离,不让数据的写入,影响读操作
- 分库分表
五.主从同步原理
数据库通过中间件连接了两个数据库,主库从库,主库负责写数据 从库负责读数据,那么如何进行同步的呢 原理是什么?
主从复制的核心就是二进制日志
二进制日志 binlog记录了所有的DDL 数据库定义语言,和DML 数据库操作语言,但不包括数据的查询 SELECT SHOW 语句
IOthread
线程专门负责去读取 主库的binlog
日志,读取完成之后就写入到从库的中继日志中relay log
,然后再由从库的SQ
Lthread`线程去读取 中继日志的文件,然后再去执行 这样主库和从库的数据就保持了一致
- Master主库在事务提交的时候,会把数据变更记录在二进制日志文件
Binlog
中。 - 从库读取主库的
binlog
,写入到从库的中继日志Relay log
- slave重做中继日志中的事件,将改变反映成自己的数据。
六.分库分表
- 前提: 单表数据量达到 1000w 和 20G
- 项目业务数据逐渐增多,或业务发展比较迅速
- 优化已经解决不了性能问题(主从读写分离,查询索引)
- IO瓶颈(磁盘IO 网络IO),CPU瓶颈(聚合查询,连接数太多)
1.拆分策略
垂直拆分
- 垂直分库
- 垂直分表
- 垂直分库
以表为依据,根据业务将不同表拆分到不同的库中。
- 垂直分表
以字段为依据,根据字段属性将不同字段拆分到不同表中
拆分规则
- 将不常用的字段单独放在一张表中
- 将text blog等大字段拆分出来放在附表中
比如感兴趣才把详情展示给用户
可能是在同一数据库下拆分成两张表
核心就是
- 冷热数据分离
- 减少IO 过度争抢,两表互不影响
水平拆分
- 水平分库:将一个库中的数据拆分到多个库中
- 水平分表:将一个表的数据拆分到多个表中(可以在同一个数据库)
路由规则
- 按照id进行取模
- 按id就是路由范围
多个库共同去存储业务的数据,海量数据存储问题,多个节点也能解决高并发的问题
水平分表
2.分库产生的问题
- 分布式事务一致性问题
- 跨界点关联的问题
- 跨界点进行分页,排序函数
- 主键避冲:比如水平分库 每个库中都存储相同的表 每个表中的数据都是自增的
为了解决增加分库分表的中间件
- MyCat
- sharding-sphere
七.事务(重要)
1.事务的特性是什么 ?
什么是事务?
事务是一组操作的集合,它是一个不可分割的工作单位,事务会把所有的操作作为一个整体一起向系统提交活撤销操作,这些操作要么同时成功,要么同时失败
ACID:
- 原子性:Atomicity:事务是不可分割的最小操作单元,要么全部成功,要么全部失败
- 一致性:Consistencey:事务完成时,必须所有数据保持一致状态
- 隔离性:Isolation:数据库系统提供的隔离机制,保证事务在不受外部并发影响的独立环境下运行
- 持久性:Durability:事务一旦提交或者回滚,他对数据库的改变就是永久的
2.并发事务带来了哪些问题?怎么解决?MySQL的默认隔离级别是?
- 并发事务问题: 脏读 不可重复读 幻读
- 隔离界别:读未提交 读已提交 可重复读 串行化
3.MySQL并发事务问题:
- 脏读:一个事务读到了另外一个事务还没有提交的数据
事务A还没有提交但是事务B可以读到最新事务修改之后的数据, 一个事务读到了另外一个事务还没有提交的数据
- **不可重复读:**一个事务先后读取同一条记录,但是两次读取的数据不同,称之为不可重复读.
- 幻读:一个事务按照条件查询的时,没有对应的数据行,但是在插入数据时,又发现这行数据已经存在,好像出现了 幻影
前提是已经解决了不可重复读的问题
一个事务内,查询同一条数据都是相同的数据 (1 ,4 相同) 但是执行3插入的时候依然提示存在这个数据这就像出现了幻觉一样
4.MySQL事务隔离界别:
- 未提交度 (一般不用)
- 读已提交: 可以解决脏读 不可重复读 幻读
- 可重复读: MySQL默认隔离级别 可以解决不可重复读,脏读 不能解决幻读 Repeatable Read
- 串行化: 可以解决所有问题,但是性能很低
事务隔离界别越往下,安全级别越高,性能越低
八.存储引擎
1.MySQL体系结构
存储引擎控制的就是MySQL数据的存储和提取的方式 ,服务器通过API和存储引擎来进行通信进行交互,Index索引是在存储引擎层实现的,不同引擎存储的结构是不一样的
InnoDB是MySQL 5.5版本之后默认的存储引擎
MySQL体系结构
存储引擎简介:
存储引擎特点:
存储引擎选择:
2.存储引擎简介:
什么是存储引擎?
默认存储引擎 : InnoDB
相当于发动机是一个机器的核心
存储引擎就是存储数据,建立索引,更新/查询数据等技术的实现方式,存储引擎是基于表而不是基于库的,所以存储引擎也可以被称为表类型
一个数据库中的多张表可以选择不同的存储引擎
查看当前数据库支持的存储引擎
show engines;
InnoDB 默认存储引擎 支持事务 支持外键
3.InnoDB
MySQL中InnoDB是一种兼顾了高可靠性和高性能的通用存储引擎, 5.5 之后作为 默认存储引擎
- DML操作支持ACID,和支持事务
- 行级锁,提高并发访问性能
- 支持外键 FOREIGH KEY,保证数据的完整性和正确性
文件
col 值得是一个一个的字段
4.MyISAM
MyISAM是MySQL早期的默认存储引擎
- 不支持事务,不支持外键
- 支持表锁,不支持行锁
- 访问速度快
5.Memery
Memory 引擎的表数据是存储在内存中的,由于受到硬件问题,或断电问题的影响,只能将这些表作为临时表或缓存使用
- 内存存放
- hash索引(默认)
6.区别:
如何选择: