文章目录
- 1. MySql 中 MyISAM 和 InnoDB 存储引擎区别
- 1.1. MyISAM
- 1.2. InnoDB
- 2. 索引的数据结构
- 2.1. B Tree索引
- 2.2. B+Tree索引
- 2.3. MySql 做的优化
- 3. 为什么使用B+Tree索引而不使用Hash索引?
- 4. 为什么使用B+Tree索引而不使用B-Tree索引?
- 5. MyISAM 存储引擎索引实现
- 6. InnoDB 存储引擎索引实现
- 7. MySQL 复合索引如何使用
- 8. 索引的缺点,以及使用注意
- 9. 事务的几个特性
- 10. 事务的隔离级别
- 11. MySQL 是如何实现可重复读的
- 12. MySQL 锁有哪些类型
- 13. 悲观锁和乐观锁是什么?使用场景是什么
- 14. MySQL 死锁的原理以及如何定位和解决
- 15. MySQL 调优手段
1. MySql 中 MyISAM 和 InnoDB 存储引擎区别
1.1. MyISAM
- 不支持事务
- 不支持外键约束
- 索引文件和数据文件是分开的,这样可以在内存里缓存更多的索引
- 对查询的性能会更好,适用于少增改、多查询的需求
1.2. InnoDB
- 支持事务
- 聚簇索引
- 强制要求有主键,支持外键约束
- 大数据量可以分库分表、高并发可以读写分离、高可用可以主备切换这些都是基于 InnoDB
2. 索引的数据结构
2.1. B Tree索引
- 每个节点要存放:
- 数据
- 指向下一节点的指针
- 指向数据的指针
2.2. B+Tree索引
- 非叶子节点要存放:
- 下一节点的最小值
- 指向下一节点的指针
- 叶子节点要存放:
- 具体的数据
2.3. MySql 做的优化
- MySQL 在 B+Tree 的基础上,加了顺序访问的指针(如上图中 Q 连接的蓝色箭头),这样便于范围查找
3. 为什么使用B+Tree索引而不使用Hash索引?
- 虽然Hash索引速度很快,但是它不支持范围查找
- 上面说到加上了顺序访问指针的 B+Tree 是支持范围查找的
4. 为什么使用B+Tree索引而不使用B-Tree索引?
-
B+Tree每行存储的节点较多,原因如下:
B-Tree结构中是将数据存储到了节点中,因此每行存的索引就变少了(规定每行存16kb)相应的深度(阶)比B+Tree深,会造成进行IO操作过多,影响性能。
-
其次B+Tree中的叶子节点存在指针,由于指针的存在,在范围查找时,移动指针即可,而B-Tree不行
5. MyISAM 存储引擎索引实现
- 因为索引文件和数据文件是分开的,所以在 B+Tree 的叶子节点中存储的不是具体的数据,而是数据对应的物理地址
6. InnoDB 存储引擎索引实现
- InnoDB 数据文件本身也是一个索引文件,这个索引默认就是根据主键建立的聚簇索引
- B+Tree 中每个叶子节点中存放的就是一个完整的数据
7. MySQL 复合索引如何使用
8. 索引的缺点,以及使用注意
- 缺点
- 会增加磁盘消耗、
- 频繁增改索引,反而会影响性能
- 使用注意
- 尽量少的创建索引
- 尽可能使用区分性高的字段建立索引
9. 事务的几个特性
- 原子性:成功一起成功,失败一起失败
- 一致性:在数据修改前后,必须都是准确的
- 隔离性:多个事务不能互相干扰,
- 持久性:事务成功提交后,对数据的修改必须永久有效
10. 事务的隔离级别
- MySQL 默认的隔离级别为:可重复读
-
读未提交,导致脏读
事务A对 ID = 1 的数据进行查询
事务B对 ID = 1 的数据进行修改
在事务B提交之前,事务A查询的结果就已经是事务B修改后的数据 -
读已提交,解决脏读,导致不可重复读
事务A对 ID = 1 的数据进行查询
事务B对 ID = 1 的数据进行修改
事务B提交前,事务A读到的是原始数据
事务B提交后,事务A再一次读取,读到的是更新后的数据
两次读取数据不一致,就是不可重复读 -
可重复读,解决不可重复读
事务A对 ID = 1 的数据进行查询
事务B对 ID = 1 的数据进行修改
事务B提交前,事务A读到的是原始数据
事务B提交后,事务A再一次读取,读到的依旧是原始数据
多次读取数据都是一致的,就是可重复读 -
串行化,解决幻读
原先表中有一条数据
事务A读取表中所有数据,此时查出了一条记录
事务B向表中插入一行记录,并直接提交
事务A再次读取,此时查出了两条记录
两次读取数据行数不一致,就是幻读
串行化,就是在事务 A 未提交时,事务B阻塞
11. MySQL 是如何实现可重复读的
- 是使用了 多版本并发控制机制,Multi-version concurrent control (MVCC)
- InnoDB 存储引擎,会在每行数据的最后加上两个隐藏列,
- 一个是保存创建改行的事务ID
- 另一个是保存删除改行的事务ID
- 事务 ID 是 MySQL 自己维护的自增ID
- 在查询操作时,需要满足以下两个条件
- 查询创建行的事务ID 小于等于 当前事务ID 的行,这样可以确保这个行是在当前事务或者之前的事务中创建的
- 查询删除行的事务ID 为空,或者大于当前事务ID 的行,这样可以保证这个行未被删除或者在之后的事务中被删除
12. MySQL 锁有哪些类型
- 按照对数据操作的类型(读/写)来分
- 读锁(共享锁):针对同一份数据,多个读操作可以同时进行而不会互相影响
- 写锁(排他锁):当前写操作没有完成前,他会阻断其他写操作和读操作
- 按照对数据操作的粒度来分
- 表锁(主要是MyISAM)
- 行锁(主要是InnoDB)
- 在增删改时会加行锁
- 查的时候一般不会加锁,因为 InnoDB,一般读取的是 MVCC 的快照
- 页锁
13. 悲观锁和乐观锁是什么?使用场景是什么
- 悲观锁就是
select * from table where id=1 for update
,这就是加上了悲观锁,担心自己获取不到这一行的锁,先提前锁上,然后就可以对这一行数据进行其他操作 - 乐观锁,就是认为自己想要获取锁的时候,就能获取到,不需要提前锁死。在查询数据的时候,除了正常的字段数据,再加上一个版本号,对这一行数据操作完成后,再判断当前库中版本号与之前读取的版本号是否一致,若一致则提交操作,若不一致则重新查询重新操作
14. MySQL 死锁的原理以及如何定位和解决
-
死锁大致原因
事务 A 对 ID = 1 的行加上排他锁
事务 B 对 ID = 2 的行加上排他锁
然后事务 A 想要请求 ID = 2 的行的锁
接着事务 B 想要请求 ID = 1 的行的锁
此时事务 A、B 互相等待 -
解决:查看死锁日志,根据 SQL 定位对应的代码,排查原因