MySQL
一、索引#
1 B树和B+树#
B树(B-树)特点#
B树和B+树是平衡多路查找树(b是balance(平衡))
- 每个根结点有多个叶子节点
- 高度平衡,每个根节点高度一致
- 高度小,查找速度较快
- 所有节点遵循左小右大
B+树和B树的区别#
- 所有结果只存储在叶子节点,所有根节点不存储数据。
- 每个叶子节点都有指向下一个节点的指针。
B+树特点:
- 查询速度稳定,存储在叶子节点,查找次数相同
- 遍历更快
- 通过叶子节点存储指针,能满足空间局部性原理,如果存储器上某个位置被访问,那么它附近的位置也会被访问
2 各种类型的索引#
2.1 聚簇索引和非聚簇索引#
按照底层存储方式角度划分
聚簇索引(聚集索引)
索引结构和数据一起存放的索引,InnoDB的主键索引就属于聚簇索引(字典里的拼音)
优点:- 查询速度快。相当于直接定位到了数据
缺点:
- 需要数据有序
- 更新代价大。更新数据,需要更新索引,需要更新索引里的数据。
非聚簇索引(非聚集索引)
索引顺序和物理存储顺序不同(字典里的偏旁)
优点:- 更新代价较小。叶子节点不存放数据
缺点:
- 需要数据有序
- 可能会需要二次查询,回表。(查的内容就在索引里就不需要回表,满足覆盖索引的条件)
2.2 主键索引和二级索引#
- 主键索引
加速查询,列值唯一(不可以为NULL),一张表只有一个主键 - 唯一索引
加速查询,列值唯一(可以有NULL),一张表可以有多个 - 普通索引
只能加速查询,允许值重复和有NULL,一张表可以有多个 - 前缀索引
只适用于字符串类型,对文本的前几个字符创建索引,比普通索引建立的数据更小
2.3 覆盖索引和联合索引#
覆盖索引
- 索引覆盖了查询内容
- 比如:对列a、b做索引,只查a或者b不需要回表。
联合索引(组合索引、复合索引)
- 使用表中的多个字段创建索引
3 联合索引的最左前缀匹配原则和失效条件#
- 定义:按照最左优先的方式进行索引的匹配。
- 示例:比如对a,b,c三列做索引,a、ab、abc可以使用索引,b、c、bc无法使用索引。
- 结构:
先按 a 排序,在 a 相同的情况再按 b 排序,在 b 相同的情况再按 c 排序。
所以,b 和 c 是全局无序,局部相对有序的,这样在没有遵循最左匹配原则的情况下,是无法利用到索引的。 - 联合索引失效条件:
- 不满足最左前缀原则
- 在列上做操作:计算、函数、类型转换
- 使用不等于、大于、小于,后边的会失效(a>100 and b=1,此时b的索引失效,但a用到了)
- 使用LIKE的时候,以%开头会导致索引失效
- 字符串不加单引号
4 使用索引的规范#
MySQL索引规范#
- 在常用的查询中使用索引
- 尽量使用覆盖索引,即在索引中包含查询所需的所有列,以避免回表操作。
- 遵循最左前缀原则
什么情况不建议使用索引#
- where、group by、order by用不到的字段不加索引
- 大量重复数据不建索引,例如性别
- 谨慎为经常更新的表创建过多索引
- 不建议使用无序的值作为索引
- 数据量小的表最好不要使用索引,少于1000个
二、特性#
InnoDB#
InnoDB的默认级别是可重复读。
InnoDB的MVCC和next-key lock 可以避免幻读产生,已经可以完全保证事务的隔离性要求,达到可串行化的效果,并且不会有可串行化的更多的锁的性能损失。
- 事物的原子性是通过undo log来保证。
- 事务的隔离性是通过读写锁 + MVCC机制来实现的。
- 事务的持久性是通过redo log来实现的。
MVCC#
MVCC是多版本并发控制,是InnoDB在可重复读隔离级别下事务的实现方式。
一般情况下读读不需要锁,读写、写写都需要锁。用了MVCC后,在读写时不需要加锁,但可能读到历史数据。
MVCC实现基于:隐式字段、undo log、read view。
undo log#
- 生成时间
- 事务开始之前
- 作用及内容
- 保存的是当前事物上一版本的数据,用于事物回滚数据、MVCC
- 使用的原因
- 事务执行过程中可能遇到各种错误,比如服务器本身的错误等。
- 程序在执行过程中通过ROLLBACK取消当前事务的执行。
- 可能已经执行一半就结束,但已经修改了很多数据,为了事务的原子性,需要把修改的数据给还原回来。
- insert undo log
- 只在事务回滚时需要,并且在事务提交后可以被立即丢弃
- update undo log
- 不仅在事务回滚时需要,在快照读时也需要;所以不能随便删除,只有在快速读或事务回滚不涉及该日志时,对应的日志才会被统一清除。
- 对同一行加锁时,undolog是链表,新的会放在旧的前边。
redo log#
- 生成时间
- 事物开始之后(由于事物两阶段提交的原因,redolog会在事物执行过程中产生)
- 作用及内容
- 保存的是内存中修改的数据,用于数据库宕机后数据的恢复
- 刷盘策略
- 先写入redo log buffer 中,然后再按照一定频率刷新到redo log file
两阶段提交协议#
- 准备阶段
- 协调者向参与者发起指令、参与者评估自己的状态。如果参与者评估指令可以完成,则会写undolog。然后锁定资源,执行操作,但是并不提交。
- 提交阶段
- 如果每个参与者明确返回准备成功,则协调者参与者发起提交指令,参与者提交资源变更的事物,释放锁定的资源。
- 如果任何一个参与者明确返回准备失败,则协调者向参与者发起终止命令,参与者取消已变更的事物,执行undolog,释放锁定的资源
三、架构、引擎#
1 基础架构#
MySQL如何执行一条SQL#
- 客户端发起请求
- 连接器(验证用户身份,给予权限)
- 查询缓存(存在缓存则直接返回,不存在则执行后续操作)
- 分析器(对SQL进行词法分析和语法分析操作)
- 优化器(主要对执行的sql优化选择最优的执行方案)
- 执行器(执行时会先看用户是否有执行权限,有才去使用这个引擎提供的接口)
- 去存储引擎获取数据返回(如果开启查询缓存则会缓存查询结果)
2 存储引擎#
存储引擎基于表,而不是数据库(比如同一个库下,a表是InnoDB,b表是MyISAM)。
默认InnoDB。
2.1 MyISAM和InnoDB的区别#
- MyISAM只支持表级锁,InnoDB还支持行级锁,默认为行级锁
- MyISAM不支持事物,InnoDB支持事物,默认可重复读。这个级别下解决幻读,是基于MVCC和Next-Key LOCK实现的。
- MyISAM不支持外键,InnoDB支持使用外键。但是一般情况下使用外键概念必须在应用层解决。
- InnoDB的redo log支持崩溃后的恢复,MyISAM不支持
- InnoDB支持MVCC,减少加锁操作,提高性能。
四、基本原理#
事物的特性(ACID)#
- 原子性:一个事务中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被回滚到事务开始前的状态,就像这个事务从来没有被执行过一样。
- 一致性:执行事务前后,数据库的完整性没有被破坏,写入的内容必须完全符合所有的预设规则。例如转账业务中,无论事务是否成功,转账者和收款人的总额应该是不变的。
- 隔离性: 并发访问数据库时,一个用户的事务不被其他事务所干扰,各并发事务之间数据库是独立的。
- 持久性: 一个事务被提交之后,对数据的修改就是永久的,即便系统故障也不会丢失。
脏读、幻读、不可重复读#
- 脏读:读取到了未提交的事务数据。
- 不可重复读:在同一事务中,两次查询同一个记录得到的结果不一致。
- 幻读:在同一事务中,两次查询同一范围,后一次查询看到了前一次查询没有看到的行。
- 脏读
例如:变量为50,事物A要修改为100,A还未提交,事物B已经读取到了100。但此时发生回滚,数据库里的变量还是50而不是100,事物B读取到的和数据库真实的不一致。 - 丢失修改
例如:事物A和事物B都对变量修改,期望将结果+1的修改。t1时刻事物A获取变量值是50,t2时刻事物B也获取变量值是50。但事物A还未执行修改完毕,数据库最后的结果是51而不是52,事物A的修改丢失了。 - 不可重复读
例如:事物A对变量只读,事物B对变量修改。t1时刻事物A读取到变量结果是50,t2时刻事物B将结果修改为100,t3时刻事物A发现变量的结果发生改变。 - 幻读
- select 某记录是否存在——不存在。
- 准备插入此记录
- 但执行 insert 时发现此记录已存在,无法插入 此时就发生了幻读
- 不可重复读和幻读的区别:幻读是查询到的个数的区别,不可重复读是内容的区别。两者解决方案不一致,加的锁不一样。
- 不可重复读:UPDATE和DELETE,幻读:INSERT。
事物隔离级别#
- 未提交读:事务中发生了修改,即使没有提交,其他事务也是可见的。
- 可能会导致脏读、幻读或不可重复读。
- 提交读:可以避免未提交读发生的情况,只有提交后的才能被看到。
- 可以阻止脏读,但是幻读或不可重复读仍有可能发生。
- 可重复读:对一个记录读取多次的结果是相同的,除非数据是被本身事务自己所修改。
- 可以阻止脏读和不可重复读,但幻读仍有可能发生。
- 串行化:最高的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰。
- 该级别可以防止脏读、不可重复读以及幻读。
InnoDB的默认级别是可重复读。
InnoDB的MVCC和next-key lock 可以避免幻读产生,已经可以完全保证事务的隔离性要求,达到可串行化的效果,并且不会有可串行化的更多的锁的性能损失。
五、锁#
- 行级锁、表级锁:行级锁开销大,冲突少,会死锁。表级锁开销小,冲突大,不会死锁
- 共享锁、排他锁
- 乐观锁、悲观锁
- next-key lock
- 间隙锁+行锁,能解决幻读的问题
- gap lock间隙锁
六、使用#
SQL执行的慢的原因和解决方法#
该SQL偶尔执行慢#
- 在刷新脏页,redo log写满了需要直接写入磁盘
- 执行的时候遇到了锁
该SQL一直执行慢#
- 没有用上索引,加索引、查看是否是对字段进行运算、函数,导致未用上索引。
- 数据库自己选错了索引,可以用index某列强制走索引。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 bc's club!