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

八股-2025.10.24

1.消息队列如何保证不重复消费?

解决重复消费的核心是 **“让消费逻辑具备幂等性”**(多次执行结果一致),同时配合 MQ 机制减少重复投递,具体分 4 类实战方案:

A. 业务层幂等设计:最通用、最核心的方案

无论 MQ 如何管控,最终需通过业务逻辑确保 “重复消费无害”,常见 3 种实现方式:

a.基于 “业务唯一 ID” 去重给每条消息绑定一个全局唯一 ID(如订单号、交易流水号,或用雪花算法生成msgId),消费前先校验该 ID 是否已处理:

  • 高并发场景:用 Redis 的SETNX(或setIfAbsent)判断,例如key=“order:pay:”+orderNo,存在则跳过,不存在则执行业务并设置过期时间(避免内存占用);
  • 强一致性场景(如金融):用数据库防重表,表中msg_unique_id设唯一索引,消费前插入记录,插入成功则处理,失败则跳过(利用数据库唯一约束保证幂等)。

b.基于状态机控制针对有明确状态流转的业务(如订单:待支付→已支付→已发货),消费前先检查当前状态是否符合处理条件。例如 “订单支付成功” 消息,若订单已处于 “已支付” 状态,直接跳过重复处理。

b.基于乐观锁更新数据库操作时,用版本号 / 时间戳控制并发,避免重复更新。例如扣库存时,SQL 写为UPDATE stock SET num=num-1, version=version+1 WHERE goods_id=? AND version=?,只有版本匹配时才执行,重复消费会因版本不匹配失败。

B. MQ 手动确认机制:减少重复投递源头

针对支持手动 Ack 的 MQ(如 RabbitMQ),关键是 **“业务成功后再发 Ack”**,而非拿到消息就确认:

  • 禁用自动 Ack,开启手动 Ack(RabbitMQ 需配置ackMode=MANUAL);
  • 消费者流程:先接收消息→执行业务逻辑→业务成功后,调用channel.basicAck()发送确认→Broker 删除消息;
  • 若业务失败(如数据库临时不可用),可拒绝消息(basicReject)并让其进入重试队列,避免直接丢弃;若业务无法重试(如参数错误),直接 Ack 并丢入死信队列(DLQ)人工处理。

3. 生产者端:避免重复发送

从消息源头减少重复,需确保 “生产者不重复发消息”:

  • 幂等性发送:给每条消息生成唯一msgId,生产者发送前先检查本地缓存 / RocketMQ 的 “事务消息” 机制,避免重复发送;
  • 确认机制:依赖 MQ 的生产者确认(如 RabbitMQ 的publisher confirms、Kafka 的acks=all),确保 Broker 成功接收后再结束,避免因 “未确认” 触发重试。

4. Broker 端:去重缓存与重试管控

部分 MQ 可通过 Broker 层配置减少重复投递:

  • 去重缓存:Broker 维护已处理消息的msgId缓存(如 Redis),接收消息时先校验,重复则直接丢弃(需注意缓存清理策略,避免内存溢出);
  • 限制重试次数:配置最大重试次数(如 RabbitMQ 的x-max-retry-count),超过次数后移至死信队列,避免无限重复投递;
  • Kafka 特殊处理:利用offset机制,消费者维护已消费的offset并及时提交(建议 “业务成功后提交 offset”),重启后从最新offset消费,减少重复。

https://www.bilibili.com/video/BV1sc411t7ny/?spm_id_from=333.337.search-card.all.click&vd_source=3abe3667e67749032f72d6f512b2a967

2.布隆过滤器的原理。

布隆过滤器是一种空间效率高,适合大规模数据集合的概率型数据结构,主要用于判断一个数据在不在集合中。布隆过滤器包括一个位数组和多个哈希函数,初始时所有位置都被置为0。当一个元素经过多个哈希函数计算后,将对应的位数组位置置为1.查询时,如果元素经过哈希函数计算后的位置都是1,则说明元素可能存在,如果有一个位置是0,则元素一定不存在。

特性

说明

1. 空间高效

用 bit 存储,空间复杂度是 O(m)

m

远小于集合实际大小)。例如存储 1 亿个元素,m

取 1.2GB 时,误判率可低至 0.1%,远优于哈希表(哈希表存储 1 亿个整数需约 400MB,但无误判,需对比)。

2. 查询 / 插入高效

时间复杂度是 O(k)

(仅需执行 k

次哈希和取模),k

通常取 5~10,速度远超数据库查询或磁盘 IO。

3. 有误判、无漏判

- 误判:“不存在”→“存在”(假阳性),无法完全避免,只能通过调大 m

k

降低;- 无漏判:“存在”→“不存在”(假阴性)绝对不会发生,只要元素曾插入,必能判定 “可能存在”。

4. 不支持删除

因为一个 bit 位可能被多个元素共享(如索引 3 被 “apple” 和 “grape” 同时映射),删除时将 bit 置 0 会导致其他元素的查询误判(假阴性)。(注:有变种 “计数布隆过滤器” 支持删除,但空间占用翻倍,实用性较低)

https://www.bilibili.com/video/BV1zK4y1h7pA/?spm_id_from=333.337.search-card.all.click&vd_source=3abe3667e67749032f72d6f512b2a967

3.说说synchronize的用法及原理

A.用法

可作用于方法和代码块。

a.作用于实例方法

锁对象:当前类的实例对象(this),即“哪个对象调用该方法,就锁哪个对象”。

效果:同一时间,只有一个线程能执行该实例的此方法,不同实例的方法可以并行执行(因为锁对象不同)。

