Redis字符串数据类型之INCR命令,通常用于统计网站访问量,文章访问量,实现分布式锁

前言

Redis的INCR命令用于将键的值增加1。如果键不存在,则会先将键的值设置为0,然后再执行INCR操作。INCR命令的作用是对计数器进行自增操作,可以用于实现多种场景,比如统计网站访问量、文章访问量、分布式锁等。

一、Redis字符串数据类型之INCR命令

1.INCR 命令介绍

(1)用法:INCR key
(2)作用:将 key 中储存的数字值增一。
(3)返回值:执行 INCR 命令之后 key 的值。
(4)示例

redis> SET PageViewNum 20
OKredis> INCR PageViewNum
(integer) 21redis> GET PageViewNum # 数字值在 Redis 中以字符串的形式保存
"21"

2.INCR 命令在Java中的实战运用

private static final long BEGIN_TIMESTAMP = 1701360000; // 开始时间戳:1701360000 -> 2023-12-01 12:00:00private static final int COUNT_BITS = 32;@Overridepublic <T> T redisIncrTest() {HashMap<String, Object> responseObj = new HashMap<>();responseObj.put("code", 200);responseObj.put("success", true);LocalDateTime now = LocalDateTime.now();long currentTimestamp = now.toEpochSecond(ZoneOffset.UTC); // 当前时间戳:1702882165System.out.println("redisIncrTest :: currentTimestamp -> " + currentTimestamp);long dValue = currentTimestamp - BEGIN_TIMESTAMP; // 增长的差值:1551885System.out.println("redisIncrTest :: dValue -> " + dValue);String date = now.format(DateTimeFormatter.ofPattern("yyyyMMdd")); // 年月日:20231218System.out.println("redisIncrTest :: date -> " + date);String REDIS_ICR_KEY = "REDIS-INCR" + ":" + date; // REDIS-INCR:20231218long count = stringRedisTemplate.opsForValue().increment(REDIS_ICR_KEY); // INCR REDIS-INCR:20231218System.out.println("redisIncrTest :: count -> " + count); // 1String countStr = stringRedisTemplate.opsForValue().get(REDIS_ICR_KEY);System.out.println("redisIncrTest :: countStr -> " + countStr); // 1// 说明:// (1)|:或,当两个位都为0时,结果才为0,两边都会计算// (2)<<:左移,各二进位左移若干位,高位丢弃,低位补零// (3)左移一位相当于乘以2,左移32位相当于将某个数乘以2的32次方long rs = dValue << COUNT_BITS | count;System.out.println(rs);System.out.println(dValue << 32);responseObj.put("data", rs);return (T) responseObj;}

二、基于INCR命令实现的分布式锁思路

INCR命令还可以用于实现分布式锁,利用Redis的单线程特性,将INCR命令作为加锁的操作,同时利用Redis的原子性保证加锁的可靠性。

1.实现步骤

(1)使用`stringRedisTemplate.opsForValue().increment(key)`方法对指定的key进行自增操作,如果key不存在,则会自动创建并将其值设置为1。
(2)判断自增后的返回值是否为1,如果为1,则说明当前线程获得了锁,否则说明锁已经被其他线程占用,需要等待。
(3)在使用完锁后,使用`stringRedisTemplate.delete(key)`方法将锁释放,即将key从Redis中删除。

2.示例代码

private static final String REDIS_DISTRIBUTED_LOCK = "Redis-Distributed-Lock"; // 分布式锁名/*** 获取锁*/
private Long acquireLock(String lockName) {Long rs = stringRedisTemplate.opsForValue().increment(lockName);System.out.println("acquireLock :: rs -> " + rs);return rs;
}/*** 释放锁*/
private void releaseLock(String lockName) {Boolean flag = stringRedisTemplate.delete(lockName);System.out.println("releaseLock :: flag -> " + flag);
}/*** 使用锁*/
private void useLock(String lockName) {Long rs = acquireLock(lockName);if (rs == 1) {try {System.out.println("嘿嘿,拿到锁啦!");Thread.sleep(1000);} catch (Exception e) {throw new RuntimeException(e);} finally {releaseLock(lockName);}}
}/*** 测试锁*/
@Override
public <T> T distributedLockTest() {useLock(REDIS_DISTRIBUTED_LOCK);return (T) "OK";
}

3.心得

感觉使用INCR命令实现的互斥锁没有SETNX命令实现的优雅,以下为基于SETNX命令实现的分布式锁示例。

