前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >关于分布式锁的那些事

关于分布式锁的那些事

作者头像
山行AI
发布2019-06-28 16:24:53
6900
发布2019-06-28 16:24:53
举报
文章被收录于专栏:山行AI

1. 为什么要有分布式锁

一般情况下,如果是单结点服务,直接用进程内的锁(一般情况下都是使用可重入锁),比如synchronized、基于AQS的ReentrantLock等。甚至于一个线程安全的容器,比如ConcurrentHashMap都可以进行资源的锁定。但是,在分布式环境中,这些方法是没有办法 跨jvm进程来做并发控制的,这时就需要分布式锁。

2. 分布式锁的实现方式

  • 数据库实现分布式锁
  • redis实现分布式锁
  • zookeeper实现分布式锁
  • etcd实现分布式锁
  • 一些其他的实现分布式锁的方式

2.1. 数据库实现分布式锁

  • 悲观锁
  • 乐观锁
  • 唯一索引
2.2.1 悲观锁

这就需要提到select for update了。这种是强制锁定,其他线程在锁释放前都无法操作,如果获取锁的线程有大量耗时的操作,会导致很多其他等待锁的线程超时,因为 这种锁也是不可重入的。

2.1.2 乐观锁

数据库的乐观锁一般是使用一个版本号字段来实现,或者直接使用时间戳代替。有点类似于CAS操作,但是没有CAS的重试。这种方式会在比对版本号不一致时导致一些线程 的操作直接失败。所以这种方式适用于并发量小,读操作比较多的场景。比如只有少量的几个用户或线程有修改数据的权限时。

2.1.3. 唯一索引
  1. 创建一个记录锁信息的表,用锁定的资源做为唯一索引;
  2. 加锁操作时插入一条锁信息记录;
  3. 释放锁时删除该记录。

特点:锁不可重入,同一个线程在没有释放锁之前无法获得锁。而且锁不会自动失效,需要手动释放。

2.2. 基于redis实现分布式锁

2.2.1. 基于setnx命令和lua脚本:
代码语言:javascript
复制
- 获取锁(unique_value可以是UUID等)SET resource_name unique_value NX PX 30000- 释放锁(lua脚本中,一定要比较value,防止误解锁)if redis.call("get",KEYS[1]) == ARGV[1] then    return redis.call("del",KEYS[1])else    return 0end
  • SETNX:SETNX key val:当且仅当key不存在时,set一个key为val的字符串,返回1;若key存在,则什么都不做,返回0。这里的val代表的是资源的唯一标识。
  • expire:expire key timeout:为key设置一个超时时间,单位为second,超过这个时间锁会自动释放,避免死锁。
  • delete:delete key:删除key,删除时要根据资源的唯一标识与val进行比对,判断是否是该资源的锁,如果是则执行delete操作进行锁释放。

这种方式的最大缺点是,加锁时只作用在一个redis节点上,当redis通过sentinel保证高可用,如果这个master节点由于某些原因发生了主从切换,那么就会出现锁丢失的情况。一个线程在Redis的master节点上拿到了锁,但是这个加锁的key还没有同步到slave节点,如果这时master故障,发生故障转移,slave节点升级为master节点就会导致锁丢失。

2.2.2. 实现redlock算法的redisson

Redisson是一个在Redis的基础上实现的Java驻内存数据网格(In-Memory Data Grid)。它不仅提供了一系列的分布式的Java常用对象,还提供了许多分布式服务。其中包括(BitSet, Set, Multimap, SortedSet, Map, List, Queue, BlockingQueue, Deque, BlockingDeque, Semaphore, Lock, AtomicLong, CountDownLatch, Publish / Subscribe, Bloom filter, Remote service, Spring cache, Executor service, Live Object service, Scheduler service) Redisson提供了使用Redis的最简单和最便捷的方法。Redisson的宗旨是促进使用者对Redis的关注分离(Separation of Concern),从而让使用者能够将精力更集中地放在处理业务逻辑上。

redlock是让获取锁的客户端获取当前的unix时间,尝试从n个redis实例中用同一个key和val为资源唯一标识获取锁。并且设置有获取锁的超时时间。当且仅当超过n/2 + 1的节点都获取到锁,并且使用的时间小于锁失效时间时,锁才算获取成功。如果锁获取失败,应该去所有的redis实例上进行解锁。

具体的redlock实现参考redisson的RLock。

2.3. 基于zookeeper实现分布式锁

  • 在zk上创建临时有序节点排序后,watch比自己小1的节点等待获取锁
  • 在zk 上创建相同名称的节点,创建成功的获得锁。
  • 具体可以使用Curator进行相关实现

参考:https://mp.weixin.qq.com/s?_biz=MzI1NDU0MTE1NA==&mid=2247483862&idx=1&sn=914d7912c7313123897ef479f0bd7f80&chksm=e9c2eddbdeb564cd58923f395441996332fb9332d8c53fc6368a96a19b6e59297020e6f47537&scene=21&xtrack=1&key=e3977f8a79490c63bdcb6c58fa99576e80eb3c7db7fe393f6039d192362c395566c47cd13a582277c96064472e8271f63879aadb0c8c0de6f81dcc876c06e46d50130699ad56ba62693f29e202a34258&ascene=1&uin=MjI4MTc0ODEwOQ==&devicetype=Windows%207&version=62060719&lang=zhCN&passticket=HBqvqAg8/E8F0PgXE4QY26oVnramk4zVwRSUT2QD%20BrblglnRt6GwnH4nA%20OLM//#wechatredirect

2.4. 基于etcd实现分布式锁

因为 etcd 使用 Raft 算法保持了数据的强一致性,某次操作存储到集群中的值必然是全局一致的,所以很容易实现分布式锁。etcd有一套实现了CAS的API,可以保持独占。etcd还有一套自动创建有序键的API,可以 控制获取锁的时序,所有试图获取锁的用户都会进入等待队列,保证获得锁的顺序全局唯一。另外etcd可以进行节点的事件监听和节点的主动释放及超时自动释放等,都是它能实现分布式锁的前提条件。参考:https://segmentfault.com/a/1190000014297365

2.5. 其他的实现分布式锁的方案

比如memcache的方式:memcache实现了CAS,也是它能来做分布式锁的前提条件,参考:https://www.zhihu.com/question/25489362

3. 主流使用的分布式锁

目前主流使用的分布式锁以redis居多,高可用环境下的redis一般RedLock使用比较多。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-06-27,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 开发架构二三事 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. 为什么要有分布式锁
  • 2. 分布式锁的实现方式
    • 2.1. 数据库实现分布式锁
      • 2.2.1 悲观锁
      • 2.1.2 乐观锁
      • 2.1.3. 唯一索引
    • 2.2. 基于redis实现分布式锁
      • 2.2.1. 基于setnx命令和lua脚本:
      • 2.2.2. 实现redlock算法的redisson
    • 2.3. 基于zookeeper实现分布式锁
      • 2.4. 基于etcd实现分布式锁
        • 2.5. 其他的实现分布式锁的方案
        • 3. 主流使用的分布式锁
        相关产品与服务
        云数据库 Redis
        腾讯云数据库 Redis(TencentDB for Redis)是腾讯云打造的兼容 Redis 协议的缓存和存储服务。丰富的数据结构能帮助您完成不同类型的业务场景开发。支持主从热备,提供自动容灾切换、数据备份、故障迁移、实例监控、在线扩容、数据回档等全套的数据库服务。
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档