当前位置: 首页 > article >正文

redis实现计数器功能

1. redisTemplateService.incrBy(key, 1L);

功能:
  • incrBy 是 Redis 中的一个命令,用于将存储在指定键的数值增加指定的增量值。如果键不存在,则在执行操作之前将其初始化为0。
  • 在这个代码片段中,incrBy 方法被调用,传入了两个参数:
    • key: 这是要操作的键。
    • 1L: 这是要增加的值,这里是一个长整型(long)值,值为1。
用途:
  • 这个操作通常用于计数器场景。例如,统计某个事件的发生次数、用户访问次数等。每次调用这个方法,都会将对应键的值增加1。

2. redisTemplateService.expire(key, 24, TimeUnit.HOURS);

功能:
  • expire 是 Redis 中的一个命令,用于设置键的过期时间。当键的过期时间到达时,键会自动被删除。
  • 在这个代码片段中,expire 方法被调用,传入了三个参数:
    • key: 这是要设置过期时间的键。
    • 24: 这是过期时间的长度,这里是24。
    • TimeUnit.HOURS: 这是时间单位,表示小时。
用途:
  • 这个操作通常用于控制数据的生命周期。例如,可以设置一个缓存项在24小时后自动失效,从而避免数据长期占用内存。

综合解释:

redisTemplateService.incrBy(key, 1L);
redisTemplateService.expire(key, 24, TimeUnit.HOURS);

这段代码首先将指定键 key 对应的值增加1,然后将该键设置为24小时后过期。这通常用于需要对某个事件进行计数并希望在一定时间内自动清除计数的场景。例如,可以用在网站访问计数器中,每访问一次页面就将计数器加1,并且希望计数器在24小时后自动重置。

示例应用场景:

假设我们有一个网站,我们希望记录每天的访问量,并在每天结束时重置计数。我们可以使用上述代码来实现这一需求:

String key = "daily_visits";
redisTemplateService.incrBy(key, 1L); // 增加访问计数
redisTemplateService.expire(key, 24, TimeUnit.HOURS); // 设置24小时后过期

这样,每天的访问量都会被记录到 daily_visits 键中,并且在24小时后自动重置为0。

seqNo同一天内会获取到相同值的场景

seqNo 是通过 redisTemplateService.incrBy(key, 1L) 获取的。这个操作会将指定键 key 对应的值增加1,并返回增加后的值。

出现相同值可能的情况:

  1. 并发请求

    • 如果多个线程或进程几乎同时执行这段代码,它们可能会读取到相同的初始值,然后各自增加1,导致最终 seqNo 相同。例如,两个线程都读取到初始值为100,然后各自增加1,结果都是101。
  2. Redis 集群环境

    • 在 Redis 集群环境中,如果多个节点同时处理对同一个键的 incrBy 操作,并且这些操作没有同步机制,也可能导致相同的 seqNo
  3. 网络延迟和重试机制

    • 在某些情况下,由于网络延迟或其他原因,客户端可能会重试操作。如果重试发生在第一次操作完成之前,那么第二次操作也会基于相同的初始值进行增加,从而导致相同的 seqNo
  4. 应用重启或故障恢复

    • 如果应用程序在一天内多次重启或发生故障恢复,而没有持久化计数器的状态,那么每次重启后计数器都会从0开始,导致在短时间内出现相同的 seqNo

解决方案:

为了避免上述情况导致的 seqNo 重复,可以考虑以下几种方法:

  1. 使用分布式锁

    • 在执行 incrBy 操作时,使用分布式锁来确保同一时间只有一个线程能够修改该键的值。这样可以防止并发问题。
  2. 持久化计数器状态

    • 将计数器的当前值持久化到数据库或其他持久化存储中,以便在应用重启后能够恢复计数器的状态。
  3. 使用唯一标识符

    • 结合业务逻辑,使用其他唯一标识符(如用户ID、订单ID等)来生成唯一的序列号,而不是依赖简单的自增操作。
  4. Redis Lua脚本

    • 使用 Redis 的 Lua 脚本功能,将 incrByexpire 操作封装在一个原子操作中,确保这两个操作要么全部成功,要么全部失败,从而避免并发问题。

示例如下:

String luaScript = "local currentVal = redis.call('incrby', KEYS[1], ARGV[1]) " +
                   "redis.call('expire', KEYS[1], ARGV[2]) " +
                   "return currentVal";
List<String> keys = Arrays.asList(key);
Long seqNo = redisTemplateService.execute(new DefaultRedisScript<>(luaScript, Long.class), keys, 1L, 24 * 60 * 60);

通过这种方式,可以确保 incrByexpire 操作是原子性的,避免了并发问题。


http://www.kler.cn/a/404539.html

相关文章:

  • 实战 | C#中使用YoloV8和OpenCvSharp实现目标检测 (步骤 + 源码)
  • 2025计算机毕设选题精选推荐【小程序方向】
  • Spring Cloud Stream实现数据流处理
  • Leetcode 第 143 场双周赛题解
  • 「Mac玩转仓颉内测版24」基础篇4 - 浮点类型详解
  • 基于SpringBoot的“网上书城管理系统”的设计与实现(源码+数据库+文档+PPT)
  • 如何取消分词搜索
  • GPT1.0 和 GPT2.0 的联系与区别
  • RabbitMQ实现异步下单与退单
  • 从0开始学习机器学习--Day32--推荐系统作业
  • 统计班级中的说谎者(字节青训)
  • LLM2CLIP:使用大语言模型提升CLIP的文本处理,提高长文本理解和跨语言能力
  • 算法学习笔记(一):滑动窗口和双指针
  • RT_Thread内核源码分析(三)——线程
  • 分布式专题-Redis核心数据结构精讲
  • 《智能指针:明晰资源所有权的 C++利器》
  • 最新Kali安装详细版教程(附安装包,傻瓜式安装教程)
  • String、StringBuilder 和 StringBuffer 的区别
  • shell 接收长参数
  • ROSSERIAL与Arduino IDE交叉开发(UBUNTU环境,包含ESP32、arduino nano)
  • 深入JMeter核心引擎:揭秘JmeterEngine、StandardJmeterEngine、ClientJmeterEngine与Remote的奥秘
  • 基于Matlab的变压器仿真模型的建模方法(3):单相双绕组变压器的拉氏变换象函数模型及其仿真模型
  • DockerFile与容器构建技术
  • Redis的String类型和Java中的String类在底层数据结构上有一些异同点
  • 大数据面试题每日练习--Hadoop是什么?它由哪些核心组件组成?
  • reactflow 中 useNodesState 模块作用