private static final String REDIS_DISTRIBUTED_LOCK = "Redis-Distributed-Lock"; // 分布式锁名/*** 获取锁*/
private boolean tryLock(String lockKey) {// SETNX Redis-Distributed-Lock 1 # 在指定的key不存在时,为key设置指定的值,若设置成功则返回1,若设置失败则返回0// EXPIRE Redis-Distributed-Lock 10Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(lockKey, "1", 10, TimeUnit.SECONDS);// 注意不能直接返回,直接返回存在拆箱操作,可能会有空指针return BooleanUtil.isTrue(flag);
}/*** 释放锁*/
private void unLock(String lockKey) {stringRedisTemplate.delete(lockKey);
}/*** 测试锁*/
@Override
public <T> T distributedLockTest() {boolean isLock = tryLock(REDIS_DISTRIBUTED_LOCK);if (isLock) {try {System.out.println("嘿嘿,拿到锁啦!");} catch (Exception e) {throw new RuntimeException(e);} finally {unLock(REDIS_DISTRIBUTED_LOCK);}}return (T) "OK";
}

这里有个问题。假如线程一获得锁之后,因为某种情况进入了阻塞状态,那么当锁过期以后,线程二就拿到锁,执行到一半,线程一开始唤醒执行,而此时线程二持有的锁就会被线程一释放掉,其它线程也如此反复,那咋整?

解决办法就是在获取锁时存入线程标志,在释放锁时先判断锁中的线程标志,判断是否与当前线程标志一致,若一致则释放锁,否则不释放。

