数据库八股
一条SQL查询语句是如何执行的? 连接器:首先需要和SQL客户端建立TCP连接,连接器负责身份认证和权限读取; 查询缓存:MySQL服务器拿到查询请求后,会会先看缓存,看之前是不是很执行过该语句;但是MySQL8.0已经删除了这一步,因为缓存命中率极差(只要表有了一点改动就要删除相关所有缓存); 分析器:分析器分为词法分析和语法分析,简单来说词法分析就是看用了哪些词,比如什么SELECT,WHERE;而语法分析则是构建一颗抽象语法树,检查语句是否合法; 优化器: 优化器的职能是指定查询的具体执行计划:比如如果有多个索引,用哪一个;多表JOIN的时候决定JION的顺序等; 执行器: 执行器阶段就是执行语句,通过存储引擎的接口进行查询操作; 事务的四大特性有哪些? 四大特性就是ACID: A:原子性;一个事务中的语句的执行要么全部成功,要么全部失败。不会出现成功一半的情况; C:一致性;事务执行之后,数据库必须从一个一致性状态转移到另一个一致性状态; I:隔离性;事务与事务之间互相隔离,一个事务的中间状态对其他事务不可见;为的就是解决脏读,不可重复读,幻读这些数据竞态问题; D:持久性:事务一旦提交,对数据库的修改是永久性的,掉电也不影响结果; 数据库的事务隔离级别有哪些? 数据库的事务隔离是为了解决以下三个问题: 脏读:事务A读取了事务B没有提交的数据,之后事务B进行了回滚,事务A读到了不存在的数据; 不可重复读:事务A读取某数据后,事务B修改了该数据,事务A再次读取发现两次结果不一样;强调的是已有数据的修改; 幻读: 事务A执行两次相同的范围查询,事务B提交了插入或删除,导致事务A的行数不一致,出现“幻影行”;强调的是行数增减; 事务隔离等级有四个: 读未提交:事务可以读取其他事务未提交的修改,没有任何隔离; 读已提交:事务只能读取其他事务已经提交的数据,解决了脏读;但不可重复读和幻读问题仍然存在; 可重复读:在同一个事务内,多次读取同一行数据的结果始终一致;即在事务开始和结束之间,其他事务对数据的修改不可见。但没有解决幻读; 串行化:拒绝并发就不会有数据竞态了,只在非常严格的时候出现; Inno DB虽然是可重复读的隔离等级,但是通过机制的特别设计解决了幻读: 快照读(普通 SELECT):只在Select第一次执行的时候创建一个ReadView快照,之后的所有查询都在快照中进行,直接解决了不可重复读的问题; 当前读(SELECT … FOR UPDATE / INSERT / UPDATE / DELETE):通过 Next-Key Lock(临键锁) 解决。Next-Key Lock = Record Lock(行锁)+ Gap Lock(间隙锁),它会锁住查询条件命中的记录以及记录之间的间隙,阻止其他事务在这个范围内插入新行。 如果事务中先用快照读读了一次(建立了 Read View),然后另一个事务插入并提交了新行,接着当前事务用当前读(比如 SELECT … FOR UPDATE)再读一次,就可能看到新插入的行 MySQL的执行引擎有哪些? 常见的执行引擎有三个: InnoDB:实际生产的唯一选择,支持事务,支持ACID特性,支持行级锁,支持MVCC;对并发访问和数据安全都有很好的实现; MyISAM:旧引擎,不支持事务、不支持行级锁(只有表级锁)、不支持外键、不支持崩溃安全恢复,并发性能很差; Memory:使用内存,数据在掉电之后就会消失,不常用; MySQL为什么使用B+树来作索引 数据存储在磁盘中,而磁盘的IO速度非常慢,索引就是为了降低查询时的磁盘IO次数而设计的; B+树的特性非常好: 层高低,每次查询的IO在2-3次:InnoDB的一页是16KB,而且所有的数据都放在叶子节点中,非叶子节点只存key,扇出非常大:一个非叶子节点能放1170的分支,两层就能放130万以上的记录;基本IO只需要两次就能查到数据;而与之相对的AVL树因为每个节点只存一个key,100w条记录需要20层,就是20次IO;跳表也是层高太高; 支持范围查询:B+树的叶子节点之间有链表(InnoDB是双向链表)进行连接,可以非常快速地进行范围查询;而B树没有,范围查询就要多次遍历,IO次数不可控;全表查询B+树只用走底层叶子链表一次,而B树要遍历全表;哈希则是做不了范围查询; 查询性能稳定:B+树的所有非叶子节点都不存数据,每次查询都要走到叶子层,路径长度一致,性能可预测;而B树因为把数据放在非叶子节点中,在降低了每层容纳分支的同时也导致了查询可能提前退出,IO次数不可控,对于优化来说是个大问题; 说一下索引失效的场景? 联合索引的基本规则就是最左前缀原则:如果建了一个联合索引 (a, b, c),B+ 树是按照 a → b → c 的顺序排列的。查询时必须从最左列开始连续匹配,才能走索引,如果使用索引时跳过了最左边的 a 则直接无法使用索引; ...