八股文|后端|MySQL|答案
21446
2023.02.20
2023.03.31
发布于 未知归属地

一.Mysql 里面为什么用 B+ 树?
哈希范围查询很慢。链表要遍历。剩下的就是树。广为人知的,二叉搜索树,AVL 树,红黑树,B 树等等。

  • 二叉搜索树会退化为链表,层数可能也会很多
  • AVL树层数依然过多
  • 红黑树只是优化了插入、更新,弱化了平衡,在更新和搜索中取了折中。但层数过多的问题没有解决。
  • B 树,层数变少了,但如果访问下一页需要回到父节点到兄弟节点
  • B+ 树,将叶子节点用链表串联起来了,子节点中包含了父节点的信息——解决B树访问下一页需要先回到父节点的问题;同时非叶子节点不保存具体的数据,而只保存关键字的索引,具体数据保存在叶子结点中——相对于B树,减少了非叶子结点(索引)的数据量,所以相同的内存空间能保存更多的索引,从而减少io次数。

二.事务的隔离级别
事务并发的几类问题:

  1. 脏读:一个事务可以读到另一个事务未提交的结果,所有的并发事务问题都会发生。
  2. 不可重复读:在一个事务中多次读取同一个数据时,结果出现不一致。因为另外一个事务在对此记录进行update操作。
  3. 幻读:在一个事务中使用相同的 SQL 两次读取,第二次读取到了其他事务新插入的行。
  4. 丢失更新:所有事务依次逐个执行,所以可以解决并发事务的所有问题。

事务的隔离级别:

  1. 读未提交:什么都没做,上面的一个问题都没解决;
  2. 读提交:只解决了上面的第一个问题;
  3. 可重复读:解决了1.2和部分3问题,MySQL 默认采用可重复读隔离级别;
  4. 串行化:完全解决上面4个问题。

三.乐观锁和悲观锁 + 使用场景

  • 悲观锁:在所有操作前都上锁
  • 乐观锁:总是假设最好的情况,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号机制和CAS算法实现。

乐观锁适用于写比较少的情况下(多读场景),即冲突真的很少发生的时候,这样可以省去了锁的开销,加大了系统的整个吞吐量。
但如果是多写的情况,一般会经常产生冲突,这就会导致上层应用会不断的进行retry,这样反倒是降低了性能,所以一般多写的场景下用悲观锁就比较合适。

四.死锁的解决方法
死锁的产生是在这样一种环境中:比如我们有两个进程AB,他们都需要资源1和资源2,当进程A持有资源1,进线程B持有资源2的时候,他们都需要对方手上的进程,而一般操作系统又不允许抢占,这个时候就发生了死锁。

从这个例子中其实可以总结出死锁的几个必要条件:

  1. 一个资源只能被一个进程所占有,不能共享
  2. 一个线程请求资源失败时,它会等待而不是释放
  3. 一个线程在释放资源之前其他进程不能抢夺资源
  4. 循环等待
    从死锁产生的原因未明可以设计一些方法去避免死锁的发生:
  1. 静态分配资源,一开始就把一个进程所需的全部资源都分配给它,但这样会降低资源的使用效率
  2. 允许抢占,需要设置进程的不同优先级,高优先级的进程可以抢占低优先级的进程的资源
  3. 把资源进行编号,申请资源必须按照资源的编号顺序来申请

如果死锁已经发生了,就需要去解开死锁,其本质思想就是分配资源打破循环等待

  1. 可以运行抢占,从一个或多个进程中抢出资源来给其他进程
  2. 也可以终止一些进程,来达到释放资源的目的

五.事务的实现
事务是作为单个逻辑工作单元执行的一系列操作。
一个逻辑工作单元必须有四个属性,称为原子性、一致性、隔离性和持久性 (ACID) 属性,只有这样才能成为一个事务。事务一般都是与数据库打交道的操作.
事务的 ACID 特性是由关系数据库管理系统来实现的。
DBMS 采用 日志 来保证事务的 原子性、一致性 和 持久性。Mysql通过预写式日志undo log、redo log,如果整个事务执行的过程系统崩溃或者断电了,在系统重启的时候,恢复机制会将undo log中未提交的事务进行回滚,保证事务的原子性;而redo log中已提交的事务重做,保证事务的持久性。
DBMS 采用 锁机制 来实现事务的隔离性。当多个事务同时更新数据库中相同的数据时,只允许 持有锁的事务 能更新该数据,其他事务必须等待,直到前一个事务释放了锁,其他事务才有机会更新该数据。

