您的位置:首页 > 汽车 > 新车 > 中国航发网上商城首页_安徽省工程建设信息网查询信息_产品推广建议_百度营销客户端

中国航发网上商城首页_安徽省工程建设信息网查询信息_产品推广建议_百度营销客户端

2024/12/22 18:33:09 来源:https://blog.csdn.net/fyh2944163240/article/details/142964024  浏览:    关键词:中国航发网上商城首页_安徽省工程建设信息网查询信息_产品推广建议_百度营销客户端
中国航发网上商城首页_安徽省工程建设信息网查询信息_产品推广建议_百度营销客户端

72 分布式锁

什么是分布式锁

分布式锁 = 分布式 + 锁。那么分布式是指的什么呢?锁又是锁的谁呢?在业务开发中我们经常会听到分布式分布式的概念,分布式也很简单,通俗的来说就是你具有多个服务器,每个服务器上运行的程序是一样的,用户的每一次请求,都会平衡的分配到随机的一个服务器中进行处理。那么这样的话就会导致一个问题,那就是并发冲突和数据不一致等问题。下面结合一个简单的小例子来介绍一下分布式环境下不使用分布式锁会造成什么问题。

假设你运营着一个在线电商平台,某个商品(商品ID为 product123)的库存为10件。当用户在下单时,系统会检查库存,确定库存是否充足,然后减少库存并生成订单。为了应对大量并发请求,你的系统使用了多台服务器来处理订单请求。

用户1(请求在服务器A上处理)和 用户2(请求在服务器B上处理)同时发出购买请求。当前库存:10件

服务器A接收到用户1的请求,并查询数据库中的库存,此时库存为10件。

服务器B也接收到用户2的请求,并同时查询数据库中的库存,此时库存也是10件。

此时,两个服务器节点都“看到”库存为10件。

服务器A为用户1生成了订单,并将库存减少1。库存应减少为9件。

与此同时,服务器B为用户2生成了订单,并将库存也减少1,认为库存现在也是9件。

  • 服务器A将新的库存9件更新回数据库。
  • 服务器B将新的库存9件(根据它的视角)更新回数据库。

最终结果:虽然两个用户下单并减少库存,但两台服务器并没有意识到对方的操作,因此它们都认为库存应该从10减少到9,而不是从10减少到8。

实际发生的问题是,两个用户都成功购买了商品,但库存实际上只减少了1件,而不是2件。这就是库存超卖的现象。

库存最终显示为9件,而实际上它应该显示为8件,意味着库存被错误地记录,系统允许用户购买超过实际库存量的商品。

分布式锁保证了在分布式系统环境下,一个方法在同一时间只能被一个机器的一个线程执行;

分布式锁应该具备的品质

课本上的概念直接列出,让人看了好似懂了又好似没懂。分布式锁简单的来说就是为了在不同服务器上的相同方法只能有一个在某一时刻运行就是了,我们需要锁住一些资源,让其达到这种效果,无论是什么,都会涉及到六个字:高可用高性能,这个也不例外。这个分布式锁需要保证高可用的获取锁和释放锁,也需要保证高性能的获取锁和释放锁。同时为了避免一些意外情况导致某线程一直占用锁,我们需要保证分布式锁具有过期时间,从而避免死锁。

  • 1、在分布式系统环境下,一个方法在同一时间只能被一个机器的一个线程执行;
  • 2、高可用的获取锁与释放锁;
  • 3、高性能的获取锁与释放锁;
  • 4、具备可重入特性;
  • 5、具备锁失效机制,防止死锁;
  • 6、具备非阻塞锁特性,即没有获取到锁将直接返回获取锁失败。

实现方式

目前我们接触到的很多大型网站都是基于分布式环境下开发的,但是有一个理论被称为CAP理论,这个理论告诉我们,**任何一个分布式系统都无法同时保证系统的一致性、可用性和分区容错性。**所以很多分布式系统都是选择牺牲一定的强一致性来得到高可用和高性能这一目标,它们选择系统只要保证最终一致性即可。

在很多的分布式场景中,为了达到最终一致性需要很多的技术支持,比如:分布式事务和分布式锁等等。这里我们先详细的介绍一下分布式锁的实现方式,分布式锁的实现一般分为三个大类:

  1. 基于数据库实现分布式锁;
  2. 基于缓存实现分布式锁;
  3. 基于ZooKeeper实现分布式锁;

接下来我们一一介绍。

