Java 面试八股文这篇专栏够用之数据库篇(二)

70 阅读8分钟

Java 面试八股文这篇专栏够用之数据库篇(二)

问1:sql 的优化的经验

面试官:sql 的优化的经验
候选人:嗯,这个在项目还是挺常见的,当然如果直说 sql 优化的话,我们会从这几方面考虑,比如建表的时候、使用索引、sql 语句的编写、主从复制,读写分离,还有一个是如果量比较大的话,可以考虑分库分表

面试官:创建表的时候,你们是如何优化的呢?
候选人:这个我们主要参考的阿里出的那个开发手册《嵩山版》,就比如,在定义字段的时候需要结合字段的内容来选择合适的类型,如果是数值的话,像 tinyint、int、bigint 这些类型,要根据实际情况选择。如果是字符串类型,也是结合存储的内容来选择 char 和 varchar 或者 text 类型

面试官:那在使用索引的时候,是如何优化呢?
候选人:【参考索引创建原则 进行描述】

面试官:你平时对 sql 语句做了哪些优化呢?
候选人:嗯,这个也有很多,比如 SELECT 语句务必指明字段名称,不要直接使用 select *,还有就是要注意 SQL 语句避免造成索引失效的写法;如果是聚合查询,尽量用 union all 代替 union ,union 会多一次过滤,效率比较低;如果是表关联的话,尽量使用 innerjoin ,不要使用用 left join right join,如必须使用一定要以小表为驱动

问2:事务的特性是什么?可以详细说一下吗?

面试官:事务的特性是什么?可以详细说一下吗?
候选人:嗯,这个比较清楚,ACID,分别指的是:原子性、一致性、隔离性、持久性;我举个例子:
A 向 B 转账 500,转账成功,A 扣除 500 元,B 增加 500 元,原子操作体现在要么都成功,要么都失败
在转账的过程中,数据要一致,A 扣除了 500,B 必须增加 500
在转账的过程中,隔离性体现在 A 向 B 转账,不能受其他事务干扰
在转账的过程中,持久性体现在事务提交后,要把数据持久化(可以说是落盘操作)

问3:并发事务带来哪些问题?

面试官:并发事务带来哪些问题?
候选人:
我们在项目开发中,多个事务并发进行是经常发生的,并发也是必然的,有可能导致一些问题

第一是脏读,当一个事务正在访问数据并且对数据进行了修改,而这种修改还没有提交到数据库中,这时另外一个事务也访问了这个数据,因为这个数据是还没有提交的数据,那么另外一个事务读到的这个数据是 “脏数据”,依据 “脏数据” 所做的操作可能是不正确的。

第二是不可重复读:比如在一个事务内多次读同一数据。在这个事务还没有结束时,另一个事务也访问该数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改导致第一个事务两次读取的数据可能不太一样。这就发生了在一个事务内两次读到的数据是不一样的情况,因此称为不可重复读。

第三是幻读(Phantom read):幻读与不可重复读类似。它发生在一个事务(T1)读取了几行数据,接着另一个并发事务(T2)插入了一些数据时。在随后的查询中,第一个事务(T1)就会发现多了一些原本不存在的记录,就好像发生了幻觉一样,所以称为幻读。

问4:怎么解决这些问题呢(上一个问题)?MySQL 的默认隔离级别是?

面试官:怎么解决这些问题呢?MySQL 的默认隔离级别是?
候选人:解决方案是对事务进行隔离

MySQL 支持四种隔离级别,分别有:

第一个是,未提交读(read uncommitted)它解决不了刚才提出的所有问题,一般项目中也不用这个。第二个是读已提交(read committed)它能解决脏读的问题的,但是解决不了不可重复读和幻读。第三个是可重复读(repeatable read)它能解决脏读和不可重复读,但是解决不了幻读,这个也是 mysql 默认的隔离级别。第四个是串行化(serializable)它可以解决刚才提出来的所有问题,但是由于让事务串行执行的,性能比较低。所以,我们一般使用的都是 mysql 默认的隔离级别:可重复读

问5:undo log 和 redo log 的区别

面试官:undo log 和 redo log 的区别
候选人:好的,其中 redo log 日志记录的是数据页的物理变化,服务宕机可用来同步数据,而 undo log 不同,它主要记录的是逻辑日志,当事务回滚时,通过逆操作恢复原来的数据,比如我们删除一条数据的时候,就会在 undo log 日志文件中新增一条 delete 语句,如果发生回滚就执行逆操作;

redo log 保证了事务的持久性,undo log 保证了事务的原子性和一致性

问6:事务中的隔离性是如何保证的呢?

答:

锁: 排他锁(如一个事务获取了一个数据的排他锁,其他事务就不能获取该行的其他锁)

MVCC: 多版本并发控制

问7:你解释一下MVCC?

面试官:事务中的隔离性是如何保证的呢?(你解释一下 MVCC)
候选人:事务的隔离性是由锁和 mvcc 实现的。

其中 mvcc 的意思是多版本并发控制。指维护一个数据的多个版本,使得读写操作没有冲突,它的底层实现主要是分为了三个部分,第一个是隐藏字段,第二个是 undo log 日志,第三个是 readView 读视图

隐藏字段是指:在 mysql 中给每个表都设置了隐藏字段,有一个是 trx_id (事务 id),记录每一次操作的事务 id,是自增的;另一个字段是 roll_pointer (回滚指针),指向上一个版本的事务版本记录地址

undo log 主要的作用是记录回滚日志,存储老版本数据,在内部会形成一个版本链,在多个事务并行操作某一行记录,记录不同事务修改数据的版本,通过 roll_pointer 指针形成一个链表

readView 解决的是一个事务查询选择版本的问题,在内部定义了一些匹配规则和当前的一些事务 id 判断该访问哪个版本的数据,不同的隔离级别快照读是不一样的,最终的访问的结果不一样。如果是 rc(read committed) 隔离级别,每一次执行快照读时生成 ReadView,如果是 rr(REPEATABLE READ) 隔离级别仅在事务中第一次执行快照读时生成 ReadView,后续复用

问8:说一下主从同步的原理?

面试官:说一下主从同步的原理?
候选人:
嗯,好的。

MySQL 主从复制的核心就是二进制日志,二进制日志记录了所有的 DDL 语句和 DML 语句

具体的主从同步过程大概的流程是这样的:

  1. Master 主库在事务提交时,会把数据变更记录在二进制日志文件 Binlog 中。
  2. 从库读取主库的二进制日志文件 Binlog,写入到从库的中继日志 Relay Log 。
  3. slave 重做中继日志中的事件,将改变反映它自己的数据。

问9:你项目使用过分库分表吗

  • 业务介绍

1 ,根据自己简历上的项目,想一个数据量较大业务(请求数多或业务累积大)

2 ,达到了什么样的量级(单表 1000 万或超过 20G )

  • 具体拆分策略

1 ,水平分库,将一个库的数据拆分到多个库中,解决海量数据存储和高并发的问题(经常使用)

2 ,水平分表,解决单表存储和性能的问题

3 ,垂直分库,根据业务进行拆分,高并发下提高磁盘 IO 和网络连接数(在微服务项目上经常使用)

4 ,垂直分表,冷热数据分离,多表互不影响

ps: 我们会发现其实进行分库分表我们会遇到相应的问题,例如:分布式事务一致性问题;跨节点关联查询;跨节点分页、排序函数;主键避重.....我们这个时候可以借助中间件sharding-sphere或者 mycat。

我们的mysql核心考点到这就结束了,我总结一下我们主要涉及到的内容如下:

image.png

image.png