当前位置: 首页 > ds >正文

【常见的面试题总结】

文章目录

  • 介绍下mysql的redolog,undolog,binlog
  • Mysql 两阶段提交(主要从redolog和undolog)
  • mysql的数据刷盘
  • mysql刷盘流程
  • 介绍下MVCC
  • mysql崩溃重启会读那些日志
  • 怎么在不改变隔离级别的情况下,解决幻读
  • 可重复隔离级别模式下是是怎么解决幻读的
  • kafka并发消费。并发度比较高,怎么实现的。
  • 介绍下MQ的顺序消费和并发消费及其原理。
  • 怎么解决redis的lua脚本超时
  • MQ消息堆积怎么解决
  • MQ怎么解决消息丢失
  • MQ怎么解决消息重复消费
  • xxl-job异常场景,执行器返回结果丢失,调度器将任务置为失败,失败任务会进行重试的场景,怎么保证幂等性。
  • select 、poll、epoll多路复用机制。
  • 如何设计一个基于 NIO 的高并发服务端?
  • NIO 中 Selector 为什么能避免阻塞?
  • Netty 是什么?有什么特点?
  • Netty 是如何基于 Java NIO 实现高并发的?
  • Nio怎么实现的同步非阻塞
  • volatile 为什么不能保证原子性?
  • kafka的并发数据量会比MQ的大这么多?

介绍下mysql的redolog,undolog,binlog

  1. Redo Log(重做日志)
    作用:
    保证事务的持久性(Durability),即即使数据库崩溃,也能通过 redo log 恢复已提交事务的数据。
    用于 InnoDB 的 物理恢复(恢复数据页的修改)。
    特点:
    物理日志,记录页的修改(脏页数据);
    顺序写,写磁盘效率高;
    主要用于崩溃恢复(Crash Recovery),重放 redo log 恢复脏页;
    属于 InnoDB 存储引擎内部日志,不对外公开。
  2. Undo Log(回滚日志)
    作用:
    实现 事务的原子性(Atomicity) 和 隔离性(Isolation);
    支持事务回滚(Rollback),通过 undo log 回滚未提交的修改;
    支持 MVCC,提供多版本快照读。
    特点:
    逻辑日志,记录数据修改前的旧版本(旧值);
    每次修改数据前,都会生成 undo log;
    用于回滚事务(撤销未提交数据);
    支持 MVCC 快照,读事务通过 undo log 访问历史版本数据;
    存储在系统表空间或独立 undo 表空间。
  3. Binlog(二进制日志)
    作用:
    记录所有修改数据库的操作(DDL、DML),用于 主从复制 和 增量备份恢复;
    保障数据在多个 MySQL 实例间的同步一致性。
    特点:
    逻辑日志,记录 SQL 语句或行修改事件;
    由 MySQL Server 层生成,所有存储引擎都使用同一个 binlog;
    非事务引擎也有 binlog(比如 MyISAM);
    支持事务的 原子提交(结合两阶段提交机制保证 binlog 与 redo log 一致);
    可配置三种格式:STATEMENT(语句)、ROW(行)、MIXED(混合)。

Mysql 两阶段提交(主要从redolog和undolog)

MySQL 的两阶段提交机制,通常是指 InnoDB 引擎与 Binlog 写入之间的两阶段提交协议(2PC)。它的主要目的是在 保证事务持久性(持久化到磁盘) 的同时,也要确保主从复制的一致性,防止 “事务提交但 Binlog 没写成功” 或 “Binlog 写了但事务没提交” 这类分布式一致性问题。
一 准备阶段(Prepare)

  1. InnoDB 写入 Redo Log(prepare 状态)
    把事务变更写入 Redo Log(WAL),标记为 prepare,先不提交
    保证宕机恢复时能重放事务
  2. 写入磁盘(fsync)
    MySQL Server 写入 Binlog(逻辑日志)
    把该事务的逻辑操作写入 Binlog 缓存
    并持久化到磁盘(fsync)

二 提交阶段(Commit)
InnoDB 接收到 Binlog 写入成功的信号
然后 InnoDB 执行 Redo Log 的 commit 操作
此时事务正式提交
为什么要分两个阶段?
保证 Binlog 与 Redo Log 的一致性:
两者都写成功 → 正常提交
有一方失败 → 都不提交(例如宕机时,InnoDB 看到的是 prepare 状态,就可以自动回滚)

mysql的数据刷盘

InnoDB 的刷盘机制通过 redo log(WAL)+ Buffer Pool + checkpoint 来保证数据一致性和持久化。事务提交时 redo log 先刷盘,后台线程周期性将 Buffer Pool 中的脏页通过 checkpoint 刷盘。为了防止页写入中断损坏,还使用了 doublewrite buffer。参数如 innodb_flush_log_at_trx_commit 和 innodb_io_capacity 可调节刷盘频率和安全性。

