@郑昀汇总
关键词:
并发控制
防止并发
英文关键词:
Distributed Lock
Distributed Lock Manager
电商目的:
保证整个(分布式)系统内对一个重要事物(订单,账户等)的有效操作线程 ,同一时间内有且只有一个。比如交易中心有N台服务器,订单中心有M台服务器,如何保证一个订单的同一笔支付处理,一个账户的同一笔充值操作是原子性的。
基于哪些服务实现分布式锁?
- memcache
- ZooKeeper
- Redis
- Hazelcast
- google Chubby
基于memcache的分布式锁
memcache的所有命令都是原子性的(internally atomic),所以利用它的add命令即可。
郑昀列出一段简单但埋下了问题的伪码:
if (cache.add("lock:{orderid}", currenttimestamp, expiredtime)) {// 已获得锁,继续try{do something}catch{...}cache.delete("lock.{orderid}")} else {// 或等待锁超时,或重试,或返回}
上面代码所暴露的常见性问题
1)如持有锁的线程异常退出或宕机,锁并没有释放;
2)设置了key的expire,那么如果有新线程在key过期后拿到了新的锁,原来超时的线程回来时,如果不经判断会误认为那是它持有的锁,会误删锁。
1)强制释放
在键值上做文章,存入的是 current UNIX time+lock timeout+1 ,这样其他线程可以通过锁的键值对应的时间戳来判断这种情况是否发生了,如果当前的时间已经大于lock.{orderid}的键值,说明该锁已失效,可以被重新使用。
2)释放自己持有的锁时,先检查是否已超时
持有锁的线程在解锁之前应该再检查一次自己的锁是否已经超时,再去做DELETE操作,因为可能客户端因为某个耗时的操作而挂起,操作完的时候锁因为超时已经被别人获得,这时就不必解锁了。
上面的办法会引入新问题:
如果多个线程检测到锁超时,都尝试去释放锁,那么就会出现竞态条件(race condition)。
场景是,
- C0操作超时了,但它还持有着锁,C1和C2读取lock.{orderid}检查时间戳,先后发现超时了。
- C1 发送delete lock.{orderid},
- C1 发送set lock.{orderid} 且成功。
- C2 发送delete lock.{orderid},
- C2 发送set lock.{orderid} 且成功。
这样,C1和C2都认为自己拿到了锁。
如果比较在意这种竞态条件,那么推荐使用基于zookeeper或redis的解决方案。
基于ZooKeeper的分布式锁
这主要得益于ZooKeeper为我们保证了数据的强一致性,即用户只要完全相信每时每刻,zk集群中任意节点(一个zk server)上的相同znode的数据一定是相同的。锁服务可以分为两类,一个是保持独占,另一个是控制时序。
所谓保持独占,就是所有试图来获取这个锁的客户端,最终只有一个可以成功获得这把锁。通常的做法是把zk上的一个znode看作是一把锁,通过 create znode的方式来实现。所有客户端都去创建 /distributed_lock 节点,最终成功创建的那个客户端也就拥有了这把锁。
控制时序,就是所有试图获取这个锁的客户端,最终都是会被安排执行,只是有个全局时序。做法和上面基本类似,只是这里 /distributed_lock 已经预先存在,客户端在它下面创建临时有序节点(这个可以通过节点的属性控制:CreateMode.EPHEMERAL_SEQUENTIAL来指 定)。zk的父节点(/distributed_lock)维持一份sequence,保证子节点创建的时序性,从而形成了每个客户端的全局时序。
ZooKeeper 里实现分布式锁的基本逻辑:
- 客户端调用create()方法创建名为“_locknode_/guid-lock-”的节点,需要注意的是,这里节点的创建类型需要设置为EPHEMERAL_SEQUENTIAL。
- 客户端调用getChildren(“_locknode_”)方法来获取所有已经创建的子节点,同时在这个节点上注册上子节点变更通知的Watcher。
- 客户端获取到所有子节点path之后,如果发现自己在步骤1中创建的节点是所有节点中序号最小的,那么就认为这个客户端获得了锁。
- 如果在步骤3中发现自己并非是所有子节点中最小的,说明自己还没有获取到锁,就开始等待,直到下次子节点变更通知的时候,再进行子节点的获取,判断是否获取锁。
释放锁的过程相对比较简单,就是删除自己创建的那个子节点即可。
基于Redis的分布式锁
接着前面的竞态条件说,同样的场景下,使用Redis的SETNX(即SET if Not eXists,类似于memcache的add)和GETSET(先写新值,返回旧值,原子性操作,可以用于分辨是不是首次操作)命令便可迎刃而解:
- C3发送SETNX lock.{orderid} 想要获得锁,由于C0还持有锁,所以Redis返回给C3一个0,
- C3发送GET lock.{orderid} 以检查锁是否超时了,如果没超时,则等待或重试。
- 反之,如果已超时,C3通过下面的操作来尝试获得锁:
GETSET lock.{orderid} <current Unix time + lock timeout + 1> - 通过GETSET,C3拿到的时间戳如果仍然是超时的,那就说明,C3如愿以偿拿到锁了。
- 如果在C3之前,有个叫C4的客户端比C3快一步执行了上面的操作,那么C3拿到的时间戳是个未超时的值,这时,C3没有如期获得锁,需要再次等待或重试。留意一下,尽管C3没拿到锁,但它改写了C4设置的锁的超时值,不过这一点非常微小的误差带来的影响可以忽略不计。
jeffkit的伪码参考:
-
# get lock
-
lock = 0
-
while lock != 1:
-
timestamp = current Unix time + lock timeout + 1
-
lock = SETNX lock.orderid timestamp
-
if lock == 1 or (now() > (GET lock.orderid) and now() > (GETSET lock.orderid timestamp)):
-
break
-
else:
-
sleep(10ms)
-
-
do_your_job()
-
-
# release lock
-
if now() < GET lock.orderid:
-
DEL lock.orderid
参考资源:
1,jeffkit,用Redis实现分布式锁,基于redis;
2,rdc.taobao,ZooKeeper典型使用场景一览;
3,Ilya Sterin,Distributed locking made easy,基于zookeeper;
4,迟炯,解读Google分布式锁服务;
5,淘宝RDC,2012,zookeeper分布式锁避免羊群效应(Herd Effect);
相关推荐
2. 技术研发:处理和分析大数据需要高效的技术手段,如分布式计算、云计算、人工智能等,这些技术的发展是当前的重要课题。 3. 数据质量与整合:确保数据的准确性和一致性,以及不同来源数据的融合,是提高数据分析...
在大数据领域,我们首先需要理解“3V”模型:Volume(大量)、Velocity(高速)、Variety(多样)。Volume代表数据的规模,随着互联网、物联网、传感器等技术的发展,数据量呈现出指数级增长。Velocity强调数据产生...
它的特征被称为“4V”:Volume(大量)、Variety(多样性)、Value(价值)和Velocity(速度)。具体而言,大量指的是数据的体积非常庞大;多样性涉及结构化、半结构化和非结构化数据,如日志、BLOG、微博、音频、...
街道级行政区划shp矢量数据,wgs84坐标系,下载直接使用
街道级行政区划shp数据,wgs84坐标系,直接下载使用。
街道级行政区划shp矢量数据,wgs84坐标系,下载直接使用
轻量级密码算法LBlock的FPGA优化实现.docx
街道级行政区划shp矢量数据,wgs84坐标系,下载直接使用
Git 资料 progit-zh-v2.1.1.pdf
街道级行政区划shp数据,wgs84坐标系,直接下载使用。
篮球计分器FPGA附程序..doc
街道级行政区划shp数据,wgs84坐标系,直接下载使用。
内容概要:本文档全面介绍了Linux开发的基础知识、应用场景、环境搭建、常用命令、Shell脚本编程以及C/C++和Python开发等内容。首先阐述了Linux开发的重要性及其在服务器端开发、嵌入式开发和系统运维等领域的广泛应用。接着详细讲解了如何选择合适的Linux发行版并安装系统,配置开发环境,包括安装必要的开发工具和配置SSH服务。文档还深入讲解了Linux基础命令,如文件和目录操作、文件内容查看与编辑、进程管理和权限管理。此外,介绍了Shell脚本编程的基本语法,包括变量、条件语句、循环语句和函数定义。针对C/C++和Python开发,文档分别讲解了编译器安装、程序编写与编译、调试方法及使用虚拟环境等内容。最后,简要介绍了Linux内核开发的相关知识,包括下载编译内核、内核模块开发等,并推荐了相关学习资源。 适合人群:对Linux开发感兴趣的初学者及有一定经验的研发人员,尤其是希望深入掌握Linux开发技能的开发者。 使用场景及目标:①掌握Linux开发环境的搭建与配置;②熟悉Linux基础命令和Shell脚本编程;③学习C/C++和Python在Linux下的开发流程;④了解Linux内核开发的基本概念和技术。 阅读建议:此文档内容丰富,涵盖面广,建议读者根据自身需求选择性阅读,并结合实际操作进行练习。特别是对于初学者,应先掌握基础命令和开发环境的搭建,再逐步深入到编程语言和内核开发的学习。
街道级行政区划shp数据,wgs84坐标系,直接下载使用。
街道级行政区划shp数据,wgs84坐标系,直接下载使用。
街道级行政区划shp数据,wgs84坐标系,直接使用。
内容概要:本文档《word练习题.docx》是一份详细的Word操作练习指南,涵盖了从基础到高级的各种功能。文档分为三个主要部分:内容编辑、页面布局和高效文档。内容编辑部分包括文本格式化、段落设置、项目编号、制表位、边框与底纹等练习;页面布局部分涉及分节符、分栏、页眉页脚、水印等设置;高效文档部分则聚焦于样式管理、导航窗格、题注、书签、超级链接、脚注与尾注、交叉引用等功能。每个练习都有具体的操作步骤,帮助用户掌握Word的各种实用技巧。 适合人群:适用于Word初学者及希望提高Word技能的中级用户,尤其是需要频繁使用Word进行文档编辑和排版的办公人员。 使用场景及目标:①帮助用户熟悉Word的基本操作,如文本编辑、格式设置等;②提升用户的文档排版能力,学会设置复杂的页面布局;③提高工作效率,掌握高效文档管理技巧,如样式应用、题注和交叉引用等。 其他说明:此文档不仅提供了具体的练习题目,还附带了详细的步骤说明,用户可以根据指引逐步完成每个练习。此外,文档中的一些练习涉及到智能文档和Office智能客户端的应用,有助于用户了解Word在企业级应用中的潜力。建议用户按照章节顺序逐步学习,实践每一个练习,以达到最佳的学习效果。
街道级行政区划shp数据,wgs84坐标系,直接下载使用。
全球腐败感知数据(2000-2023)——3000行 33个指标 关于数据集 该数据集包含3000行和33列,涵盖了2000年至2023年的腐败感知指数(CPI)数据和各种治理指标。它包括国家排名、分数和其他指标,如公共部门腐败、司法腐败、贿赂指数、商业道德、民主指数、法治、政府效率、经济指标和人类发展指数。 这些数据可用于: 腐败趋势分析 腐败对GDP、人类发展指数和治理的影响 跨国比较 数据可视化和机器学习模型 该数据集对研究人员、数据分析师、政策制定者和对研究全球腐败趋势非常有用。