/*** 获取锁*/
private boolean tryLock(String lockKey) {// SETNX Redis-Distributed-Lock 1 # 在指定的key不存在时,为key设置指定的值,若设置成功则返回1,若设置失败则返回0// EXPIRE Redis-Distributed-Lock 10String val = "LOCK-" + Thread.currentThread().getName();Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(lockKey, val, 10, TimeUnit.SECONDS);// 注意不能直接返回,直接返回存在拆箱操作,可能会有空指针return BooleanUtil.isTrue(flag);
}/*** 释放锁*/
private void unLock(String lockKey) {String threadLockName = "LOCK-" + Thread.currentThread().getName();String val = stringRedisTemplate.opsForValue().get(lockKey);// 只有同一个线程才能主动释放锁if (threadLockName.equals(val)) {stringRedisTemplate.delete(lockKey);}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/234119.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

JS中的selection事件与range

前言 本文简单总结下文本输入中的 Selection 与 Range 事件。 测试地址见: 在线效果预览 ::selection && Selection 自定义选取颜色 项目中一般有主题色的需求&#xff0c;这时候可以通过 css 中的::selection伪类可以自定义选中背景颜色 ::selection {background: yel…

【论文笔记】Distilling the Knowledge in a Neural Network

Abstract 几乎任何机器学习算法性能提升的一个非常简单的方法是在相同数据上训练多个不同的模型&#xff0c;然后对它们的预测结果进行平均。 不幸的是&#xff0c;使用整个模型集合进行预测繁琐&#xff0c;可能会因为计算成本过高而难以部署给大量用户&#xff0c;尤其是如果…

MyBatis的原始DAO开发!!!

引用&#xff1a;MyBatis的删除、修改、插入操作&#xff01;&#xff01;&#xff01;-CSDN博客的准备工作&#xff01;&#xff01;&#xff01;&#xff08;准备工作都一样只不过文件名称有所不同&#xff09; 1.利用原始DAO开发&#xff0c;查询所有的信息。 UserDao&#…

使用 Rsync 在 Linux 上实现高效文件同步和备份

前言 Rsync&#xff08;Remote Sync&#xff09;是一款在 Linux 系统中被广泛应用于文件备份和同步的工具&#xff0c;它提供了快速、可靠且灵活的文件同步方案。 Rsync 的优势 增量传输&#xff1a;Rsync 只传输文件的变更部分&#xff0c;大幅减少了数据传输的时间和网络带…

【数据库模拟题目集】选择题

数据库应用程序的编写是基于数据库三级模式中的&#xff08;外模式&#xff09; 对创建数据库模式一类的数据库对象的授权可由CREATE USER时实现。新创建的数据库用户有三种权限&#xff0c;CONNECT、RESOURCE和DBA。拥有RESOURCE权限的用户&#xff08;不能创建模式 &#xf…

H5嵌入小程序(web-view)、BMap百度地图组件、导航功能、返回web-view页

H5嵌入小程序&#xff08;web-view&#xff09;及导航功实现 1、H5页面中的地图展示2、H5传递数据到微信小程序&#xff0c;并调用wx.openLocation微信内置地图及导航功能的实现 1、H5页面中的地图展示 创建地图容器&#xff0c;添加导航按钮 <template><div><…

关于通信基站综合防雷方案介绍

为了获取更好的通信效果&#xff0c;通信基站在选址时通常地势要高于周围环境&#xff0c;气候条件恶劣&#xff0c;夏季通讯及机房设备及发射铁塔遭受雷击灾害的风险较高&#xff0c;而现代的电信设备对雷电又较为敏感&#xff0c;使得雷害问题日益凸显出来&#xff0c;如果防…

裂解汽油行业分析:到2027 年将达到 202.4 亿美元

裂解汽油&#xff0c;又称pygas&#xff0c;是石化工业中用石脑油或瓦斯油生产乙烯和丙烯的副产品。热解气是一种复杂的碳氢化合物混合物&#xff0c;主要由苯、甲苯和二甲苯等芳烃组成。热解汽油是生产苯乙烯、苯酚和合成橡胶等化学品的宝贵原料。 全球市场&#xff1a; 预计在…

延迟消息队列的几种实现方案,哪种更适合业务,要看具体情况分析

延迟消息队列的几种实现方案&#xff0c;延迟消息怎么实现&#xff0c;很多人可能一想到的是rabbitmq的死信队列来实现&#xff0c;但是一旦引入mq的话&#xff0c;就依赖这个中间件&#xff0c;另外维护成本&#xff0c;开发成本都很大&#xff0c;那有么有简单点的实现方式呢…

Linux安全之SELinux理解

安全增强式 Linux&#xff0c;即SELinux(Security-Enhanced Linux)是一个 Linux 内核的安全模块&#xff0c;其提供了访问控制安全策略机制&#xff0c;包括了强制访问控制(Mandatory Access Control&#xff0c;MAC)。SELinux 是一组内核修改和用户空间工具&#xff0c;已经被…

9ACL访问控制列表

为什么要有访问控制&#xff08;Access Control List&#xff09;&#xff1f; 因为我可能在局域网中提供了一些服务&#xff0c;我只希望合法的用户可以访问&#xff0c;其他非授权用户不能访问。 原理比较简单&#xff0c;通过对数据包里的信息做过滤&#xff0c;实现访问控…

houdini 神经网络

实现个神经网络的3D可视化&#xff0c;美爆了&#xff01;-腾讯云开发者社区-腾讯云 https://vimeo.com/stefsietz GitHub - julrog/nn_vis: A project for processing neural networks and rendering to gain insights on the architecture and parameters of a model throu…

unity2d 关闭全局重力

UNITY2D项目默认存在Y轴方向重力&#xff0c;创建俯视角2D场景时可通过以下配置关闭 Edit > Project Settings > Physics 2D > General Settings > Gravity 设置Y0

vue打包后直接本地访问index.html

以下方法可能有些项目并不生效 在vue.config.js 文件&#xff0c;没有的话就新建一个&#xff0c;并添加publicPath: ./ ’ module.exports {publicPath: ./ }router.js 把mode改成hash模式 const router new Router({mode: "hash",routes }); export default rout…

Elasticsearch——索引数据

索引可以说是Elasticsearch中非常重要的模块&#xff0c;一个索引可以视作关系数据库中的一张表&#xff0c;本帖将详细介绍与Elasticsearch索引相关的各种功能等。主要内容如下&#xff1a; 索引映射(mapping)结构的定义方法&#xff0c;常用的各种字段类型和动态映射的使用。…

细说 MySQL 用户安全加固策略

这是一篇关于如何加强 MySQL 用户安全的文章&#xff0c;通读全文您可以了解密码复杂度策略、连接控制插件以及密码变更策略的相关知识。本文内容仅供参考&#xff0c;请在操作时以实际环境为准&#xff0c;避免造成经济损失。 作者&#xff1a;余振兴&#xff0c;爱可生 DBA 团…

Linux系统LVS+Keepalived群集

目录 一、概述 &#xff08;一&#xff09;群集特性 1.负载均衡 2.健康检查&#xff08;探针&#xff09; 3.故障转移 &#xff08;二&#xff09;Keepalived 1.作用 &#xff08;1&#xff09;支持故障自动转移 &#xff08;2&#xff09;支持节点健康状态检…

Wireshark与其他工具的整合

第一章&#xff1a;Wireshark基础及捕获技巧 1.1 Wireshark基础知识回顾 1.2 高级捕获技巧&#xff1a;过滤器和捕获选项 1.3 Wireshark与其他抓包工具的比较 第二章&#xff1a;网络协议分析 2.1 网络协议分析&#xff1a;TCP、UDP、ICMP等 2.2 高级协议分析&#xff1a;HTTP…

使用k8s部署Mysql实例~hostpath,nfs服务器,PV和PVC持久化

1.k8s数据存储的分类&#xff1a; 1.基础存储的分类&#xff1a; 分类说明EmptyDirEmptyDir是在Pod被分配到Node时创建的&#xff0c;无须手动指定&#xff0c;当Pod销毁时&#xff0c; EmptyDir中的数据也会被永久删除HostPathHostPath就是将Node主机中一个实际目录挂载到Po…

Spring Boot重试调用第三方API

简介 在实际的应用开发中&#xff0c;我们经常需要调用第三方API来获取数据或执行操作。然而&#xff0c;由于网络波动、第三方服务不稳定等原因&#xff0c;API调用可能会面临失败的情况。为了提高应用的可靠性和稳定性&#xff0c;我们需要在调用第三方API时实现优雅的重试机…