Java主流分布式解决方案多场景设计与实战(完结)
[- Java主流分布式解决方案多场景设计与实战(完结)--- “夏のke” ---789it--.--top/2315/]
谈谈分布式锁与它的那些应用场景
分布式锁是控制分布式系统中不同节点对共享资源访问的一种机制,确保在任何时刻只有一个节点可以访问特定的资源,避免数据不一致和并发冲突等问题。以下是分布式锁的常见应用场景:
- 资源访问控制
-
- 数据库并发操作:在分布式系统中,多个应用实例可能同时访问数据库。例如,在电商系统中,多个服务器节点可能同时处理商品库存的扣减操作。使用分布式锁可以确保在同一时刻只有一个节点能够执行库存更新操作,防止超卖现象的发生。
- 文件系统访问:当多个节点需要访问共享文件系统中的同一文件时,分布式锁可以保证在任何时刻只有一个节点对文件进行写操作,避免文件内容被破坏或数据不一致。比如,多个服务器节点可能需要向同一个日志文件中写入日志信息,通过分布式锁可以确保日志文件的完整性和正确性。
- 任务调度
-
- 定时任务:在分布式环境下,可能存在多个节点都部署了定时任务,如每天凌晨的数据库备份任务。如果不进行控制,可能会导致多个节点同时执行备份任务,造成资源浪费和数据冲突。分布式锁可以确保只有一个节点能够执行定时任务,其他节点在获取锁失败后会等待或放弃执行。
- 分布式工作流:在分布式工作流系统中,不同的任务可能需要在不同的节点上执行,并且需要按照一定的顺序进行。分布式锁可以用于控制任务的执行顺序,确保每个任务在获取到相应的锁后才能执行,从而保证工作流的正确性和一致性。
- 缓存数据一致性
-
- 缓存更新:在分布式系统中,缓存通常被多个节点共享。当缓存中的数据过期或需要更新时,多个节点可能同时检测到并尝试更新缓存。使用分布式锁可以保证只有一个节点能够执行缓存更新操作,其他节点在等待锁释放后可以直接从缓存中获取最新数据,避免缓存数据的不一致性。
- 缓存预热:在系统启动或某些特定情况下,需要对缓存进行预热,即提前将一些数据加载到缓存中。分布式锁可以确保在缓存预热过程中,只有一个节点负责加载数据,避免多个节点重复加载数据,提高缓存预热的效率。
- 分布式事务
-
- 保证数据一致性:在分布式事务中,可能涉及多个不同的服务或数据库操作。分布式锁可以用于在事务执行过程中对相关资源进行锁定,确保在事务提交或回滚之前,其他事务无法访问这些资源,从而保证分布式事务的一致性。例如,在一个涉及多个微服务的订单处理系统中,使用分布式锁可以确保在订单创建、库存扣减和支付等操作过程中,相关数据的一致性。
实现分布式锁的方式有多种,常见的有基于数据库、基于缓存(如 Redis)和基于分布式协调服务(如 Zookeeper)等方式,不同的实现方式在性能、可靠性和复杂性等方面各有特点,需要根据具体的应用场景和需求进行选择。
分布式锁的加锁和解锁原理是什么?
分布式锁旨在确保在分布式系统里,同一时刻仅一个客户端能对共享资源进行访问。以下为你详细阐述常见的基于 Redis 和 ZooKeeper 实现的分布式锁的加锁与解锁原理。
基于 Redis 实现的分布式锁
加锁原理
Redis 实现分布式锁的核心是利用
SET
命令的原子性。
SET
命令可在设置键值的同时设置过期时间,防止因客户端异常崩溃而导致锁无法释放,造成死锁。示例命令如下:
plaintext
SET lock_key unique_value NX PX expire_time
lock_key:代表锁的键,不同的业务场景需使用不同的锁键,从而保证锁的独立性。unique_value:是客户端生成的唯一值,用于标识该锁是由哪个客户端加的,这样在解锁时能确保只有加锁的客户端才能解锁。NX:表示仅当键不存在时才设置成功,也就是只有一个客户端可以成功设置该键,实现了锁的互斥性。PX expire_time:为键设置过期时间,单位是毫秒。这样即便客户端在持有锁期间崩溃,锁也会在过期后自动释放,避免死锁。
解锁原理
解锁时要保证原子性,可使用 Lua 脚本来实现。因为 Lua 脚本在 Redis 中是原子执行的,能避免在判断和删除操作之间出现并发问题。示例 Lua 脚本如下:
lua
if redis.call("get",KEYS[1]) == ARGV[1] then return redis.call("del",KEYS[1])else return 0end
- 脚本会先检查锁的键对应的值是否与客户端传入的唯一值相等,若相等,就表明该客户端持有此锁,可执行删除操作来释放锁;若不相等,就返回 0,表示解锁失败。
基于 ZooKeeper 实现的分布式锁
加锁原理
ZooKeeper 是一个分布式协调服务,其数据结构是树形的,节点可分为持久节点、临时节点、顺序节点等。利用 ZooKeeper 实现分布式锁的步骤如下:
- 客户端在特定的锁节点下创建一个临时顺序节点。
- 客户端获取该锁节点下的所有子节点,然后对这些子节点按顺序排序。
- 客户端检查自己创建的节点是否是序号最小的节点,若为最小节点,则表示成功获取到锁;若不是最小节点,则监听比自己序号小一位的节点的删除事件。
解锁原理
当客户端完成操作后,只需删除自己创建的临时顺序节点即可释放锁。一旦节点被删除,那些监听该节点删除事件的客户端会收到通知,然后重新检查自己是否成为了序号最小的节点,若成为最小节点,就可获取锁。
两种实现方式的对比
- Redis:性能较高,适合对性能要求高、并发量大的场景,但在极端情况下(如主从切换)可能会出现锁丢失问题。
- ZooKeeper:可靠性强,能保证锁的强一致性,不过性能相对 Redis 较低,更适合对可靠性要求高的场景。