前面学习了Redis的数据结构以及命令、Redis中的事务和Redis对Lua脚本的支持。

这一章就对Redis这些特性做一下实战性应用——基于Redis的分布式锁实现。

Lock和Distributed Lock

在这之前先来认识下锁(Lock)和分布式锁(Distributed Lock):

In computer science, a lock or mutex (from mutual exclusion) is a synchronization mechanism for enforcing limits on access to a resource in an environment where there are many threads of execution. A lock is designed to enforce a mutual exclusion concurrency control policy.

参考wiki的解释:在计算机科学领域中,锁是为了限制多线程环境访问一个资源的一种同步机制。锁被设计相互排斥的并发策略。

Lock的前提条件:

  • 同一台机器上的共同资源;
  • 多线程环境访问共同资源;

Lock目标:

  • 保证多线程访问资源的一致性;

Lock的实现:

  • 单核处理器上禁用中断,使得同步资源能够被访问结束;
  • 硬件支持的原子指令,“比较和交换”等等,用于测试锁是否是空闲,如果空闲获取锁;

Operating systems use lock managers to organise and serialise the access to resources. A distributed lock manager (DLM) runs in every machine in a cluster, with an identical copy of a cluster-wide lock database. In this way a DLM provides software applications which are distributed across a cluster on multiple machines with a means to synchronize their accesses to shared resources.

参考wiki解释分布式锁:操作系统用锁管理器实现有组织有顺序的访问资源。分布式锁运行在集群环境中的每台机器上,使得数据具有相同的副本。分布式锁提供分布式软件应用同步访问共享资源。

Distribute Lock的前提条件:

  • 分布式软件应用;
  • 分布式软件中的共享资源;

Distribute Lock目标:

  • 保证分布式应用访问共享资源的一致性

Distribute Lock实现方式:

  • 基于Redis实现;
  • 基于Zookeeper实现;
  • 基于Etcd或者Consul实现;
  • Google开发的Chubby(Lock Service);

独占式锁的特点和影响

按照用途、场景划分,锁的类型非常多。如:排它锁(独占式锁)、共享锁,自旋锁、互斥锁,读锁、写锁。但是在分布式环境中的所谓的分布式锁,大多数情况下都是指:分布式独占式锁。

1.特点分析:

  • 每次只能一个占用锁;
  • 可以重复进入锁;
  • 只有占用者才可以解锁;
  • 获取锁和释放锁都需要原子
  • 不能产生死锁
  • 尽量满足性能

本质:同步互斥,使得处理任务能够一个一个逐步的过临界资源。

造成的影响:

  • 降低并发数,使得多任务处理,只能一个一个的进行;
  • 任务的换进换出造成切换上的开销;

本质:使得吞吐量大打折扣。

基于Redis的实现

Redis实现分布式锁的基础

1.Redis本身就是单线程:

  • 单个命令执行具有原子性、无竞态条件,这个特点符合一次只有一个客户端争用锁;

2.Redis提供了set if not exists操作:

  • 存在即不设置,这个特点符合锁的独占性(排它特点);

下面来先来看下获取锁:

return jedis.set(lockKey, lockValue, NX, EX, expireTime) != null ? true : false;

这里使用set指令,具有原子操作特点,不会被其他客户端操作中断,在分布式环境中,是安全的,没有竞态条件产生,一次只能有一个客户端争用锁;使用nx,即存在不设置,符合独占特点;设置ex,有过期效果,不会产生永久独占即死锁;最后设置了lockValue,这样就和当前加锁任务做了绑定,后面可以用其作为解锁的钥匙;

再来看下解锁操作:

static final String RELEASE_LOCK_LUA = "if redis.call('get', KEYS[1]) == ARGV[1] " +
"then return redis.call('del', KEYS[1]) else return 0 end"; Object result = jedis.eval(RELEASE_LOCK_LUA, 1, lockKey, lockValue);

这里解锁是用了Lua脚本,上篇文章中介绍了Redis内置一个Lua解释器,Redis调用解释器执行Lua脚本也是具有原子性的,即同一时刻只有一个客户端的操作能被执行,所以这里使用Lua脚本解锁无竞态条件;解锁符合是占用锁的任务释放的原理;

但是以上实现的分布式锁缺点是:

  • 不具有重入性,即当前任务获取了锁,在下次获取时将会死锁;
  • 不能自旋获取,即获取失败时,将会立即返回失败;

本人对其进行了改造,分别做了适应以上两种场景的分布式锁,详情可以戮[Distributed Lock],欢迎大家一起来完善。

总结

本文从What、Features、How的角度分析了分布式锁。总的来说,单机应用中的多线程或者多进程的锁的放大版基本上就是分布式锁了。万变不离其宗,实现独占锁的关键性要素:

  • 目标:互斥同步,资源访问原子化;
  • 实现:一次只能有一个争用到锁,争用过程是个原子过程,只能争用到的解锁,不会发生死锁;
参考

Redis 分布式锁的正确实现方式

Rewriting our lock

Lock

题外话

参考Rewriting our lock中使用setnx实现的分布式,严格意义上来说是有死锁问题的。setnx和expire不具有原子性。当setnx成功后,expire前应用发生宕机,这会导致锁永远不会过期,别的应用始终争用不到锁。当然这种情况比较特殊,但是做代码是一件严谨的事!

