幂等性

幂等性是数学概念,f(x)=f(f(x))。计算机领域,意为对同一个系统,使用同样的条件,一次请求和重复的多次请求对系统资源的影响是一致的.

该问题产生的一些场景

多次调用带来的是结果不一致和并发安全问题

  • 浏览器多次调用.
  • 业务调用超时或者没有接收到,重试
  • mq重发
  • 多次入库

解决方案

首先需要明确的是在一般场景中可以简单的解决或者忽略,在强一致性环境中必须校验,比如订单,支付

  • 请求带token

    页面每次刷新后端给前端提供一个token,每次点击只处理一个token.防止多次点击.这种情况前端优先处理

  • 事务

    简单粗暴

  • 数据库乐观锁

    查删没有影响,增改可能重复

    save:对于业务中有唯一标志的,数据可以加唯一索引.没有唯一标志的,比如添了1条就是多1条的,在业务层就要判断.
    update:在数据库加一个version

    update t set a=a+1,version=version+1 where version=1;//成功了只有1次执行机会
    
  • 去重表或redis去重

    去重表:业务标志存入表去重.失败了删除.定期删除.优点是可以直接进事务回滚,缺点是每个业务都需要表,对数据库压力会随量级增加

    insert ignore into log (user_id) value (#{userId})
    

    redis:存zset.分数为时间.失败了删除,另外起一个定时器定期删除.相对去重表,性能更好,操作更简单

    之前做了一个数据增量统计,2种方式都试了下:

       @Override
       public boolean haveExist(BusinessEnum businessEnum, String value) {
            Boolean flag = stringRedisTemplate.opsForZSet().add(PREFIX_REPEAT_KEY + businessEnum.code,value,System.currentTimeMillis());
            return !(flag == null ? false : flag);
        }
    
        @Override
        public Long remove(BusinessEnum businessEnum, String value) {
            return stringRedisTemplate.opsForZSet().remove(PREFIX_REPEAT_KEY + businessEnum.code, value);
        }
    
        @Override
        public void clearCache() {
            BusinessEnum[] businessEnums = BusinessEnum.values();
            long now = System.currentTimeMillis();
            for (BusinessEnum biz : businessEnums) {
                stringRedisTemplate.opsForZSet().removeRangeByScore(PREFIX_REPEAT_KEY + biz.code, 0, now - biz.timeout);
            }
        }	
    
  • 业务状态

    根据状态操作,如订单

    if(status==paid){
        dosth..
    }
    
  • 单机锁与分布式锁

  • mq

    自带ack,保证消息百分百到达,性能优秀

谈谈分布式

分布式复杂性的原因有很多,很重要一点是网络的不可靠性.服务之间调用可能为成功,失败,超时. 要保证百分百成功,事务和重试是必不可少的.处理起来也要结合业务多种处理方式搭配起来用.比较推崇的一种方式是走mq,服务异步化和保证可靠性.

posted @ 2020-06-10 18:45  noodlesjh  阅读(108)  评论(0)    收藏  举报