b.作用于静态方法

锁对象:当前类的Class对象(每个类在JVM中只有一个Class对象,全局唯一)。

效果:同一时间,所有线程(无论哪个实例)执行该静态方法都需要排队,锁粒度是“整个类”。

c.作用于同步代码块

锁对象:手动指定的“任意对象”(需要是引用类型,如this,Class对象,自定义Object实例),锁粒度可精准控制)(只锁临界区,不锁整个方法)。

B.底层原理

依赖JVM的”对象头“和”监视器“,需结合”Java对象结构“和”字节码指令“理解:

a.核心依赖:

Java每个对象在内存中分为三个部分:对象头,实例数据,对齐填充,对象头是synchronized实现锁的关键。

关键逻辑:synchronized的锁状态会直接修改对象头的Mark Down-当线程获取锁时,JVM会根据当前锁的状态(无锁->偏向->轻量->重量)更新Mark Down的标志位,实现锁的升级。

b.锁的核心

synchronized的”互斥“能力”依赖Monitor(监视器),可理解为一个“由JVM管理的,负责线程竞争的工具”,其本质是一个C++实现的对象(ObjectMonitor)。

d.Monitor 的工作流程(以同步代码块为例):

线程进入synchronized代码块时,会尝试获取 Monitor 的 Owner 权限:

  • 若 Monitor 无 Owner(初始状态),当前线程直接成为 Owner,锁状态从 “无锁” 升级(如偏向锁);
  • 若 Monitor 已有 Owner(锁被占用),当前线程进入 EntryList 阻塞,等待 Owner 释放锁;
  • 线程执行完同步代码块,或调用wait()方法时:
  • 若执行完代码:释放 Monitor(Owner 置空),唤醒 EntryList 中等待的线程,重新竞争锁;
  • 若调用wait():线程从 Owner 移至 WaitSet,释放锁;待其他线程调用notify(),再从 WaitSet 移回 EntryList,重新竞争锁。

C. 字节码层面的体现(加分项)

a.通过javac编译synchronized代码后,字节码中会出现两个关键指令,对应 “获取锁” 和 “释放锁”:

b.monitorenter:进入同步代码块时执行,尝试获取 Monitor 的 Owner 权限(若失败则阻塞);

c.monitorexit:退出同步代码块时执行,释放 Monitor 的 Owner 权限(唤醒等待线程)。
https://www.bilibili.com/video/BV1aG4recEQw/?spm_id_from=333.337.search-card.all.click&vd_source=3abe3667e67749032f72d6f512b2a967

https://www.bilibili.com/video/BV14C4y127W1/?spm_id_from=333.337.search-card.all.click

4.线程间同步方式有哪些?

A.互斥锁:保证同一时间只有一个线程访问共享资源。

B.自旋锁:一种非阻塞锁,线程会尝试循环获取锁,适用于持有锁时间极短的情况下。

C.读写锁:基于“读写分离”的优化锁,分为读锁(共享锁,多个线程可同时读)和写锁(排他锁,进一个线程可写),解决“读多写少”场景下的性能问题。

D.信号量:通过“许可证”控制同时访问资源的线程数量,可理解为“多线程的限流工具”。

E.条件变量:配合ReentranLock使用,实现“精细化的线程等待/唤醒”,可替代Object的wait()/notify(),支持多个等待队列(一个锁可对应多个Condition)。

https://www.bilibili.com/video/BV1oQ4y1C73G/?spm_id_from=333.337.search-card.all.click&vd_source=3abe3667e67749032f72d6f512b2a967

http://www.dtcms.com/a/524718.html

相关文章:

  • 力扣2576. 求出最多标记下标
  • 做网站需要什么配置服务器工业产品设计软件
  • 大型语言模型基础之 Prompt Engineering:打造稳定输出 JSON 格式的天气预报 Prompt
  • [cpprestsdk] JSON类--数据处理 (`json::value`, `json::object`, `json::array`)
  • 2014 个人网站备案soho需要建网站吗
  • BugKu Web渗透之 文件包含2
  • 为什么建设营销型网站wordpress弹穿登陆
  • 高职示范校建设网站建设工程标准合同范本
  • GBASE8s数据库-常用SQL命令
  • 第 02 天:虚拟化与 Linux 设置(重点:VirtualBox 实践)
  • 服务器实时流量监控工具推荐:确保服务器不被滥用
  • 深度学习------YOLOv5《第二篇》
  • Google Drive 超大文件下载到服务器
  • vscode中Java开发环境的配置
  • 【穿越Effective C++】条款5:了解C++默默编写并调用哪些函数——编译器自动生成的秘密
  • docker镜像从服务器Acopy到服务器B
  • 网站开发微信端小说网站如何建设
  • 网站建设黄页视频wordpress的标签设置主页
  • 学习java线程池ThreadPoolExecutor类使用介绍
  • 有关建设旅行网站的建设负责网站的建设规划
  • 学Java第三十七天-------常用API
  • 微信漫画网站模板wordpress 视频展示
  • 【NestJS】依赖注入:超越自动new的深层解析
  • 北京网站建设策划建设公司长春建站培训
  • Mac常用软件
  • 【Linux笔记】网络部分——基于Socket套接字实现最简单的HTTP协议服务器
  • Linux基础知识(三、Linux常见操作目录命令)
  • 类似凡科建站的平台手机网站仿站
  • 在网站文字上做笔记设计制作中国第一架飞机的人是
  • C++ 分治 快速选择算法 堆排序 TopK问题 力扣 215. 数组中的第K个最大元素 题解 每日一题