Java面试专题-MySQL篇事务等

把面试专题的MySQL看完了,但是感觉不全,还得看看其他的八股,不过准备跟着敲一下黑马点评,家人们推荐吗

二、事务

1.事务特性

事务是一组操作的集合,它是一个不可分割的工作单位,事务会把所有的操作作为一个整体一起向系统提交或撤销操作请求,即这些操作要么同时成功,要么同时失败。

  • ACID是什么?可以详细说一下吗?

  • 原子性(Atomicity):事务是不可分割的最小操作单元,要么全部成功,要么全部失败。

  • 一致性(Consistency):事务完成时,必须使所有的数据都保持一致状态。

  • 隔离性(Isolation):数据库系统提供的隔离机制,保证事务在不受外部并发操作影响的独立环境下运行。

  • 持久性(Durability):事务一旦提交或回滚,它对数据库中的数据的改变就是永久的。

  • InnoDB 引擎通过什么技术来保证事务的这四个特性的呢?

    • 持久性是通过redo log(重做日志)来保证的;
    • 原子性是通过 undo log(回滚日志)来保证的;
    • 隔离性是通过MVCC(多版本并发控制)或锁机制来保证的;
    • 一致性则是通过持久性+原子性+隔离性来保证;

2.并发事务问题、隔离级别

  • 并发事务带来哪些问题?怎么解决这些问题呢?MySQL的默认隔离级别是?

    • 并发事务问题:脏读、不可重复读、幻读

    • 隔离级别:读未提交、读已提交、可重复读、串行化

      关系:先问问题,然后通过隔离级别解决

(一)并发事务问题

问题 描述
脏读 一个事务读到另外一个事务还没有提交的数据。
不可重复读 一个事务先后读取同一条记录,但两次读取的数据不同,称之为不可重复读。
幻读 在一个事务内多次查询某个符合查询条件的记录数量,如果出现前后两次查询到的记录数量不一样的情况,就意味着发生了幻读现象。

脏读:

没提交但是已经暂时修改了数据库的内容,事务B读到了修改后的数据

如果那个事务最终回滚,则读到的数据是无效的。 alt 不可重复读:

在同一个事务中,多次读取同一数据,由于其他事务的提交修改(UPDATE),导致读取结果不一致。 alt 幻读: alt

不可重复读和幻读有什么区别?
  • 不可重复读的重点是内容修改或者记录减少比如多次读取一条记录发现其中某些记录的值被修改
  • 幻读的重点在于记录新增比如多次执行同一条查询语句(DQL)时,发现查到的记录增加了。

(二)隔离级别

  • 怎么解决并发事务的问题呢?

    解决方案:对事务进行隔离

    隔离级别 脏读 不可重复读 幻读
    Read uncommitted 读未提交
    Read committed 读已提交 ×
    Repeatable Read(默认)可重复读 × ×
    Serializable 串行化 × × ×
    • 可重复读(REPEATABLE READ)能解决部分幻读问题,但不能完全解决所有幻读场景!

      具体来说:

      ✅ 能解决:快照读(普通SELECT) 的幻读

      ❌ 不能解决:当前读 + 写操作 导致的幻读

      -- 可重复读下的锁机制:
      -- 1. 快照读:不加锁,只读快照
      -- 2. 当前读:加记录锁(Record Lock)
      
      -- 问题:记录锁只能锁已存在的记录!
      -- 对于"不存在"的记录(即其他事务将要插入的记录),无法预先加锁
      

      场景1:纯读操作 - ✅ 解决

      -- 事务A
      BEGIN;
      SELECT * FROM users WHERE age > 20;  -- 返回3条记录
      
      -- 事务B插入并提交
      INSERT INTO users(name, age) VALUES('新用户', 25);
      COMMIT;
      
      -- 事务A再次查询
      SELECT * FROM users WHERE age > 20;  -- 还是返回3条记录!
      -- 可重复读通过MVCC避免了幻读
      

      场景2:读后写 - ❌ 未解决!

      -- 事务A
      BEGIN;
      -- 第一次查询:统计年龄>20的人数
      SELECT COUNT(*) FROM users WHERE age > 20;  -- 返回3
      
      -- 事务B插入
      INSERT INTO users(name, age) VALUES('新用户', 25);
      COMMIT;
      
      -- 事务A尝试更新这些用户
      UPDATE users SET vip = 1 WHERE age > 20;
      -- 影响了4行!包括事务B插入的那行
      
      -- 事务A再次查询
      SELECT COUNT(*) FROM users WHERE age > 20;  -- 返回4!
      -- 出现了幻读!
      
    • SERIALIZABLE(可串行化) :最高的隔离级别,完全服从 ACID 的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。

    注意:事务隔离级别越高,数据越安全,性能越差

