一些面试拷打问题,主要来自字节一面
Redis扣减余票成功后宕机了,新的主节点数据存在一部分的不同步,可能导致MySQL的余票数量和Redis不一样,如何解决这个问题
场景:Redis余票60张,A抢票成功剩59张,但此时主节点宕机,从节点显示仍有60张票,会发生超卖问题。
开启AOF=always,每次修改立马写入磁盘,但会非常影响性能;
启用Redis min-replicas-to-write限制,确保至少有1个从节点和主节点保持较高的同步
Redis做库存的预扣减,那什么时候去真正扣减库存?
我们使用 Redis 做库存预扣减,用户点击购买时先通过 Lua 脚本进行原子扣减。如果成功,就将订单信息发送到 Kafka,由后台服务异步消费,执行订单创建和数据库扣减库存操作。如果 Kafka 消费失败或数据库扣减失败,就将该消息投递到
rollback-topic
,由专门的消费者监听并将库存补偿回 Redis。异步生成订单,用户点击下单后就希望跳转支付页面但是Kafka还没有成功创建订单怎么办?
为了兼容 Kafka 异步下单的延迟,同时提升用户体验,我们设计了“订单状态中间态”的机制。用户点击下单时,系统立即生成订单号,并在 Redis 中标记为
PENDING
状态,返回前端用于长轮询,挂起请求3s后去查询订单状态。Kafka 后台异步创建订单成功后将状态更新为CREATED
,前端轮询检测到后跳转支付页面。若为FAIL
则显示错误重试,仍然为PENDING
则由前端退避后重试1~2次。Kafka有丢消息的风险,让它去生成订单,它挂了消息丢失怎么办?
- 通过配置ack确保发送成功,配置最小同步副本数确保分区容错
- Kafka宕机重启后,根据Redis中
PENDING
状态的订单去补偿
RocketMQ延迟订单取消,要是客户在临近取消时间到期时去支付,结果支付成功订单却取消了怎么办?
在执行取消订单的逻辑之前去主动查询数据库的状态,如果是已取消则可以关闭订单,再去主动检查支付渠道的支付状态看看到底有没有支付成功,和数据库的支付状态进行对比并以支付宝的支付状态为准补偿数据库。
布隆过滤器应该是单实例还是多实例部署,如何去做布隆过滤器的更新?
为了保证一致性与准确性,我采用 布隆过滤器单实例部署,结合 Redis 分布式存储 实现共享过滤器。启动多个实例也会共用Redis上的布隆过滤器。
对于两个不同业务场景,我采用不同更新策略:- 注册手机号布隆过滤器:注册成功后立即异步加入布隆过滤器,同时每日定时从数据库全量同步,确保漏加数据被补全。
- 节目 ID 布隆过滤器:由于节目 ID 属于静态数据,采用 定时任务+全量重建 的方式,每隔一定时间拉取节目库中的 ID 重建布隆过滤器,确保恶意请求被拦截。
多实例去获取分布式锁,确保只有一个实例去进行更新操作
对于大数据量的初始化,可以多个异步线程分别加载一部分数据。或者是采用分片集群部署多个布隆过滤器,每一个只负责过滤自己这一部分的数据。
基于内存手写一个限流器,传参为唯一标识和maxQPS,统计过去1s内是否触发限流(类似滑动窗口限流)
Redis和MySQL的一致性如何保证?
消息队列的选型
Kafka:超高吞吐量(十几万,基于sendfile零拷贝 + 页缓存写入 + 追加写入commit log + 分区并行实现)
RocketMQ:丰富的功能(事务消息 + 原生延迟队列) + 高吞吐量(十万)
RabbitMQ:微秒级的执行时间,比其他的毫秒级要快,但其实用户体验上微秒和毫秒没什么区别
tomcat线程池有没有了解过
参数配置:
最小空闲线程数(类似于核心线程数)
最大线程数
线程最大空闲时间
是否预启动最小线程数(java中是由任务提交后才进行创建)
任务队列最大长度
和java线程池的区别
Tomcat线程池用于处理网络请求,尽最大努力满足所有连接。在达到核心线程数后直接创建临时线程处理,达到最大线程数才会将任务放到阻塞队列,阻塞队列满后才执行拒绝策略;
Java线程池用于异步处理任务和并发编排等,尽量将任务交给核心线程去执行。在达到核心线程数后会将任务放到阻塞队列,阻塞队列满后创建临时线程处理,达到最大线程数后执行拒绝策略。
HashMap put源码有没有看过?
项目中线程池的使用
异步初始化布隆过滤器;加载节目详情
*N*cpu 表示 核心数。
如果是CPU密集型任务,就需要尽量压榨CPU,参考值可以设为 *N*cpu+1
如果是IO密集型任务,参考值可以设置为 2 * *N*cpu
系统还可以如何去优化以应对大流量
- 进一步削峰处理:引入排队系统
当前系统是 Redis + Lua 原子扣库存,但面对极高并发时(比如 100 万 QPS 秒杀),Redis 本身也可能被打爆。
🎯进阶优化:
- 使用令牌桶机制 + 排队模型,只让前 N 个请求能进入 Redis;其余进入排队等待区,避免 Redis 被瞬间压垮;
- 在网关层提前做限流,如对同一IP的访问频率做限制,提前拦截恶意请求,sentinel只做兜底方案
- 引入skywalking做分布式链路id,快速定位服务,监控系统
- 灰度发布?