六.讲讲 MVCC
多版本并发控制 (MVCC) 通过保持某个时间点的数据快照来实现。也就是说,无论事务执行多长时间,事务中看到的数据都不会受到其他事务的影响。根据事务的开始时间,每个事务可能在同一时间看到同一表的不同数据。
简单来说,多版本并发控制的思想就是保存数据的历史版本,通过数据行的多版本控制来实现数据库的并发控制。通过这种方式,我们可以比较版本号,以确定是否显示数据,并在不锁定事务隔离效果的情况下读取数据。

七.MySQL主从复制?
主从复制是指将 主数据库(Master)中的 DDL 和 DML 操作通过二进制日志传输到 从数据库(Slave) 上,然后将这些日志重新执行(重做),从而使得从数据库的数据与主数据库保持一致。MySQL 支持单向、异步复制,复制过程中一个服务器充当主服务器,而一个或多个其它服务器充当从服务器。
主从复制的作用有:

  • 当主数据库出现问题时,可以切换到从数据库;
  • 可以进行数据库层面的读写分离,实现负载均衡;
  • 可以在从数据库上进行实时数据备份。

基本原理流程:
MySQL 的主从复制是一个 异步 的复制过程(一般情况下感觉是实时的),数据将从一个 MySQL 数据库(Master)复制到另外一个 MySQL 数据库(Slave),在 Master 与 Slave 之间实现整个主从复制的过程是由三个线程参与完成的,其中有两个线程(SQL 线程和 I/O 线程)在 Slave 端,另外一个线程( I/O 线程)在 Master 端。
Master 端:打开二进制日志(binlog )记录功能 —— 记录下所有改变了数据库数据的语句,放进 Master 的 binlog 中;
Slave 端:开启一个 I/O 线程 —— 负责从 Master上拉取 binlog 内容,放进自己的中继日志(Relay log)中;
Slave 端:SQL 执行线程 —— 读取 Relay log,并顺序执行该日志中的 SQL 事件。

三种主从复制的方式:
1)同步复制:所有从机接收到Binlog,才认为事务提交成功;最安全,但性能差,一般不用
2)异步复制:只要主机事务提交成功,就对客户端返回成功;后台线程异步把Binlog同步给从机,然后从机回放。最快,但可能丢失数据;
3)半同步复制:部分从机接收到Binlog,才对客户端返回成功
一般做法是牺牲一致性换取高可用;数据丢失后,人工修复。
为了解决主从复制延迟太大,切换到从机后数据丢失太多的问题,采用了并行回放策略(1.按数据维度并行——两个事务没有操作交集可以并行;2.按事务提交顺序并行——同时提交成功的事务是可以并行的)

八.Binlog和Redo Log的一致性问题
通过2PC(2阶段提交方案)解决,具体为:
阶段1:事务提交之前,Redo Log,Undo Log已写入,然后Binlog写入内存,等待刷盘;
阶段2:先刷盘Binlog,然后让InnoDB执行Commit。
以Binlog的刷盘判定一个事务是否被成功提交,让Redo Log 向Binlog靠齐。
宕机场景:
1)如果阶段1宕机,Binlog在内存,数据丢失,Redo Log 通过回滚解决;
2)如果阶段2宕机,Binlog写一部分,对Binlog做截断,对Redo Log做回滚;
3)如果阶段2宕机,Binlog已刷盘,但未执行Commit,恢复时通过比对Binlog和Redo Log,Undo Log发起Commit操作。

九.主从同步的延迟原因及解决办法?
架构方面
1) 业务的持久层采用分库架构,mysql 服务能力水平扩展,分散压力
2) 单个库读写分离,一主多从,主写读从,分散压力。这样从库比主库压力高,保护主库
3) 服务在业务和DB之间加入 memcache 和 redis 的cache层,降低读的压力
4) 不同业务的 mysql 放在不同的物理机,降低压力
5) 使用比主库更好的硬件设备,Mqsql 压力小,延迟就减少了

硬件方面
1) 采用好服务器,比如 4u 比 2u 性能明显好,2u 比 1u 性能明显好。
2) 存储用 ssd 或者盘阵或者 san,提升随机写的性能。
3) 主从间保证处在同一个交换机下面,并且是万兆环境。

Mysql 主从同步加速
1) sync_binlog 在 Slave 端设置为 0。
2) log-slave-updates 从服务器从主服务器接受的更新日志不计入二进制日志
3) 直接禁用 Slave 的 binlog
4) Slave 端,如果存储引擎是 innodb,innodb_flush_log_at_trx_commit =2
5) 同步参数调整主库是写,对数据安全性较高,比如 sync_binlog=1,innodb_flush_log_at_trx_commit = 1 之类的设置是需要的而 slave 则不需要这么高的数据安全,完全可以将 sync_binlog 设置为 0 或者关闭 binlog,innodb_flushlog 也可以设置为 0 来提高 sql 的执行效率

对以上答案有任何疑问欢迎交流指出!
评论 (33)