3.undo log和redo log

  • 缓冲池(buffer pool):主内存中的一个区域,里面可以缓存磁盘上经常操作的真实数据,在执行增删改查操作时,先操作缓冲池中的数据(若缓冲池没有数据,则从磁盘加载并缓存),以一定频率刷新到磁盘,从而减少磁盘IO,加快处理速度

  • 数据页(page):是InnoDB存储引擎磁盘管理的最小单元,每个页的大小默认为16KB。页中存储的是行数据 alt

1)redo log

重做日志,记录的是事务提交时数据页的物理修改,是用来实现事务的持久性

该日志文件由两部分组成:重做日志缓冲(redo log buffer)以及重做日志文件(redo log file),前者是在内存中,后者在磁盘中。当事务提交之后会把所有修改信息都存到该日志文件中,用于在刷新脏页到磁盘,发生错误时,进行数据恢复使用。

顺序磁盘 IO 写操作可以媲美内存随机读写,性能很高! alt

(2)undo log

回滚日志,用于记录数据被修改前的信息,作用包含两个:提供回滚MVCC(多版本并发控制)。undo log和redo log记录物理日志不一样,它是逻辑日志

  • 可以认为当delete一条记录时,undo log中会记录一条对应的insert记录,反之亦然,

  • 当update一条记录时,它记录一条对应相反的update记录。当执行rollback时,就可以从undo log中的逻辑记录读取到相应的内容并进行回滚。

undo log可以实现事务的一致性和原子性

总结

  • undo log和redo log的区别

  • redo log:记录的是数据页的物理变化,服务宕机可用来同步数据(持久性)

  • undo log:记录的是逻辑日志,当事务回滚时,通过逆操作恢复原来的数据

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

4.mvcc

  • 事务中的隔离性是如何保证的呢?

    • 锁:排他锁(如一个事务获取了一个数据行的排他锁,其他事务就不能再获取该行的其他锁)
    • mvcc:多版本并发控制
  • 解释一下MVCC?

    全称Multi-Version Concurrency Control,多版本并发控制。指维护一个数据的多个版本,使得读写操作没有冲突。

    MVCC的具体实现,主要依赖于数据库记录中的隐藏字段、undo log日志、readView

MVCC-实现原理

1.记录中的隐藏字段
id age name DB_TRX_ID DB_ROLL_PTR DB_ROW_ID
隐藏字段 含义
DB_TRX_ID 最近修改事务ID,记录插入这条记录或最后一次修改该记录的事务ID
DB_ROLL_PTR 回滚指针,指向这条记录的上一个版本,用于配合undo log,指向上一个版本
DB_ROW_ID 隐藏主键,如果表结构没有指定主键,将会生成该隐藏字段
2.undo log

回滚日志, 在insert、update、delete的时候产生的便于数据回滚的日志。

当insert的时候,产生的undo log日志只在回滚时需要,在事务提交后,可被立即删除。

而update、delete的时候,产生的undo log日志不仅在回滚时需要,mvcc版本访问也需要,不会立即被删除。

  • undo log版本链 alt alt 不同事务或相同事务对同一条记录进行修改,会导致该记录的undo log生成一条记录版本链条,链表的头部是最新的旧记录,链表尾部是最早的旧记录。
3.read view

ReadView(读视图)是快照读SQL执行时MVCC提取数据的依据,记录并维护系统当前活跃的事务(未提交的)id。

  • 当前读 读取的是记录的最新版本,读取时还要保证其他并发事务不能修改当前记录,会对读取的记录进行加锁。对于我们日常的操作,如:

​ select ... lock in share mode(共享锁),select ... for update、update、insert、delete(排他锁)都是一种当前读。

  • 快照读

    简单的select(不加锁)就是快照读,快照读,读取的是记录数据的可见版本,有可能是历史数据,不加锁,是非阻塞读。

    • Read Committed:每次select,都生成一个快照读。

    • Repeatable Read:开启事务后第一个select语句才是快照读的地方。

ReadView中包含了四个核心字段:

字段 含义
m_id 当前活跃的事务ID集合
min_trx_id 最小活跃集合ID
max_trx_id 预分配事务ID,当前最大事务ID + 1(因为事务ID是自增的)
creator_trx_id ReadView创建者的事务ID