Redis(七)分布式锁的更多相关文章

  1. Redis学习系列七分布式锁

    一.简介 熟悉.Net多线程的都知道,当多个线程同时操作一个全局缓存对象(static对象实例.Dictionary.List等)时,会存在多线程争用问题,包括EF.Dapper等本身的缓存机制,都存 ...

  2. 利用多写Redis实现分布式锁原理与实现分析(转)

    利用多写Redis实现分布式锁原理与实现分析   一.关于分布式锁 关于分布式锁,可能绝大部分人都会或多或少涉及到. 我举二个例子:场景一:从前端界面发起一笔支付请求,如果前端没有做防重处理,那么可能 ...

  3. Redis实现分布式锁原理与实现分析

    一.关于分布式锁 关于分布式锁,可能绝大部分人都会或多或少涉及到. 我举二个例子: 场景一:从前端界面发起一笔支付请求,如果前端没有做防重处理,那么可能在某一个时刻会有二笔一样的单子同时到达系统后台. ...

  4. 基于redis 实现分布式锁的方案

    在电商项目中,经常有秒杀这样的活动促销,在并发访问下,很容易出现上述问题.如果在库存操作上,加锁就可以避免库存卖超的问题.分布式锁使分布式系统之间同步访问共享资源的一种方式 基于redis实现分布式锁 ...

  5. 用Redis构建分布式锁-RedLock(真分布)

    在不同进程需要互斥地访问共享资源时,分布式锁是一种非常有用的技术手段. 有很多三方库和文章描述如何用Redis实现一个分布式锁管理器,但是这些库实现的方式差别很大,而且很多简单的实现其实只需采用稍微增 ...

  6. 用Redis实现分布式锁 与 实现任务队列(转)

    这一次总结和分享用Redis实现分布式锁 与 实现任务队列 这两大强大的功能.先扯点个人观点,之前我看了一篇博文说博客园的文章大部分都是分享代码,博文里强调说分享思路比分享代码更重要(貌似大概是这个意 ...

  7. Redis实现分布式锁

    http://redis.io/topics/distlock 在不同进程需要互斥地访问共享资源时,分布式锁是一种非常有用的技术手段. 有很多三方库和文章描述如何用Redis实现一个分布式锁管理器,但 ...

  8. 基于redis的分布式锁

    <?php /** * 基于redis的分布式锁 * * 参考开源代码: * http://nleach.com/post/31299575840/redis-mutex-in-php * * ...

  9. Redis实现分布式锁与任务队列

    Redis实现分布式锁 与 实现任务队列 这一次总结和分享用Redis实现分布式锁 与 实现任务队列 这两大强大的功能.先扯点个人观点,之前我看了一篇博文说博客园的文章大部分都是分享代码,博文里强调说 ...

  10. 使用Redis实现分布式锁

    在天猫.京东.苏宁等等电商网站上有很多秒杀活动,例如在某一个时刻抢购一个原价1999现在秒杀价只要999的手机时,会迎来一个用户请求的高峰期,可能会有几十万几百万的并发量,来抢这个手机,在高并发的情形 ...

随机推荐

  1. PHP面试题2019年小米工程师面试题及答案解析

    一.单选题(共29题,每题5分) 1.PHP面向对象方法重写描述错误的是? A.子类必须继承父类 B.子类可以重写父类已有方法 C.重写之后子类会调用父类方法 D.子类也可以具有与父类同名的属性,进行 ...

  2. Redis哨兵模式大key优化

    目前,Redis哨兵模式,内存资源有限,有很多key大于500M,性能待优化.需要迁移至Redis-cluster集群中.        涉及到的key如下: 0,hash,duser_record, ...

  3. css3自适应布局单位vw,vh详解

    视口单位(Viewport units) 什么是视口? 在桌面端,视口指的是在桌面端,指的是浏览器的可视区域:而在移动端,它涉及3个视口:Layout Viewport(布局视口),Visual Vi ...

  4. git设置github的远程仓库的相关操作

        git能够把github作为远程仓库,本地可以进行推送有关变更,从而多人可以进行协作开发工作.    1  ssh-keygen -t rsa -C "your-email@163. ...

  5. hadoop 自定义OutputFormat

    1.继承FileOutputFormat,复写getRecordWriter方法 /** * @Description:自定义outputFormat,输出数据到不同的文件 */ public cla ...

  6. [转]HotSpot VM GC 的种类

    原文地址:http://www.cnblogs.com/redcreen/archive/2011/05/04/2037029.html collector种类 GC在 HotSpot VM 5.0里 ...

  7. [20191012]组成rowid.txt

    [20191012]组成rowid.txt --//昨天做了拆分rowid的测试,链接http://blog.itpub.net/267265/viewspace-2659613/=>[2019 ...

  8. 05webpack-webpack-dev-server时时跟新-第2种方式

    <!--14 第一种方式 自动打开浏览器 端口号 指定托管的跟目录 启动热刷新 这种是在webpack.json中去配置的 直接在package中 写 将“script”:{ "dev ...

  9. 初学树型dp

    树型DP DFS的回溯是树形DP的重点以及核心,当回溯结束后,root的子树已经被遍历完并处理完了.这便是树形DP的最重要的特点 自己认为应该注意的点 好多人都说在更新当前节点时,它的儿子结点都给更新 ...

  10. 初学Python几个小程序练习

    使用格式化输出的三种方式实现以下输出(name换成自己的名字,既得修改身高体重,不要厚颜无耻) name = 'ABDMLBM' height = 175 weight = 140 # "M ...