mysql刷盘流程

  1. 从磁盘加载目标数据页到 Buffer Pool(如果还未在内存中)
  2. 在 Buffer Pool 中修改数据页内容(产生脏页 Dirty Page)
    → 数据并未立刻写回磁盘。
  3. 生成 Redo Log 记录修改(逻辑或物理更改)
    → redo log 写入 redo log buffer(内存中的日志缓存)。
  4. 事务提交时(COMMIT):
    刷 redo log 到磁盘(WAL:Write-Ahead Logging),称为 redo log 的刷盘;
    此时事务才真正被认为“提交成功”。
  5. 后台线程异步将脏页刷新到磁盘(刷盘)
    → 这个是刷盘动作,称为 Checkpoint,由后台线程控制。

介绍下MVCC

  1. 什么是 MVCC?
    MVCC 是一种数据库实现 并发控制(Concurrency Control) 的技术,目的是实现:
    高并发下数据的读写隔离;
    避免加锁带来的阻塞,提升系统吞吐量;
    支持数据库的隔离级别,比如 Repeatable Read。
  2. MySQL InnoDB 中的 MVCC 实现
    1. 隐藏字段
      每条记录有两个隐藏字段:
      trx_id:创建这条数据的事务ID;
      roll_ptr:指向旧版本数据的指针(undo log 位置)。
    2. 事务快照
      每个事务启动时,会生成一个快照,记录当前活跃的事务ID列表;
      读取时,事务会检查某条记录的 trx_id 是否在快照范围内,决定是否可见。
    3. 读取过程
      读取当前版本数据(最新记录);
      如果当前版本对该事务不可见,则沿 roll_ptr 找到旧版本,直到找到对事务可见的版本。

mysql崩溃重启会读那些日志

崩溃恢复时,MySQL 会读取 redo log 来重放已提交但未刷盘的事务修改,确保数据不会丢失。同时使用 undo log 回滚未提交事务的变更,保证数据一致性。这是基于 InnoDB 的 WAL(日志先行)和多版本控制机制。

怎么在不改变隔离级别的情况下,解决幻读

不改变隔离级别(比如仍保持 READ COMMITTED)的情况下,解决幻读,主要依靠 显式加锁 来防止“幻影行”插入。以下是常用做法:

  1. 使用范围锁(间隙锁 Gap Lock)
    虽然在 READ COMMITTED 下,InnoDB 默认不加间隙锁,但你可以通过 显式加锁让 InnoDB 对查询范围加锁,从而避免幻读。
    SELECT * FROM table WHERE condition FOR UPDATE;
    SELECT * FROM table WHERE condition LOCK IN SHARE MODE;
    这会对满足条件的行以及范围内的间隙加锁,阻止其他事务在该范围插入新行。
    幻读是因为在同一事务中,两次查询条件范围之间插入了新数据;
    通过加范围锁,阻止其他事务插入这段范围内的数据;
    虽然不改变隔离级别,但显式加锁起到了类似 REPEATABLE READ 的防幻读效果。

可重复隔离级别模式下是是怎么解决幻读的

在 可重复读(REPEATABLE READ)隔离级别 下,MySQL(InnoDB引擎)通过 MVCC + next-key锁(间隙锁 + 行锁的组合) 来避免幻读。
具体机制

  1. MVCC(多版本并发控制)
    保证同一事务内,所有的读操作看到的是同一时间点的快照数据版本;
    防止不可重复读,但单靠 MVCC 无法阻止幻读,因为幻读是新插入的“幻影行”。
  2. Next-Key Lock(前键锁)
    InnoDB 对读取的范围使用 next-key锁,即对记录的索引记录加行锁 + 对行与行之间的间隙加间隙锁(gap lock);
    这样就锁住了索引记录和它前后的间隙,阻止其他事务在该范围插入新行,防止幻读
    例子:对 [a, b] 范围加锁,不允许其他事务插入 a 和 b 之间的新行。

工作流程举例:
事务A执行范围查询 SELECT * FROM orders WHERE id BETWEEN 100 AND 200;
InnoDB 给满足条件的记录加行锁,同时给范围的间隙加间隙锁;
事务B尝试在 id=150 插入新记录时会被阻塞,直到事务A提交;
事务A多次查询同一范围,不会看到新插入的“幻影行”。