例如,对于上面这个事务图,在事务5第一次“查询id为30的记录”时,它的

m_ids是3,4,5(因为事务2已提交)

min_trx_id是3

max_trx_id是6

creator_trx_id是5 简单地说,就是 可以访问已提交的事务数据,或者当前版本的事务数据

  • 版本链数据访问规则(trx_id:代表是当前事务ID):

    1. trx_id == creator_trx_id ?可以访问该版本 ——>成立,说明数据是当前这个事务更改的。

    2. trx_id<min_trx_id ?可以访问该版本 ——>成立,说明数据已经提交了。

    3. trx_id>max_trx_id ?不可以访问该版本 ——>成立,说明该事务是在ReadView生成后才开启。

    4. min_trx_id <= trx_id <= max_trx_id ?如果trx_id不在m_ids中是可以访问该版本的 ——>成立,说明数据已经提交。

不同的隔离级别,生成ReadView的时机不同:

READ COMMITTED:在事务中每一次执行快照读时生成ReadView。 REPEATABLE READ:仅在事务中第一次执行快照读时生成ReadView,后续复用该ReadView。

(1)RC隔离级别下,在事务中每一次执行快照读时生成ReadView。

例如,对于上面这个事务图,在事务5第一次“查询id为30的记录”时,它的

ReadView

m_ids: {3,4,5}
min_trx_id: 3
max_trx_id: 6
creator_trx_id: 5

trx_id就是拿版本链中的DB_TRX_ID来依次对比,到2的时候满足第二个条件,读到数据0x0002

alt 不同事务或相同事务对同一条记录进行修改,会导致该记录的undo log生成一条记录版本链条,链表的头部是最新的旧记录,链表尾部是最早的旧记录。

3.read view

ReadView(读视图)是快照读SQL执行时MVCC提取数据的依据,记录并维护系统当前活跃的事务(未提交的)id。

  • 当前读 读取的是记录的最新版本,读取时还要保证其他并发事务不能修改当前记录,会对读取的记录进行加锁。对于我们日常的操作,如:

​ select ... lock in share mode(共享锁),select ... for update、update、insert、delete(排他锁)都是一种当前读。

  • 快照读

    简单的select(不加锁)就是快照读,快照读,读取的是记录数据的可见版本,有可能是历史数据,不加锁,是非阻塞读。

    • Read Committed:每次select,都生成一个快照读。

    • Repeatable Read:开启事务后第一个select语句才是快照读的地方。

ReadView中包含了四个核心字段:

字段 含义
m_id 当前活跃的事务ID集合
min_trx_id 最小活跃集合ID
max_trx_id 预分配事务ID,当前最大事务ID + 1(因为事务ID是自增的)
creator_trx_id ReadView创建者的事务ID

例如,对于上面这个事务图,在事务5第一次“查询id为30的记录”时,它的

m_ids是3,4,5(因为事务2已提交)

min_trx_id是3

max_trx_id是6

creator_trx_id是5 简单地说,就是 可以访问已提交的事务数据,或者当前版本的事务数据

  • 版本链数据访问规则(trx_id:代表是当前事务ID):

    1. trx_id == creator_trx_id ?可以访问该版本 ——>成立,说明数据是当前这个事务更改的。

    2. trx_id<min_trx_id ?可以访问该版本 ——>成立,说明数据已经提交了。

    3. trx_id>max_trx_id ?不可以访问该版本 ——>成立,说明该事务是在ReadView生成后才开启。

    4. min_trx_id <= trx_id <= max_trx_id ?如果trx_id不在m_ids中是可以访问该版本的 ——>成立,说明数据已经提交。

不同的隔离级别,生成ReadView的时机不同:

READ COMMITTED:在事务中每一次执行快照读时生成ReadView。 REPEATABLE READ:仅在事务中第一次执行快照读时生成ReadView,后续复用该ReadView。

(1)RC隔离级别下,在事务中每一次执行快照读时生成ReadView。

例如,对于上面这个事务图,在事务5第一次“查询id为30的记录”时,它的

ReadView

m_ids: {3,4,5}
min_trx_id: 3
max_trx_id: 6
creator_trx_id: 5

trx_id就是拿版本链中的DB_TRX_ID来依次对比,到2的时候满足第二个条件,读到数据0x0002

ReadView

m_ids: {4,5}
min_trx_id: 4
max_trx_id: 6
creator_trx_id: 5

