三种分布式锁 简易说说(包含前一篇提到的redis分布式锁)
大多数互联网系统都是分布式部署的,分布式部署确实能带来性能和效率上的提升,但为此,我们就需要多解决一个分布式环境下,数据一致性的问题。
当某个资源在多系统之间,具有共享性的时候,为了保证大家访问这个资源数据是一致的,那么就必须要求在同一时刻只能被一个客户端处理,不能并发的执行,否者就会出现同一时刻有人写有人读,大家访问到的数据就不一致了。
一、我们为什么需要分布式锁?
在单机时代,虽然不需要分布式锁,但也面临过类似的问题,只不过在单机的情况下,如果有多个线程要同时访问某个共享资源的时候,我们可以采用线程间加锁的机制,即当某个线程获取到这个资源后,就立即对这个资源进行加锁,当使用完资源之后,再解锁,其它线程就可以接着使用了。例如,在JAVA中,甚至专门提供了一些处理锁机制的一些API(synchronize/Lock等)。
但是到了分布式系统的时代,这种线程之间的锁机制,就没作用了,系统可能会有多份并且部署在不同的机器上,这些资源已经不是在线程之间共享了,而是属于进程之间共享的资源。
因此,为了解决这个问题,我们就必须引入「分布式锁」。
分布式锁,是指在分布式的部署环境下,通过锁机制来让多客户端互斥的对共享资源进行访问。
分布式锁要满足哪些要求呢?
排他性:在同一时间只会有一个客户端能获取到锁,其它客户端无法同时获取
避免死锁:这把锁在一段有限的时间之后,一定会被释放(正常释放或异常释放)
高可用:获取或释放锁的机制必须高可用且性能佳
讲完了背景和理论,那我们接下来再看一下分布式锁的具体分类和实际运用。
二、分布式锁的实现方式有哪些?
目前主流的有三种,从实现的复杂度上来看,从上往下难度依次增加:
基于数据库实现
基于Redis实现
基于ZooKeeper实现
无论哪种方式,其实都不完美,依旧要根据咱们业务的实际场景来选择。
基于数据库实现:
基于数据库来做分布式锁的话,通常有两种做法:
基于数据库的乐观锁
基于数据库的悲观锁
我们先来看一下如何基于「乐观锁」来实现:
乐观锁机制其实就是在数据库表中引入一个版本号(version)字段来实现的。
当我们要从数据库中读取数据的时候,同时把这个version字段也读出来,如果要对读出来的数据进行更新后写回数据库,则需要将version加1,同时将新的数据与新的version更新到数据表中,且必须在更新的时候同时检查目前数据库里version值是不是之前的那个version,如果是,则正常更新。如果不是,则更新失败,说明在这个过程中有其它的进程去更新过数据了。
下面找图举例,
(图片来源网络)
如图,假设同一个账户,用户A和用户B都要去进行取款操作,账户的原始余额是2000,用户A要去取1500,用户B要去取1000,如果没有锁机制的话,在并发的情况下,可能会出现余额同时被扣1500和1000,导致最终余额的不正确甚至是负数。但如果这里用到乐观锁机制,当两个用户去数据库中读取余额的时候,除了读取到2000余额以外,还读取了当前的版本号version=1,等用户A或用户B去修改数据库余额的时候,无论谁先操作,都会将版本号加1,即version=2,那么另外一个用户去更新的时候就发现版本号不对,已经变成2了,不是当初读出来时候的1,那么本次更新失败,就得重新去读取最新的数据库余额。
通过上面这个例子可以看出来,使用「乐观锁」机制,必须得满足:
(1)锁服务要有递增的版本号version
(2)每次更新数据的时候都必须先判断版本号对不对,然后再写入新的版本号
我们再来看一下如何基于「悲观锁」来实现:
悲观锁也叫作排它锁,在Mysql中是基于 for update 来实现加锁的,例如:
//锁定的方法-伪代码public boolean lock(){ connection.setAutoCommit(false) for(){ result = select * from user where id = 100 for update; if(result){ //结果不为空, //则说明获取到了锁 return true; } //没有获取到锁,继续获取 sleep(1000); } return false;}
//释放锁-伪代码connection.commit();
上面的示例中,user表中,id是主键,通过 for update 操作,数据库在查询的时候就会给这条记录加上排它锁。
(需要注意的是,在InnoDB中只有字段加了索引的,才会是行级锁,否者是表级锁,所以这个id字段要加索引)
当这条记录加上排它锁之后,其它线程是无法操作这条记录的。
那么,这样的话,我们就可以认为获得了排它锁的这个线程是拥有了分布式锁,然后就可以执行我们想要做的业务逻辑,当逻辑完成之后,再调用上述释放锁的语句即可。
基于Redis实现(redlock算法详解: https://www.jianshu.com/p/fba7dd6dcef5)
基于Redis实现的锁机制,主要是依赖redis自身的原子操作,例如:
SET user_key user_value NX PX 100
redis从2.6.12版本开始,SET命令才支持这些参数:
NX:只在在键不存在时,才对键进行设置操作,SET key value NX 效果等同于 SETNX key value
PX millisecond:设置键的过期时间为millisecond毫秒,当超过这个时间后,设置的键会自动失效
上述代码示例是指,
当redis中不存在user_key这个键的时候,才会去设置一个user_key键,并且给这个键的值设置为 user_value,且这个键的存活时间为100ms
为什么这个命令可以帮我们实现锁机制呢?
因为这个命令是只有在某个key不存在的时候,才会执行成功。那么当多个进程同时并发的去设置同一个key的时候,就永远只会有一个进程成功。
当某个进程设置成功之后,就可以去执行业务逻辑了,等业务逻辑执行完毕之后,再去进行解锁。
解锁很简单,只需要删除这个key就可以了,不过删除之前需要判断,这个key对应的value是当初自己设置的那个。
另外,针对redis集群模式的分布式锁,可以采用redis的Redlock机制。
基于ZooKeeper实现(更详细的描述链接: https://juejin.im/post/5c01532ef265da61362232ed?utm_source=gold_browser_extension)
其实基于ZooKeeper,就是使用它的临时有序节点来实现的分布式锁。
原理就是:当某客户端要进行逻辑的加锁时,就在zookeeper上的某个指定节点的目录下,去生成一个唯一的临时有序节点, 然后判断自己是否是这些有序节点中序号最小的一个,如果是,则算是获取了锁。如果不是,则说明没有获取到锁,那么就需要在序列中找到比自己小的那个节点,并对其调用exist()方法,对其注册事件监听,当监听到这个节点被删除了,那就再去判断一次自己当初创建的节点是否变成了序列中最小的。如果是,则获取锁,如果不是,则重复上述步骤。
当释放锁的时候,只需将这个临时节点删除即可。
(图片来自网络)
如图,locker是一个持久节点,node_1/node_2/…/node_n 就是上面说的临时节点,由客户端client去创建的。
client_1/client_2/…/clien_n 都是想去获取锁的客户端。以client_1为例,它想去获取分布式锁,则需要跑到locker下面去创建临时节点(假如是node_1)创建完毕后,看一下自己的节点序号是否是locker下面最小的,如果是,则获取了锁。如果不是,则去找到比自己小的那个节点(假如是node_2),找到后,就监听node_2,直到node_2被删除,那么就开始再次判断自己的node_1是不是序列中最小的,如果是,则获取锁,如果还不是,则继续找一下一个节点。
以上,就讲完了为什么我们需要分布式锁这个技术,以及分布式锁中常见的三种机制,欢迎大家一起交流。
转载于:https://www.cnblogs.com/yzf666/p/10119850.html
三种分布式锁 简易说说(包含前一篇提到的redis分布式锁)相关推荐
- 【C语言】三种不同方法实现简易整型计算器 (三个方法递增优化)
前言:在学习C语言一段时间后,相信我们都能用C语言写出简易的计算器小程序,然而一般方法都会使代码比较冗余,不够简便,代码可变性也会比较差 (见列一): 今天我就使用函数指针数组给大家带来实现起来代码比 ...
- python求两个数的最大公约数穷举法_求两个数字的最大公约数-Python实现,三种方法效率比较,包含质数打印质数的方法...
#coding:utf-8 importtime#辗转相除法: defcommonDivisor1(num1,num2):if num1 temp=num1 num1=num2 num2=tempif ...
- Jedis使用总结【pipeline】【分布式的id生成器】【分布式锁【watch】【multi】】【redis分布式】
参考: http://www.blogjava.net/masfay/archive/2012/07/03/382080.html http://www.jeffkit.info/2011/07/10 ...
- 笔记连载 | Day6 FPGA三种建模方式区别及Verilog语法基础篇
听说99%的同学都来这里充电吖 本系列为线下学员学习笔记整理分享,如有想要报名参加线下培训,可以点击以下超链接文章了解,购买开发板可以到叁芯智能科技企业淘宝店下单. 各类研修班 | FPGA就业研修班 ...
- 分布式锁简单入门以及三种实现方式介绍(滴滴)
很多小伙伴在学习Java的时候,总是感觉Java多线程在实际的业务中很少使用,以至于不会花太多的时间去学习,技术债不断累积!等到了一定程度的时候对于与Java多线程相关的东西就很难理解,今天需要探讨的 ...
- 三种实现分布式锁的方式
一.为什么要使用分布式锁 我们在开发应用的时候,如果需要对某一个共享变量进行多线程同步访问的时候,可以使用我们学到的Java多线程的18般武艺进行处理,并且可以完美的运行,毫无Bug! 注意这是单机应 ...
- 分布式锁以及三种加锁方式
在很多场景中,我们为了保证数据的最终一致性,需要很多的技术方案来支持,比如分布式事务.分布式锁等.那具体什么是分布式锁,分布式锁应用在哪些业务场景.如何来实现分布式锁呢? 一 为什么要使用分布式锁 我 ...
- 分布式锁简单入门以及三种实现方式介绍_徐刘根的博客-CSDN博客
原文地址 rel="stylesheet" href="https://csdnimg.cn/release/phoenix/template/css/ck_htmled ...
- 分布式锁简单入门以及三种实现方式介绍
分布式锁简单入门以及三种实现方式介绍 2018年01月11日 21:16:28 徐刘根 阅读数:37912 标签: 分布式 分布式锁 高并发 更多 个人分类: 集群分布式 版权声明:本文为博主原创文章 ...
最新文章
- signature=0a26d8967069103efeee67346aac0529,Construction of Thinned Gated Single-Assignment Form
- 解析|2G~5G与未来天线技术!
- android打包apk、apk签名及360加固工具的使用
- 【thymeleaf】th:text、[[]]、th:utext、[()]输出变量
- java testwhileidle_DBCP踩坑(二):连接池检查testWhileIdle失效
- bat文件设置dns服务器,.bat文件设置IP、DNS
- asp.net中,%#%,%=%和%%分别是什么意思,有什么区别
- GeoPoint与LocationData
- jboss7.1.安装配置
- ios 图片裁剪框架_iOS 图片裁剪与修改
- vue的自定义指令的坑
- asp.net core 系列之并发冲突
- 理论综述与创新---结构洞的新应用
- 更改桌面 计算机 的图标w10,W10电脑系统如何修改桌面快捷方图标
- c语言统计大小写字母 空格 和数字个数,C++统计中英文大小写字母、数字、空格及其他字符个数的方法...
- 【clickhouse踩坑记录】clusters表中分片副本的浅析
- 22道接口测试面试题答案,记住轻松挑战年薪50万
- 浙大 计算机 设计学 考研科目,2020浙江大学软件工程考研参考书目
- webchart 生成饼状图 java_WebChart生成折线图,柱状图,饼状图
- Sorry ,中产 -20160929
热门文章
- java 线程间的通讯(升级版)
- 解释一下pack和unpack
- 【渗透测试】—如何利用文件包含的方式进行攻击
- 12505 java_Java jdbc Oracle error: ORA 12505
- JS开发引用HTML DOM的location和document对象
- stack和queue容器
- Vue绑定style的使用方式(令组件复用)
- python文件输入符_python基础入门详解(文件输入/输出 内建类型 字典操作使用方法)...
- pyecharts显示所有x轴_基于Pyecharts可视化大屏案例一(1)
- Tr A 矩阵快速幂