一、MQ
问题1:消费者漏消息
说明:一般都是开发不当导致,只要利用好,是可以避免丢失消息的
漏消息场景:
try catch将异常吞掉
redis锁使用不当
public class OrderCancelListener{
@Override
public void onReceived(Message message) {
String lockKey = "order.cancel.mq."; // 消息并行消费放重
try {
Boolean l = redisUtil.setNX(lockKey,"1",60*60L, TimeUnit.SECONDS);
if(l == null){
throw new RuntimeException("");
}
if(!l){ //防止消息重复
return;
}
boolean result = orderCancelManager.cancelOrder(cancelOrderMq);
//业务逻辑
} catch (Exception e){
log.error("OrderCancelListener exception",e);
throw new RuntimeException("",e);
} finally {
redisUtil.del(lockKey);
}
}
}
存在的问题:
1.锁删除可能删除别人的锁,造成消息重复消费
2.机器宕机,锁未删除,造成消息丢失
问题2:消费幂等
说明:首先,MQ有重试机制,这个机制MQ可以关闭,但我们一般不要这么干,否则会丢消息;其次,MQ重试的概率还是很高的,并发量大了肯定会遇到,所以幂等问题一定要考虑。
消息重试时机:
抛出异常
执行超时
网络问题
MQ内部问题
如果保障幂等:
通过数据库事务表来实现,通过本身业务表的唯一约束控制,或者加一张消息表,将消息表和业务表放入同一个事务(需要注意必须在同一个库)
insert的数据加唯一索引。
@Transactional
public boolean save(){
try {
userMapper.save();
addrMapper.save();
}catch (DuplicateKeyException dke){
log.info("唯一约束冲突");
}
return true;
}
update加where条件保障只能更新一次。
@Transactional
public boolean update(){
//update xxx set status = 'FAIL' where id = 1 and status = 'PROCESS';
int userUpdateCount = userMapper.update();
//update xxx amount = amount + 1 where id = 123;
if(userUpdateCount > 0){
addrMapper.update();
}
return true;
}
如果不只是数据库操作比如还有ES的操作,redis的操作,rpc的调用等,那么就需要这里的每一步都保障幂等。
ES:docId保障幂等redis:lua脚本保障原子性 RPC:提供方保障
通用解决办法(基于消息幂等表的非事务方案)
问题3:消费顺序
说明:首先,单个的topic内部由于是多分片的,所以消费的顺序,并不能保障跟发送的顺序一致;当然,JMQ也提供顺序队列,我们也是可以使用的,这种的并发会比较低;其次,多个topic之间有依赖关系的,也需要考虑顺序问题。
问题4:生产者漏发消息
如何保障生产者消息不漏发
MQ给的意思是:send方法不报错,就会100%发送成功;报错了,需要客户端自己去重试
rabbitmq本地事务
二、REDIS
问题1:数据丢失问题
数据丢失场景:
异步复制:在 master 写成功,但 slave 同步完成之前,master 宕机了,slave 变为 master,数据丢失。
网络分区:发生脑裂,分区后一个 master 继续接收写请求,分区恢复后这个 master 可能会变为 slave,那么之前写入的数据就丢了。
问题2:数据库和缓存一致性问题
说明:缓存如果需要查询实时性高,精准数据的场景,比如账户余额的一些检查等,应该实时查库。网上有很多解决缓存一致性问题的方案,延时双删,接binlack等。
三、Mysql
说明:
1. 创建库时选择库是否分片,及分片数。
2. 分片的库在创建表的时候,需要指定在每个分片上都建立表还是只在一个分片上建立表。
3. 如果分片的库事务要生效,就要保障所有的sql都路由到同一个分片才行,所以在建表的时候我们要考虑是建分片表还是不分片表。
四、方案设计之善用补偿
方案设计中多考虑以下几点:
1. RPC异常情况考虑
2. 服务器突然停机情况考虑
3. 数据库、redis等异常情况考虑