kafka并发消费。并发度比较高,怎么实现的。

  1. 分区机制天然并发
    Kafka 的每个 Topic 可以有多个 Partition,每个 Partition 可以独立被不同线程消费:
    一个消费者线程最多一个分区;
    多个分区就能同时被多个线程并发消费;
    分区越多,并发能力越强。
    吞吐随分区数线性提升(前提是磁盘、CPU 跟得上)。
  2. 消费是客户端拉取模型(Pull)
    Kafka 的消费者主动去拉取数据:
    批量拉取(fetch size 可配置)→ 减少网络往返;
    多线程并发拉取多个分区 → 提高并发;
    不需要 Broker 主动推送 → Broker 更轻量。

介绍下MQ的顺序消费和并发消费及其原理。

顺序消费,单线程,单队列处理。
并发消费,多队列 , 多线程处理 ,牺牲顺序,换取吞吐性能

怎么解决redis的lua脚本超时

Redis 的 Lua 脚本执行超时,通常是因为脚本执行超过了配置的 lua-time-limit(默认 5000ms),导致 Redis 进入“忙碌(BUSY)状态”,此时 Redis 无法处理其他请求,严重时可能需要强制中断脚本。

  1. 优化 Lua 脚本逻辑
    控制数据量:每次操作控制在 1000 条以内(视情况);
    拆分为多个小脚本 + 多次调用;
    避免写复杂的排序、分页、嵌套循环;
    避免操作跨 slot(在 Redis Cluster 中);
  2. 提高 Lua 执行超时时间(临时手段
  3. 应急终止脚本:SCRIPT KILL
  4. 重构为客户端控制逻辑(根本优化)
    Lua 脚本只判断库存 & 减库存;
    客户端控制分批、分页、数据整合等逻辑;

MQ消息堆积怎么解决

  1. 加快消费速度
    增加消费者数量(扩容)或线程池大小;
    提高每条消息处理效率(优化业务逻辑);
    使用异步/批量消费,减少 I/O 阻塞;
    减少重试逻辑/幂等性逻辑的性能开销;
    示例:RocketMQ 可以使用 MessageListenerConcurrently 并发消费
  2. 使用并行消费(分区/分片)
    Kafka / RocketMQ 都支持 Topic 下多个 Partition;
    确保消息按队列均匀分布,避免热点 Partition;
    每个 Partition 可以被不同消费者线程消费,实现并行;
  3. 加速消息投递与过滤
    使用更高效的协议或网络栈;
    消费端只订阅需要的 tag,减少无效过滤逻辑;
    提前做消息过滤(如 RocketMQ 的 tag filtering / SQL92 表达式);
  4. 调整 Broker 配置
    Broker 调整队列数、IO线程池大小;
    增加 broker 副本数量,实现负载分担;
    增大 maxMessageSize、pullBatchSize、consumeMessageBatchMaxSize(RocketMQ)
  5. 临时限流生产者或丢弃非关键消息
    消息堆积太多,优先保核心业务的消息;
    可以对低优先级的 producer 限流、丢弃;
    RocketMQ 提供延迟消息、死信队列机制;
  6. 顺序消费的情况下,单个消息的报错阻塞后续的消息,导致单个队列的消息堆积。

MQ怎么解决消息丢失

1.生产者同步发送
2.手动的ack应答机制。
3.broker的持久刷设置同步刷盘机制。

MQ怎么解决消息重复消费

redis分布式锁锁住消息的msgId,一般还会设置消息的过期时间。
使用数据库的唯一索引保证数据的唯一性。

xxl-job异常场景,执行器返回结果丢失,调度器将任务置为失败,失败任务会进行重试的场景,怎么保证幂等性。

幂等性的使用一般依赖于mysql的唯一索引或者redis的分布式锁,具体的原因具体的分析。

select 、poll、epoll多路复用机制。

目前支持I/O多路复用的系统调用有select,pselect,poll,epoll。与多进程和多线程技术相比,I/O多路复用技术的最大优势是系统开销小,系统不必创建进程/线程,也不必维护这些进程/线程,从而大大减小了系统的开销。
I/O多路复用就是通过一种机制,一个进程可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。但select,poll,epoll本质上都是同步I/O,因为他们都需要在读写事件就绪后自己负责进行读写,也就是说这个读写过程是阻塞的,而异步I/O则无需自己负责进行读写,异步I/O的实现会负责把数据从内核拷贝到用户空间

如何设计一个基于 NIO 的高并发服务端?

一个 Acceptor 线程负责接收连接;
多个 Selector 线程负责处理读写;
每个客户端连接用非阻塞 Channel;
使用消息队列 + Reactor 模式分离业务逻辑和 IO 处理。

NIO 中 Selector 为什么能避免阻塞?

因为它使用了非阻塞的 Channel;
且只有当通道就绪时(如可读),Selector 才会通知我们;
调用 read() 不会阻塞线程(无数据时返回 0)。

Netty 是什么?有什么特点?

基于 NIO 的高性能异步事件驱动网络框架;
支持多协议开发(TCP/UDP/HTTP 等);
提供灵活的编解码、事件处理机制;
线程模型和零拷贝设计提升性能。

Netty 是如何基于 Java NIO 实现高并发的?

使用 Java NIO 的 Selector 机制;
自定义事件循环(EventLoopGroup);
一个 EventLoop 绑定多个 Channel,每个 Selector 在单线程中高效轮询;
大量优化了 Selector 的使用(如 epoll bug workaround)

Nio怎么实现的同步非阻塞

  1. 非阻塞模式(Non-blocking)
    在非阻塞模式下,调用 read()、write() 等操作不会阻塞当前线程;
    如果数据未准备好,调用立即返回,不会挂起线程;
    这样线程可以同时处理多个通道。
  2. 多路复用(Selector)
    Selector 是 NIO 的核心,它监控多个 Channel 的事件(连接、读、写等);
    线程调用 selector.select(),等待感兴趣的事件发生;
    当事件就绪,线程被唤醒,轮询所有就绪的通道,进行对应处理;
    线程不会为每个连接创建一个独立线程,避免大量线程切换开销。

volatile 为什么不能保证原子性?

原子性:一个操作要么全部完成、要么完全不做,中间不能被线程调度器打断。
而volatile并不能保证我们的对象只能被一个线程处理完成,因此不具备原子性。

kafka的并发数据量会比MQ的大这么多?

  1. Kafka:一个分区对应一个日志文件(Segment Log)
    每个 Partition 是一个独立的日志文件,写入和读取相互隔离,且顺序写磁盘;
    每个 Partition 有唯一的 Leader 负责写入,多个消费者按分区划分并行消费,天然支持多线程并发消费;
    读写操作互不影响,减少锁竞争和上下文切换;
    多 Partition 可以分布在不同 Broker,实现跨节点并行扩展。
    优势:
    并发写入和读取无锁冲突,性能极高;
    通过分区天然实现消息的水平并行处理;
    消费者组内,分区被多个消费者分担,读写分离,提高吞吐。
  2. 传统 MQ:所有数据写到一个 CommitLog 文件(或者单一队列)
    传统 MQ 多数采用一个统一的存储队列(CommitLog)或内存结构,所有消息写入同一个文件/内存队列;
    读写通常竞争同一个队列资源,导致锁竞争和上下文切换;
    消费者通常采用 push 模型,消息一旦消费即删除,且消息顺序强依赖单队列结构;
    并发度受限于单个队列的锁和 IO 资源瓶颈。
    劣势:
    多消费者并发竞争单个队列锁,写读冲突频繁,性能瓶颈明显;
    单个文件或队列成瓶颈,难以横向扩展;
    消费者间的负载均衡、扩展性差。
http://www.xdnf.cn/news/6709.html

相关文章:

  • Spring Cloud:Gateway(统一服务入口)
  • 从 Vue3 回望 Vue2:性能优化内建化——从黑盒优化到可控编译
  • Spring循环依赖详解
  • idea启用lombok
  • 【网络编程】十、详解 UDP 协议
  • 使用Python实现简单的人工智能聊天机器人
  • 海康相机连接测试-极简版
  • Python Socket编程:实现简单的客户端-服务器通信
  • Linux运行时的参数、命令、网络、磁盘参数和日志监控
  • 小黑独自享受思考心流:80. 删除有序数组中的重复项 II
  • 雷云4 鼠标滚轮单击失灵解决办法
  • 智慧灌区测控一体化闸门系统综合解决方案
  • 2:OpenCV—加载显示图像
  • 梯度下降算法:原理、实现与可视化分析
  • 谷歌地图代理 | 使用 HTML 和矢量模式 API 更轻松地创建 Web 地图
  • 软件测试—接口测试面试题及jmeter面试题
  • 记参加一次数学建模
  • C++运算符重载练习
  • Android 中使用通知(Kotlin 版)
  • PPT 转高精度 PDF API 接口
  • 【流程控制结构】
  • Vue.js教学第一章: Vue 简介与环境搭建
  • neo4j框架:java安装教程
  • 《项目管理知行合一:知识体系构建与实战应用指南》
  • 十步法基于Vanna打造高效便捷的 SQL 生成与业务洞察工具
  • 消息队列与Kafka基础:从概念到集群部署
  • 文件上传Ⅲ
  • 基于React的高德地图api教程007:椭圆的绘制、编辑和删除
  • 【项目】自主实现HTTP服务器:从Socket到CGI全流程解析
  • C++ --- new与delete