一、MQ

问题1:消费者漏消息

说明:一般都是开发不当导致,只要利用好,是可以避免丢失消息的

漏消息场景:

  1. try catch将异常吞掉

  2. 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重试的概率还是很高的,并发量大了肯定会遇到,所以幂等问题一定要考虑。

消息重试时机:

  1. 抛出异常

  2. 执行超时

  3. 网络问题

  4. MQ内部问题

如果保障幂等:

  1. 通过数据库事务表来实现,通过本身业务表的唯一约束控制,或者加一张消息表,将消息表和业务表放入同一个事务(需要注意必须在同一个库)

  2. insert的数据加唯一索引。

@Transactional
   public boolean save(){
        try {
            userMapper.save();
            addrMapper.save();
        }catch (DuplicateKeyException dke){
            log.info("唯一约束冲突");
        }
        return true;
    }
  1. 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;
   }
  1. 如果不只是数据库操作比如还有ES的操作,redis的操作,rpc的调用等,那么就需要这里的每一步都保障幂等。

  2. ES:docId保障幂等redis:lua脚本保障原子性 RPC:提供方保障

  3. 通用解决办法(基于消息幂等表的非事务方案)

问题3:消费顺序

说明:首先,单个的topic内部由于是多分片的,所以消费的顺序,并不能保障跟发送的顺序一致;当然,JMQ也提供顺序队列,我们也是可以使用的,这种的并发会比较低;其次,多个topic之间有依赖关系的,也需要考虑顺序问题。

问题4:生产者漏发消息

如何保障生产者消息不漏发

  1. MQ给的意思是:send方法不报错,就会100%发送成功;报错了,需要客户端自己去重试

  2. rabbitmq本地事务

二、REDIS

问题1:数据丢失问题

数据丢失场景:

  1. 异步复制:在 master 写成功,但 slave 同步完成之前,master 宕机了,slave 变为 master,数据丢失。

  2. 网络分区:发生脑裂,分区后一个 master 继续接收写请求,分区恢复后这个 master 可能会变为 slave,那么之前写入的数据就丢了。

问题2:数据库和缓存一致性问题

说明:缓存如果需要查询实时性高,精准数据的场景,比如账户余额的一些检查等,应该实时查库。网上有很多解决缓存一致性问题的方案,延时双删,接binlack等。

三、Mysql

说明:

1. 创建库时选择库是否分片,及分片数。

2. 分片的库在创建表的时候,需要指定在每个分片上都建立表还是只在一个分片上建立表。

3. 如果分片的库事务要生效,就要保障所有的sql都路由到同一个分片才行,所以在建表的时候我们要考虑是建分片表还是不分片表。

四、方案设计之善用补偿

方案设计中多考虑以下几点:

1. RPC异常情况考虑

2. 服务器突然停机情况考虑

3. 数据库、redis等异常情况考虑