文档章节

电商课题V:分布式锁

旁观者-郑昀
 旁观者-郑昀
发布于 2012/11/24 14:00
字数 1332
阅读 272
收藏 11
@郑昀汇总 
关键词:
并发控制
防止并发
 
英文关键词:
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)。
场景是,
  1.  C0操作超时了,但它还持有着锁,C1和C2读取lock.{orderid}检查时间戳,先后发现超时了。
  2.  C1 发送delete lock.{orderid},
  3.  C1 发送set lock.{orderid} 且成功。
  4.  C2 发送delete lock.{orderid},
  5.  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,保证子节点创建的时序性,从而形成了每个客户端的全局时序。

 
基于Redis的分布式锁
接着前面的竞态条件说,同样的场景下,使用Redis的SETNX(即SET if Not eXists,类似于memcache的add)和 GETSET(先写新值,返回旧值,原子性操作,可以用于分辨是不是首次操作)命令便可迎刃而解:
  1. C3发送SETNX lock.{orderid} 想要获得锁,由于C0还持有锁,所以Redis返回给C3一个0,
  2. C3发送GET lock.{orderid} 以检查锁是否超时了,如果没超时,则等待或重试。
  3. 反之,如果已超时,C3通过下面的操作来尝试获得锁:
    GETSET lock.{orderid} <current Unix time + lock timeout + 1>
  4. 通过GETSET,C3拿到的时间戳如果仍然是超时的,那就说明,C3如愿以偿拿到锁了。
  5. 如果在C3之前,有个叫C4的客户端比C3快一步执行了上面的操作,那么C3拿到的时间戳是个未超时的值,这时,C3没有如期获得锁,需要再次等待或重试。留意一下,尽管C3没拿到锁,但它改写了C4设置的锁的超时值,不过这一点非常微小的误差带来的影响可以忽略不计。
jeffkit的伪码参考:
  1. # get lock
  2. lock =  0
  3. while lock  !1:
  4.     timestamp = current Unix  time + lock timeout +  1
  5.     lock =  SETNX lock. orderid timestamp
  6.      if lock ==  1  or  (now ( )  >  ( GET lock. orderid )  and now ( )  >  ( GETSET lock. orderid timestamp ) ):
  7.          break
  8.      else:
  9.         sleep (10ms )
  10.  
  11. do_your_job ( )
  12.  
  13. # release lock
  14. if now ( )  < GET lock. orderid:
  15.      DEL lock. orderid

© 著作权归作者所有

旁观者-郑昀
粉丝 100
博文 77
码字总数 162700
作品 0
朝阳
私信 提问
关于 Multiget hole:spymemcached对此的实现方法

火丁说: 『 Multiget的无底洞问题 Facebook在Memcached的实际应用中,发现了Multiget无底洞问 题,具体表现为:出于效率的考虑,很多Memcached应用都已Multiget操作为主,随着访问量的增加,...

旁观者-郑昀
2013/02/08
127
0
spymemcached :某个mc节点操作连续超时超过998次就 Auto-Reconne...

spymemcached 中,一个 mc client 与某一个 mc node 之间已建立的 connection 上, 如果执行操作屡屡超时(net.spy.memcached.OperationTimeoutException), 那么,有一个计数器专门记录超时...

旁观者-郑昀
2013/02/08
162
0
spymemcached 的 useNagle 问题与 TCP/IP延迟发送数据

先说一下结论。 如果你没有特意在 spymemcached 的 client bean definition 里配置 useNagleAlgorithm 属性为 True, 那么默认 spymemcached 是不启用 Nagle 算法的。 所以默认情况下不会引发...

旁观者-郑昀
2013/02/08
167
0
数据一致性检测的应用场景与最佳实践

随着业务规模的扩张,企业系统变得越来越复杂,在这种复杂的分布式系统架构下,难免会出现远程调用失败,消息发送失败,并发 bug 等等问题,这些问题最终会导致系统间的数据不一致,导致用户...

中间件小哥
11/06
0
0
中国电子商务区块链规范发展中心将成立。

中国电子商务区块链规范发展中心隶属于工信部主管中国电子商务协会中国电子商务应用推广中心,是"中国电子商务新经济产业发展课题组"的重要职能机构,致力于规范区块链市场环境、开展区块链技...

二进制财经
2018/07/03
0
0

没有更多内容

加载失败,请刷新页面

加载更多

OSChina 周五乱弹 —— 你已经是个成熟的熊猫了

Osc乱弹歌单(2019)请戳(这里) 【今日歌曲】 @Sharon啊 :#今日歌曲推荐# 分享黑鸭子的单曲《羞答答的玫瑰静悄悄的开》 《羞答答的玫瑰静悄悄的开》- 黑鸭子 手机党少年们想听歌,请使劲儿...

小小编辑
22分钟前
83
8
结合Spring Security进行web应用会话安全管理

在本文中,将为大家说明如何结合Spring Security 和Spring Session管理web应用的会话。 一、Spring Security创建使用session的方法 Spring Security提供4种方式精确的控制会话的创建: alwa...

fightinging
27分钟前
3
0
83、Mybatis和Hibernate重要区别

Mybatis;入门简单,程序容易上手开发,节省开发成本。Mybatis需要程序猿自己编写sql语句,是一个不完全的ORM框架,对sql修改和优化非常容易实现。 Mybatis适合开发需求变更频繁的系统,比如...

lianbang_W
今天
5
0
设计模式之状态模式

定义 Allow an object to alter its behavior when its internal state changes.The object will appear to change its class.(当一个对象内在状态改变时允许其改变行为,这个对象看起来像改...

陈年之后是青葱
今天
6
0
Python常用模块之os.path

os.path.abspath(path) 输入相对路径,返回绝对路径 Python 3.7.0 (v3.7.0:1bf9cc5093, Jun 27 2018, 04:59:51) [MSC v.1914 64 bit (AMD64)] on win32Type "copyright", "credits" or "lic......

松鼠大帝
今天
11
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部