在介绍之前,我们先仔细思考一下锁的概念,什么是锁呢?锁只是一个概念,它不涉及到一个具体的事物。任何只要能保证唯一性的都可以作为锁,我们仔细想想什么能保证唯一性呢?没错,就是数据库中的ID和Redis中的setnx,只要插入了两个一样的就会失败,这就是等价于获取锁失败了。

基于数据库实现分布式锁

通过上面的介绍,那么这个就很简单了,就是创建一个表,这个表就是为了分布式系统用来获取锁的,比如就创建了这个:

CREATE TABLE `distributed_lock` (`lock_key` VARCHAR(64) NOT NULL PRIMARY KEY,`lock_value` VARCHAR(64) NOT NULL,`expires_at` TIMESTAMP NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

如果这个线程想获取到这个锁,就在这个表中插入一条数据,其中lock_key为该线程需要对这个数据进行操作的ID,比如订单ID等等

在每个分布式节点中,当需要对某个共享资源进行操作时,我们会尝试向 distributed_lock 表中插入一条记录。如果插入成功,则表示获取了锁,可以安全地执行操作。如果插入失败,则说明锁已被其他节点占用。

INSERT INTO `distributed_lock` (`lock_key`, `lock_value`, `expires_at`)
VALUES ('product_123', 'UUID-xxxx', DATE_ADD(NOW(), INTERVAL 10 SECOND))
ON DUPLICATE KEY UPDATE`lock_value` = IF(`expires_at` < NOW(), 'UUID-xxxx', `lock_value`),`expires_at` = IF(`expires_at` < NOW(), DATE_ADD(NOW(), INTERVAL 10 SECOND), `expires_at`);

到了评价这个方法的优劣的时候了,首先这个是基于MySQL的,那就是外存中的,不用说效率低,效率低带来的结果就是并发性不高,就不适合高并发的情况,但是该方法方便,但是一般不用。

基于 MySQL 的分布式锁适合那些并发量不大、锁的粒度较粗的场景,如任务调度、定时任务等。对于高并发、低延迟要求较高的场景,建议使用更高效的分布式锁实现,如 Redis 或 ZooKeeper。

ON DUPLICATE KEY UPDATE:这个语句的作用是在插入新记录时,如果 lock_key 已经存在(比如因为主键或唯一约束冲突),则不会重新插入,而是执行更新操作。换句话说,如果已经有人持有了锁,这里会判断锁是否已经过期,如果过期了就让当前请求获取锁。

基于Redis实现分布式锁

基于setnx实现分布式锁

set if not exist = setnx;

  1. 加锁:执行setnx,若成功再执行expire添加过期时间
  2. 解锁:执行delete命令
SETNX lock_key unique_value
EXPIRE lock_key 10

这里会存在一些问题,首先就是这事两个操作,不是原子性的,如果客户端在执行 SETNX 后崩溃,EXPIRE 命令无法执行,导致这个锁永远不会自动释放,造成死锁问题。

我们可以通过这个来解决:set nx ex

SET lock_key unique_value NX EX 10

这个是原子性的。

同时这里还有一个过期时间的问题,就是我获取到锁的这个线程,无法在过期时间内完成这个任务,导致别的线程获得了锁,从而又导致了分布式锁的问题。

我们可以通过自动续期(检查当前线程是否完成了任务,如果没有完成就自动的延长过期时间),我们也可以采用更成熟的比如redisson,该提供了更成熟的分布式锁的设计。

基于Redis的Lua脚本能力

使用 SETNX 创建锁时,如果锁被客户端成功获取并持有,当客户端任务执行完毕后需要手动释放锁。然而,如果客户端在释放锁之前崩溃或者出现了错误,可能会导致锁未被正确释放,或者其他客户端错误地释放了本不属于它的锁。

使用 DEL 命令来释放锁时,必须确保只有持有锁的客户端才可以释放它。为此,可以将 unique_value 设置为唯一值(如 UUID),并通过 Lua 脚本确保只有在持有锁的客户端才能删除锁。

if redis.call("GET", "lock_key") == "unique_value" thenreturn redis.call("DEL", "lock_key")
elsereturn 0
end
基于Redisson实现分布式锁

参考资料

  • Redis分布式锁-这一篇全了解(Redission实现分布式锁完美方案)
  • 7年一线互联网经验,超爱图解底层原理,全栈一枚

重点关注这个方式实现,在以后会经常使用这个Redisson来实现分布式锁。

基于Zookeeper实现分布式锁

在这里插入图片描述

优点:不必多说,可以保证高可用,毕竟是专业的,Curator框架已原生支持系列分布式锁命令,使用简单

缺点:也不必多说,成本高,需要单独维护一套ZK集群。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com