redis 的incr 高并发 原子性计数器

前言:6月底 公司录单的人比较多,由于先前的系统用的同步锁 ,我们是多服务实例,导致出现重复单号的问题,我想到的解决办法有两种 ,第一种是 Redis锁 第二种是自增key,下面实现的是用第二种方法 自增key 。

不选择Redis锁的原因:

  • 它获取锁的方式简单粗暴,获取不到锁直接不断尝试获取锁,比较消耗性能。
  • 另外来说的话,Redis 的设计定位决定了它的数据并不是强一致性的,在某些极端情况下,可能会出现问题。锁的模型不够健壮。
  • 即便使用 Redlock 算法来实现,在某些复杂场景下,也无法保证其实现 100% 没有问题,关于 Redlock 的讨论可以看 How to do distributed locking。
  • Redis 分布式锁,其实需要自己不断去尝试获取锁,比较消耗性能。

1.Redis自增key 的好处

  • 原子性(atomicity):一个事务是一个不可分割的最小工作单位,事务中包括的诸操作要么都做,要么都不做。
  • Redis所有单个命令的执行都是原子性的,这与它的单线程机制有关;
  • Redis命令的原子性使得我们不用考虑并发问题,可以方便的利用原子性自增操作
  • 简单解释就是你的服务即使是多机器多进程的,incr也能保证每次返回的结果不会出现相同的值.

2.逻辑梳理

  • Redis 获取Key自增次数 每次+1
  • 如果不存在key 默认值设置为30000
  • 截取数据 返回

3.代码实现:

    /**
     * 此方法只适用于************号和*****************号生成            
     * @param id 序列主键类型:BA/AJ等
     * @return
     */
    @Transactional(isolation = Isolation.SERIALIZABLE, rollbackFor = Exception.class)
    public synchronized String createNumberBAandAJ(String id) {
        //Redis 获取Key自增次数 每次+1
        String key="case:GenerateEnum:"+id;
        Long increment = redisTemplate.opsForValue().increment(key, 1);
        //如果不存在key  默认值设置为30000  方便区分与数据库字段不相同
        if(1 == increment ){
            increment = redisTemplate.opsForValue().increment(key, 30000);
        }

        //redisTemplate.boundValueOps("success").get(0, -1)
        //截取数据
        String s = "000000" + increment;
        s = s.substring(s.length() - 6);
        log.info("截取后的数据================>"+s);
        return s;
    }

4.总结:
在一些对高并发请求有限制的系统或者功能里,比如说秒杀活动,或者一些网站返回的当前用户过多,请稍后尝试。这些都是通过对同一时刻请求数量进行了限制,一般用作对后台系统的保护,防止系统因为过大的流量冲击而崩溃。对于系统崩溃带来的后果,显然还是拒绝一部分请求更能被维护者所接受。
而在各种限流中,除了系统自身设计的带锁机制的计数器外,利用Redis实现显然是一种既高效安全又便捷方便的方式。

5.redis 的incr原理

  • Redis Incr 命令将 key 中储存的数字值增一。
  • 如果 key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 INCR 操作。
  • 如果值包含错误的类型,或字符串类型的值不能表示为数字,那么返回一个错误。
  • 本操作的值限制在 64 位(bit)有符号数字表示之内。

计数器是 Redis 的原子性自增操作可实现的最直观的模式了,它的想法相当简单:每当某个操作发生时,向 Redis 发送一个 INCR 命令。

比如在一个 web 应用程序中,如果想知道用户在一年中每天的点击量,那么只要将用户 ID 以及相关的日期信息作为键,并在每次用户点击页面时,执行一次自增操作即可。

比如用户名是 peter ,点击时间是 2012 年 3 月 22 日,那么执行命令 INCR peter::2012.3.22 。

可以用以下几种方式扩展这个简单的模式:

  1. 可以通过组合使用 INCR 和 EXPIRE ,来达到只在规定的生存时间内进行计数(counting)的目的。
  2. 客户端可以通过使用 GETSET 命令原子性地获取计数器的当前值并将计数器清零,更多信息请参考 GETSET 命令。
  3. 使用其他自增/自减操作,比如 DECR 和 INCRBY ,用户可以通过执行不同的操作增加或减少计数器的值,比如在游戏中的记分器就可能用到这些命令。

学习如逆水行舟不进则退,记录问题,增加经历。

  • 8
    点赞
  • 45
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值