基于Redis实现简单的分布式锁
- 事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。
- 事务是一个原子操作:事务中的命令要么全部被执行,要么全部都不执行。
Available since 1.0.0.
Time complexity: O(1)
Set key to hold string value if key does not exist. In that case, it is equal to SET. When key already holds a value, no operation is performed. SETNX is short for "SET if Not eXists".
Return value
Integer reply, specifically:
1 if the key was set
0 if the key was not set
起始版本:1.0.0
时间复杂度:O(1)自动将key对应到value并且返回原来key对应的value,返回之前的旧值,如果之前Key不存在将返回nil。
上一个经验虽说可以解决这条数据该“插入还是更新”的问题,但需要知道当前操作是否针对某数据的首次操作的需求还不少。例如我的程序会在不同时间接收到同一条消息的不同分片信息包,我需要在收到该消息的首个信息包(发送是无序的)时做些特殊处理。早些时候的做法是为消息在MongoDB维护一个状态对象,有信息包来的时候就走“上锁->检查消息状态->根据状态决定做不做特殊操作->解锁” 这个流程,虽然同事已经把锁的粒度控制得非常细了,但有锁的程序遇上多实例部署就歇了。Redis的GETSET是解决这个问题的终极武器,只需要把当前信息包的唯一标识对指定的状态属性进行一次GETSET操作,再判断返回值是否为空则知道是否首次操作。GETSET替我们把两次读写的操作封装成了原子操作。
public static final int ACQUIRE_LOCK_MAX_ATTEMPTS = 10;public static final long EXPIRE_TIME = 5000L;/*** 基于时间戳根据lockKey尝试获取锁,需要与releaseLock成对使用;* <p/>使用该方法的前提是必须要保证服务器之间时间同步* <p/>如果持有锁的时间超过 #{EXPIRE_TIME},视为超时,其他客户端可以对其进行重新获取锁的操作** @param lockKey - 锁键值,即争夺的资源* @return - 当成功获取锁后,返回true,否则返回false;如果没有获取锁,需要客户端进行轮询来尝试获取*/public boolean acquireLock(String lockKey) {return acquireLock(lockKey, 0);}private boolean acquireLock(String lockKey, int depth) {long setnx = jedis.setnx(lockKey, String.valueOf(System.currentTimeMillis()));if (setnx == 1L) {//说明客户端已经获得锁LOG.info(String.format("lock key : %s is acquired!", lockKey));return true;} else {//此时,该lockKey已经被其他客户端加锁String keyTimestamp = jedis.get(lockKey);if (keyTimestamp == null) {//如果该值已经被清空,就尝试去重新获取if (depth == ACQUIRE_LOCK_MAX_ATTEMPTS) {//如果尝试次数超过10次,则不再尝试,直接返回falseLOG.info(String.format("lock key : %s exceed max attemps: %s, quit!", lockKey,ACQUIRE_LOCK_MAX_ATTEMPTS));return false;}return acquireLock(lockKey, depth + 1);}long intervalTime = System.currentTimeMillis() - Long.valueOf(keyTimestamp);if (intervalTime < EXPIRE_TIME) {//锁在一定时间内并没有超时,获取锁失败LOG.info("lock key : %s acquire failed! other client persist this lock!", lockKey);return false;} else {//锁已经超时,尝试执行getset操作,设置当前时间戳String getSetTimestamp = jedis.getSet(lockKey, String.valueOf(System.currentTimeMillis()));if (getSetTimestamp == null) {//考虑非常特殊的情况,有人释放了锁执行del操作//此时get/set拿到的是nil值,说明已经获得了锁LOG.info(String.format("lock key : %s is acquired! GETSET returns null!", lockKey));return true;}if (!getSetTimestamp.equals(keyTimestamp)) {//在设置时,说明该锁已经被其他client加上//此时会有对应的副作用,比如LOG.info("lock key : %s acquire failed! other client acquire this lock!", lockKey);return false;} else {//锁已更新,可以正常返回LOG.info(String.format("lock key : %s is acquired! origin client is time out!", lockKey));return true;}}}}
/*** 释放lockKey对应的锁,注意需要与acquireLock成对使用* <p/>不能随意对其他人使用的锁进行释放操作** @param lockKey* @return - 如果释放成功返回true*/public boolean releaseLock(String lockKey) {boolean result = jedis.del(lockKey) == 1L;LOG.info(String.format("lock key: %s is released!", lockKey));return result;}
四月 28, 2016 5:03:18 下午 org.apache.catalina.core.StandardWrapperValve invoke
严重: Servlet.service() for servlet [springmvc] in context with path [] threw exception [Request processing failed; nested exception is redis.clients.jedis.exceptions.JedisConnectionException: java.net.SocketException: Broken pipe] with root cause
java.net.SocketException: Broken pipeat java.net.SocketOutputStream.socketWrite0(Native Method)at java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:109)at java.net.SocketOutputStream.write(SocketOutputStream.java:153)at redis.clients.util.RedisOutputStream.flushBuffer(RedisOutputStream.java:52)at redis.clients.util.RedisOutputStream.flush(RedisOutputStream.java:213)at redis.clients.jedis.Connection.flush(Connection.java:288)at redis.clients.jedis.Connection.getBinaryBulkReply(Connection.java:214)at redis.clients.jedis.Connection.getBulkReply(Connection.java:205)at redis.clients.jedis.Jedis.get(Jedis.java:101)at com.api.example.controller.UserController.decrElement(UserController.java:52)at sun.reflect.GeneratedMethodAccessor25.invoke(Unknown Source)at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)at java.lang.reflect.Method.invoke(Method.java:483)at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:221)at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:137)at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:110)at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandleMethod(RequestMappingHandlerAdapter.java:777)at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:706)at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85)at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:943)at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:877)at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:966)at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:857)at javax.servlet.http.HttpServlet.service(HttpServlet.java:620)at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:842)at javax.servlet.http.HttpServlet.service(HttpServlet.java:727)at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:303)at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:88)at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:220)at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:122)at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:501)at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171)at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:950)at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116)at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:408)at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1040)at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:607)at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:314)at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)at java.lang.Thread.run(Thread.java:745)
Jedis jedis = jedisPool.getResource();try {while (true) {String productCountString = jedis.get("product");if (Integer.parseInt(productCountString) > 0) {if (acquireLock(jedis, "abc")) {int productCount = Integer.parseInt(jedis.get("product"));System.out.println(String.format("%tT --- Get product: %s", new Date(), productCount));
// System.out.println(productCount);jedis.decr("product");releaseLock(jedis, "abc");return "Success";}Thread.sleep(1000L);} else {return "Over";}}} finally {jedis.close();}
转载于:https://www.cnblogs.com/mmaa/p/5789865.html
基于Redis实现简单的分布式锁相关推荐
- springboot mysql行锁_SpringBoot基于数据库实现简单的分布式锁
本文介绍SpringBoot基于数据库实现简单的分布式锁. 1.简介 分布式锁的方式有很多种,通常方案有: 基于mysql数据库 基于redis 基于ZooKeeper 网上的实现方式有很多,本文主要 ...
- 基于 Redis + Lua 脚本实现分布式锁,确保操作的原子性
为了保证数据的争用安全,通常要采用锁机制控制. 如果是单应用部署,直接通过synchronized关键字修改方法,就能解决,但是如果是分布式的部署 该方法就不能解决这个问题啦,此时就引出了一个分布式锁 ...
- redis多服务器共享_基于redis和shedlock实现分布式锁(超简单)
一.背景 线上部署了两台服务器,通过nginx轮询的方式进行负载均衡.但是这样存在一个问题同一个用户的session共享问题.你或许会说,使用ipHash模式就可以解决session共享的问题,是的确 ...
- 基于redis集群的分布式锁redlock
Redis 作者为了解决因为主备切换.脑裂导致 Redis 单集群分布式锁不安全的问题,提出了 redlock 算法,下面是针对 文章 的翻译和一些自我理解. 一.安全性和可用性保证 用三个属性来建模 ...
- Redis应用学习——Redis事务与实现分布式锁
2019独角兽企业重金招聘Python工程师标准>>> 1. Redis事务机制 1. 与MySQL等关系数据库相同,Redis中也有事务机制,Redis的事务实质上是命令的集合,但 ...
- redisson redlock(基于redisson框架和redis集群使用分布式锁)
一.关于分布式锁的两篇文章 文章1 文章2 二.redis分布式锁存在的问题 redis实现分布式锁有很多种方案,比较完善的方案应该是用setNx + lua进行实现.简单实现如下: java代码-加 ...
- 在 Redis 上实现的分布式锁
由于近排很忙,忙各种事情,还有工作上的项目,已经超过一个月没写博客了,确实有点惭愧啊,没能每天或者至少每周坚持写一篇博客.这一个月里面接触到很多新知识,同时也遇到很多技术上的难点,在这我将对每一个有用 ...
- redis set 超时_redis分布式锁3种实现方式对比分析总结
我在这篇文章提到了分布式锁,但没有展开来讲,抛砖引玉,今天就来说说高并发服务编程中的redis分布式锁. 这里罗列出3种redis实现的分布式锁,并分别对比说明各自特点. Redis单实例分布式锁 实 ...
- Day137-139.尚品汇:制作SKU、商品详情、项目优化:Redis缓存、redssion分布式锁
目录 Day5 制作SKU 1. 制作SKU 2. 多表查询如何写? 3. 制作SKU 4. Thymeleaf Day06 商品详情 1. 获取分类信息 2. 获取最新价格信息 3. 获取销售信息 ...
最新文章
- 互斥量、读写锁长占时分析的利器——valgrind的DRD
- [转]商业智能在电子商务交易中6大应用分析
- 微信电脑网页二维码扫描登录简单实现
- c# winform窗口自适应各种分辨率类
- MySQL数据库的性能优化总结
- 使用devops的团队_DevOps团队的3种指标仪表板
- 如何用Canarytokens搭建蜜罐并检测可疑入侵
- 苹果mac图像编辑和设计工具:Photoshop 2021
- PHP问题Parse error: syntax error, unexpected end of file in
- Android项目:通过ant重新打包proguard混淆器jar文件
- 使用 Python 批量下载喜马拉雅有声书音频
- 嵌入式分享合集126
- #AR游戏--音之国度#初次简单的测试
- LogLoss的公式演化
- android 让app全屏显示,Android app设置全屏模式
- 【机器学习】数值分析02——任意方程求根
- python黑白像素面积占比计算(脏污、白点等)
- 江西省信息技术知识竞赛题库
- 投影仪显示服务器连接,投影仪怎么连接电脑 电脑与投影仪连接方法【详细步骤】...
- 计算机软件专业英语简历,计算机软件专业的英文简历模板
热门文章
- python连接redis哨兵_python连接redis sentinel集群
- 王道 —— 操作系统的运行机制和体系结构
- 卡尔曼滤波算法及C语言实现(转载)
- CMake 使用方法 CMakeList.txt编写简单分析
- mysql查询一个数据库所有表的记录数,mysql 查看数据库中所有表的记录数
- Python GUI界面编程初步 01 - GUI库的特点和选择
- simulink 快捷键 运行_【泡泡读者来搞】ROS、Simulink、Carsim的互联与规划、控制算法的验证...
- android怎么刷新活动,Android 如何刷新当前activity的显示数据?
- 联想开机启动项按哪个_联想电脑开机按f12后,怎么设置默认启动项
- python 去掉文件后缀_python从zip中删除指定后缀文件(推荐)