alt (2)RR隔离级别下,仅在事务中第一次执行快照读时生成ReadView,后续复用该ReadView。

访问的是事务2 alt

总结

  • 好的,事务中的隔离性是如何保证的呢?(你解释一下MVCC)

MySQL中的多版本并发控制。指维护一个数据的多个版本,使得读写操作没有冲突。他的底层实现主要分为了三个部分:

  • 隐藏字段:
  1. trx_id(事务id),记录每一次操作的事务id,是自增的
  2. roll_pointer(回滚指针),指向上一个版本的事务版本记录地址
  • undo log:
  1. 回滚日志,存储老版本数据
  2. 版本链:多个事务并行操作某一行记录,记录不同事务修改数据的版本,通过roll_pointer指针形成一个链表
  • readView解决的是一个事务查询选择版本的问题

    • 根据readView的匹配规则和当前的一些事务id判断该访问那个版本的数据

    • 不同的隔离级别快照读是不一样的,最终的访问的结果不一样

​ RC:每一次执行快照读时生成ReadView

​ RR:仅在事务中第一次执行快照读时生成ReadView,后续复用

三、主从同步原理

MySQL主从复制的核心就是二进制日志

**二进制日志(BINLOG)**记录了所有DDL(数据定义语言)语句和DML(数据操纵语言)语句,但不包括数据查询(SELECT、SHOW)语句。 alt 复制分成三步:

  1. Master 主库在事务提交时,会把数据变更记录在二进制日志文件 Binlog中。

  2. 从库读取主库的二进制日志文件Binlog,写入到从库的中继日志 Relay Log。

  3. slave重做中继日志中的事件,将改变反映它自己的数据。

四、分库分表

  • 你们项目用过分库分表吗?

主库从库分担了访问压力,但解决不了海量数据存储

分库分表的时机:

  1. 前提,项目业务数据逐渐增多,或业务发展比较迅速(比如:单表的数据量达1000W20G以后)
  2. 优化已解决不了性能问题(主从读写分离、查询索引。。)
  3. IO瓶颈(磁盘IO、网络IO)、CPU瓶颈(聚合查询、连接数太多)

1.垂直分库

垂直分库:以表为依据,根据业务将不同表拆分到不同库中。

特点:

  1. 按业务对数据分级管理、维护、监控、扩展
  2. 在高并发下,提高磁盘IO和数据量连接数 alt

2.垂直分表

垂直分表:以字段为依据,根据字段属性将不同字段拆分到不同表中。

特点:

  1. 冷热数据分离
  2. 减少IO过度争抢,两表互不影响

拆分规则:

  • 把不常用的字段单独放在一张表
  • 把text,blob等大字段拆分出来放在附表中 alt

3.水平分库

水平分库:将一个库的数据拆分到多个库中。

特点:

  1. 解决了单库大数量,高并发的性能瓶颈问题
  2. 提高了系统的稳定性和可用性

路由规则:

  • 根据id节点取模
  • 按id也就是范围路由,节点1(1-100万),节点2(100万-200万) alt

4.水平分表

水平分表:将一个表的数据拆分到多个表中(可以在同一个库内)。

特点:

  1. 优化单一表数据量过大而产生的性能问题;
  2. 避免IO争抢并减少锁表的几率。

alt

分库之后的问题:

  • 分布式事务一致性问题

  • 跨节点关联查询

  • 跨节点分页、排序函数

  • 主键避重

分库分表中间件:

  • sharding-sphere

  • mycat

总结

  • 你们项目用过分库分表吗

  • 业务介绍

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

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

  • 具体拆分策略

1,水平分库,将一个库的数据拆分到多个库中,解决海量数据存储和高并发的问题 sharding-sphere, mycat

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

3,垂直分库,根据业务进行拆分,高并发下提高磁盘IO和网络连接数

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

#面试被问“你的缺点是什么?”怎么答##数据人的面试交流地##面试时最害怕被问到的问题##你觉得哪一届的校招最难?##我的求职总结#
全部评论

相关推荐

