面试题答案
一键面试1. Redis 事务冲突优化策略
- 乐观锁机制:
- 技术点:在执行 Redis 事务前,先获取数据的版本号或时间戳。在事务执行时,将获取的版本号与当前数据版本号比较。若版本号一致,执行事务;否则,回滚并重新执行。在 Redis 中可利用
WATCH
命令实现,WATCH
命令可以监控一个或多个键,当事务执行时,如果被监控的键被其他客户端修改,事务将被打断。 - 设计思路:在抢购场景下,客户端在获取商品库存等数据时,同时获取其版本标识。准备执行购买操作时,以
WATCH
监控该数据。若在MULTI
和EXEC
之间数据未被修改,事务正常执行;若数据被修改,EXEC
返回nil
,客户端捕获此情况后重试事务。
- 技术点:在执行 Redis 事务前,先获取数据的版本号或时间戳。在事务执行时,将获取的版本号与当前数据版本号比较。若版本号一致,执行事务;否则,回滚并重新执行。在 Redis 中可利用
- 排队处理:
- 技术点:使用 Redis 的列表(List)数据结构,将抢购请求依次存入列表。然后通过单线程消费者从列表中按顺序取出请求,依次执行 Redis 事务。这样可以避免多个事务同时竞争资源导致冲突。
- 设计思路:在高并发抢购开始时,客户端将抢购请求发送到 Redis 列表。服务器端启动一个单线程消费者,循环从列表中读取请求并执行相关 Redis 事务,确保同一时间只有一个事务在执行,避免事务冲突。
2. 消息队列积压处理策略
- 增加消费能力:
- 技术点:增加消息队列的消费者数量,提高消息处理速度。可以通过水平扩展消费者实例,如在分布式系统中,部署多个消费者节点。同时,优化消费者代码,减少单个消息处理时间,例如采用异步 I/O、多线程等技术提高处理效率。
- 设计思路:根据预估的高并发量,合理规划消息队列的分区数量。在消息队列(如 Kafka)中,每个分区可以被一个消费者组中的一个消费者消费。通过增加消费者组中的消费者实例数量,使每个消费者处理不同分区的消息,从而提高整体消费能力。
- 消息限流:
- 技术点:在消息生产者端进行限流,防止大量消息瞬间涌入消息队列。可以使用令牌桶算法或漏桶算法实现限流。例如,使用 Guava 库中的
RateLimiter
实现令牌桶算法限流,设定每秒允许发送的消息数量,生产者按照此速率向消息队列发送消息。 - 设计思路:在抢购场景开始前,根据消息队列的处理能力和系统资源,计算出合理的限流速率。在生产者端,对每个抢购请求进行限流判断,只有获取到令牌的请求才能将消息发送到消息队列,避免消息队列因瞬间接收过多消息而积压。
- 技术点:在消息生产者端进行限流,防止大量消息瞬间涌入消息队列。可以使用令牌桶算法或漏桶算法实现限流。例如,使用 Guava 库中的
3. 事务补偿与消息队列协同稳定性设计
- 幂等性设计:
- 技术点:无论是 Redis 事务执行后的操作,还是消息队列消费后的操作,都要保证幂等性。例如在订单确认和发货通知环节,数据库层面通过唯一索引防止重复插入订单数据;消息队列消费者在处理消息时,先检查消息是否已处理过(可通过消息 ID 或业务唯一标识),若已处理则直接返回,不重复处理。
- 设计思路:在订单确认业务逻辑中,对插入订单操作使用数据库唯一索引约束,当重复插入相同订单数据时,数据库会抛出异常,应用层捕获异常并处理,确保订单不会重复创建。在消息队列消费者端,维护一个已处理消息的缓存(如 Redis 缓存),每次处理消息前先查询缓存,若消息已存在则跳过处理。
- 消息确认与重试机制:
- 技术点:消息队列采用可靠的消息传递机制,如 Kafka 的
acks
机制,生产者发送消息后等待消息队列的确认。若消息发送失败,进行重试。在消费者端,当消费消息失败时,根据失败原因决定是否重试。例如,因网络瞬时故障导致失败,可进行重试;若因业务逻辑错误导致失败,记录错误日志并进行人工干预。 - 设计思路:在生产者端,设置
acks = all
,确保消息被所有副本成功接收后才认为发送成功。若发送失败,按照一定的重试策略(如指数退避重试)进行重试。在消费者端,捕获消费消息的异常,对于可重试的异常(如网络异常),在一定次数内重试消费;对于不可重试的异常(如业务数据格式错误),将消息放入死信队列,并记录详细错误信息,供人工排查处理。
- 技术点:消息队列采用可靠的消息传递机制,如 Kafka 的