前面学习了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. list方法补充

    在上一个随便我们写了list 常用的方法,该随便为一些需要补充的内容 注:本次例子为: student = ["张天赐","小明","小红" ...

  2. JS原生对象实现异步请求以及JQ的ajax请求四种方式

    一.JS原生方式异步请求 <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="A ...

  3. h5本地存储登录页面实现记住密码功能

    <!DOCTYPE html> <html> <head> <title></title> </head> <style ...

  4. Python上下文管理器的使用

    上下文管理器可以控制代码块执行前的准备动作,以及执行后的清理动作. 创建一个上下文管理器类的步骤:(1)一个__init__方法,来完成初始化(可选)(2)一个__enter__方法,来完成所有建立工 ...

  5. Vue实战狗尾草博客后台管理系统第三章

    Vue实现狗尾草博客后台管理系统第三章 本章节,咱们开发管理系统侧边栏及面包屑功能. 先上一张效果图 样式呢,作者前端初审,关于设计上毫无美感可言,大家可根据自己情况设计更好看的哦~ 侧边栏 这里我们 ...

  6. ElasticSearch安装及运行的坑

    一.确认centos系统是为64位的,x86的不可以安装 1. 下载elasticsearch包 2. 用 tar -zxvf 解压包 3. 增加一个elk用户,elasticsearch7不可用ro ...

  7. oracle通过impdp导入不同表用户、不同表空间的数据

    原创 tegwy 发布于2013-04-21 17:34:39 阅读数 46592 收藏 展开 impdp smtj2012/密码@localhost/devdb DIRECTORY=dump_dir ...

  8. 『010』NoSQL

    『010』索引-Database NoSQL [001]- 点我快速打开文章[01-Redis 简单介绍] 更新中

  9. 2. Java程序的运行机制

    一.完成一个Java程序的流程:编辑Java源代码→编译Java程序→运行Java程序 1. 在记事本中编写Java程序,然后保存为.java类型文件(Java源文件) 2. 使用javac命令将源文 ...

  10. 往对象数组里面添加相同的key 不同的value 和删除相同的key值

    应用场景:后盾字段没有发给你  自己补充数据 <div v-for="item in list" :key="item.id"> <p> ...