12-22 14:21
已编辑
门头沟学院 Java
bg:&nbsp;双二&nbsp;非科班&nbsp;无实习&nbsp;&nbsp;最后签了得物(给的有点多,希望强度能顶住)时间线:&nbsp;今年七月份开始投&nbsp;&nbsp;八月九月开始大量面试&nbsp;&nbsp;十一月十二月开奖总共参加了四十场面试&nbsp;最后拿到了九个offer&nbsp;不过大部分是中厂&nbsp;大厂还是差口气面试线:美团一面挂京东一面挂淘天二面挂字节一面挂讯飞三面+hr面&nbsp;&nbsp;意向顺丰线下三面&nbsp;&nbsp;&nbsp;&nbsp;意向途虎三次面试&nbsp;意向上海银行线下面&nbsp;&nbsp;意向携程线下两面+线上hr+加面&nbsp;意向快手客户端三面+hr&nbsp;&nbsp;意向去哪儿&nbsp;两件+&nbsp;hr&nbsp;&nbsp;意向拼多多客户端三面+hr&nbsp;&nbsp;直接开奖得物两个部门六次面试&nbsp;直接开奖先是签了携程&nbsp;但是后来得物开了个比较大的包,且业务较为核心,所以和携程解约跟得物签了,说实话也不知道这个决定是好是坏,毕竟携程再怎么变也肯定比得物wlb的多,其实挺想去携程的各方面,但是实在是业务跟薪资不太行,希望日后能顶得住得物的强度。其实今年刚开始找的时候还是挺迷茫的,自己不是科班,是个地理相关的专业,又没有实习,感觉确实劣势很大,几乎每场面试都会问为什么没有实习,但是好在挺下来了,虽然头部大厂没什么机会,感觉他们很看实习,面试的时候感觉没什么大问题,结果面完就挂,也是没什么办法,所以如果有机会能刷个实习履历,一定还是要刷一个,不管是面试还是谈薪的时候都要有底气很多。很多时候也会迷茫,互联网朝十晚十的生活到底是不是自己想要的,钱就真那么重要吗?重要到连基本的生活都可以放弃,我还是很想下了班有时间,去喝一杯,去骑会车或者打会游戏,签了互联网就像签了卖身契一样,把自己的灵魂出卖给公司,用工时去换似乎很高的工资,但这一切值得吗,拿心理或是身体健康去换一段时间的快钱,而且还有对未来的迷茫,对35岁的焦虑,但是要是真让你去放弃,去考个编制,或者找一些轻松点,赚的少点的工作,又总是不甘心,人总是矛盾的。读研之前,我觉得毕业能找份本专业的工作,每个月赚个一万多就很知足了,但是读研之后见到、听到的多,似乎心也无法平静,也想去赚一份高薪,我不知道这个选择是否正确,我不知道我人生中的每一个选择是否正确,也许日后看来并不是最优选,不过这也是我站在雾里,所能看到,能做到的、看来是正确的选择了,至于感受如何,也许只有一年后的今年能回答了,哈哈,一年后的今天我一定回到这个帖子,聊一聊工作了半年之后,到底感觉如何。
2025年终总结
点赞 评论 收藏
分享
这里给到体验感好差九坤投资可以说是我见过最逆天的公司了。数据实习生岗位。面试官一上来,就笑嘻嘻的问:“有人找你拍广告吗?”我还没理解他什么意思,他接着就说,感觉你长的很像明星。。。?这啥玩意啊。首先鄙人就是一般长相,抛开这个不谈,他是怎么说出这么雷人的问题的?接着就是问我们学校保研情况,感觉也是比较了解我们学校,我以为还他是老学长,结果告诉我在我们学校旁边上的。。可是。。。我们学校旁边是一所代砖啊下面终于步入正轨了,问我项目是自己写的吗,共享屏幕运行一下他想看看,我运行过程中他就盯着面板让我解释什么意思,结果刚好报错redis&nbsp;disconnect他就让我排查一下这个bug。因为我是在linux起的redis服务,检查一下是服务还没启动。起服务过程中发现我给密码忘了,我就说我密码忘了,面试官让我找一下,我说去看看配置文件(.conf)吧。打开配置文件是密密麻麻的字,他一脸不屑的问:“怎么可能在这?”&nbsp;。。?等我把密码找出来了他不说话了😅后续就是,前几天压测时&nbsp;stream里的任务没及时清理,任务进不去了,导致项目启动就报错,具体错误有点忘了。我跟他把可能的问题讲了一遍,他就一直在那笑。最后时间快到了,我也不想跟他聊了。到我反问环节了,我问他平时redis用的很多吗。这个d毛说他没用过。。。感觉被侮辱了因为我共享屏幕看不到他的脸,我真该多看他几眼,记住这个d毛。我只能说这种人,10个m都不够s的
你面试体验感最差/最好的...
点赞 评论 收藏
分享
评论
1
3
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务