Mysql面试内容(常问点)
写在前面
- 个人博客首页
- 内容由思维导图转换而来
MySQL
存储结构
- B-Tree:所有节点都存放键和数据(不采用)
- B+Tree:非叶子节点存放的是键,只有叶子节点存放数据,另外叶子节点有一条指向兄弟节点的指针
索引
聚簇索引
- 依赖有序数据、更新代价大,因为B+树是多路平衡树,所以保证插入数据的有序性,可以减少排序的时间消耗,且如果更新的话,由于叶子节点存放数据
- 主键索引就是一种聚簇索引,而主键索引一般都是自增的,且不允许修改的,那么更新代价也会有所保证
- 只有Innodb存储引擎存在
非聚簇索引
- 同样依赖有序,叶节点存放的是ID与索引字段,需要回表查询数据
- 如果是覆盖索引,就不需要回表了,因为叶节点存放的就是需要查的数据(注意:上述是Innodb,而Myisam叶节点存放的是指针,覆盖索引只有查Id)
- 聚簇索引得名来自于,在Innodb存储引擎中,只有.idb文件,其索引和数据存放在一个文件,而Myisam中右MYI和MY文件分开存储索引和数据
索引失效情况
单列索引无法存储null值,联合索引是无法存储全null值
失效
- 前导索引失效(’%A’)
- 最左匹配原则(A,B)单独查B失效
- 如果mysql估计使用全表扫描要比使用索引快,则不使用索引(字段值少,就几个值,数据量再大也无所谓)
- varchar字段,拼接整数条件,索引失效(MySQL 要会自动把字符串转为数字)
- 拼接条件时,对索引字段进行了计算,函数
- or条件两边有一个没建索引,就会索引失效
- (a,b)联合索引,a用了范围查询,b就不会走索引,因为a的值此时是一个范围,不是固定的,在这个范围内b的值不是有序的,因此b字段用不上联合索引
事务与锁
事务概念
ACID 原子性Atomicity、一致性consistencey、隔离性Isolation、持久性Durability
并发事务问题
脏读
- 读取了未提交事务的修改
丢失修改:事务A修改,被事务B的修改给冲掉,两事务读取到的数据一致
不可重复读: 事务A读取后,事务B修改了数据,事务A再读取发现和第一次不一致了
幻读:和不可重复读很像,但是为新增或者删除导致的幻觉一样,读的不一致
事务级别
READ UNCOMMIT
- 脏读、幻读、不可重复读
READ COMMIT
- 解决脏读
READ REPEAT
- 解决脏读、不可重复读,快照读通过MVCC机制解决幻读,当前读上解决幻读(通过临建锁 )
SERIALIZABLE
- ALL KILL
锁
按加锁机制分
- 乐观锁
- 悲观锁
按锁粒度分
行锁(记录锁)
- 间隙锁Gap Lock:存在于非唯一索引,锁定时,不包含where条件本身(not_unique = 24),会锁住(24,X)
- 临键锁Next-key-Lock:存在于非唯一索引中,锁定时,包含where条件本身(not_unique = 24),会所住[24,X)的区间(下一个区间)
- Innodb的行锁依赖于索引,如果加锁时没有使用到索引,就会退化为表锁
表锁
页锁
按照兼容性分
共享锁S
- lock in share mode
排他锁X
- for update
意向锁
- 为表级锁
- 与行的X锁和S锁兼容,并且在给行记录加S/X锁时,会给表加IS/IX,这样来防止加表锁时检查行锁情况的大量消耗
事务实现
- 通过buffer pool logBuffer RedoLog 和Undo Log以及mvcc来实现事务,以Update为例:
- 首先会根据where条件找到数据所在页,然后放入Buffer Pool中.
- 执行update语句,修改Buffer Pool中的数据,也就是内存中的数据
- 针对这条update语句,会生成RedoLog对象,存入LogBuffer中,然后也会生成undolog用于事务回滚
- 如果事务提交,那么就会把RedoLog对象持久化,后续会对BufferPool落盘
- 如果事务回滚,则会利用undolog日志回滚
- 当然,ACID的保障,原子性A主要是通过UndoLog回滚来保证,通过RedoLog来保证其持久性,然后通过MVCC与 锁机制保证其隔离性,自然最后就能保证一致性
MVCC
READ COMMIT级别下
- 每次都会生成一个ReadViewer,来解决脏读
READ REPEAT级别下
- 最开始读取的事务生成ReadView,在这个事务内沿用一个,这样可以实现重复读
- 在当前读下,读取的都是最新的,如果是锁住单行,那么其他事务对行上下进行操作时,可能会产生幻读,此时借用Next-key-lock来锁住一个范围,解决幻读
三个辅助
ReadView
- m_low_limit_id:trix_jd小于这个,说明当前修改的事务已经提交了,可以被读到
- m_creator_trx_id:trix_id等于这个说明当前修改时自己所在的事务产生的
- m_up_limit_id:trix_id小于up的话,是可见的
- m_ids:创建readView时,当前活跃的事务ID列表,没有被提交的,是不可见的
undo-log
insert undo log
- 提交后就删除,因为只对事务本身可见,对其他事务不可见
update undo log
- 配合MVVC机制,实现不加锁解决一些事务问题
辅助字段
- 用于没有唯一主键,默认生成来代替主键的:DB_ROW_ID
- 指向UndoLog的指针:DB
- trix_id当前记录的事务版本id
Mysql执行流程
存储架构分为Server层 和 存储引擎层,其中Server曾主要负责建立连接、分析和执行SQL。存储层则负责数据的存储与提取
连接器:负责应用与数据库之间建立连接,比如用户名密码校验,和读写权限的校验
查询缓存:如果是Select语句那么就会进行读取缓存(鸡肋功能,8.0之后直接给去掉了)
解析SQL:通过解析器对SQL进行词法分析 和 语法分析,然后如果校验不过,就会直接返回语法错误
执行SQL
预处理器:检查SQL中的表和类是否存在,将*转换为全字段
优化器:为SQL执行指定一个执行计划,走不走索引也是在这一步来确定
执行器:执行SQL从存储引擎中获取数据返回
注:在执行器与存储层交互的时候,有几个查询细节,比如索引下推(5.7之后)减少二级索引回表查询的次数,举个例子:将(a,b)索引a范围查询导致b失效的,推到存储引擎来判断,这样存储引擎直接判断失效,不再为了查询到a的数据而进行回表
主从复制
概念:通过binlog,slave会从master读取该文件的内容,然后进行数据的同步操作
为什么Mysql采用B+树作为其索引结构,而不采用B树?
- B树上非叶节点需要存储数据,那么就减少了存储索引的数量,需要通过增加层数来支持,而B+树只用叶子节点来存储,虽然有数据冗余,但是能够减少高度,着用就能够减少IO次数(因为IO读取时,是分页读取,所以如果将叶节点通过三次IO就读取到的话,肯定是更加高效的)
Mysql底层引擎之间的区别?
Innodb与Myisam之间的区别:
- 第一,行级锁上,M是不支持的,I是支持的
- 第二,事务上,M是不支持的,I是支持的(展开讲事务隔离级别,默认是RR)
- 第三,外键上,M不支持,I支持
- 第四,在数据库发生异常崩溃后,M不支持恢复,I支持,主要是通过redolog
- 第五,关于MVCC,M是不支持的,而I是支持的
- 第六,比较重要的一点,两者在索引实现有点不一样,M的索引是非聚簇索引,并且其索引的B+树叶节点存放的是数据的指针地址,而I有非聚簇和聚簇索引,其主键索引B+树上叶节点存放的就是数据本身,然后其他的非聚簇索引则存放的主键和索引字段
聊聊如何进行SQL优化
创建索引需要注意
- 尽量给where后的列,order by,group by,distinc中的字段加索引
- 尽量加联合索引,不要建冗余索引,吧区分度最高的放在联合索引左侧,字段小的往左放,使用最频繁的往左放
- 表中的索引不能太多,建议是不超过5个,mysql优化器会根据统一信息,对用到的索引进行评估,制定方案,太多了反而影响优化器的执行时间
写SQL需要注意
- 不要使用select * ,消耗更多的cpu和io,无法使用覆盖索引,尽量列出所需列来
- 避免使用子查询,尽量把子查询优化为join操作,这样可以使用索引
- 避免关联太多的表,mysql最多支持60个,建议不超过5个,但实际财务类可能就超过了。
- 能用union all就不要用union,性能更好,因为union要去重
当表达到百万级,如何优化分页查询
- 利用主键索引,按照顺序存储的主键id,可以先查出来分页的id集合有哪些,再根据id回表查询,有联合索引也可以用联合索引,查出最大覆盖列的数据,再回表
- 倒叙查询,如果数据位于后方