分布式锁¶
分布式锁是一种用于在分布式系统中同步访问共享资源的机制。随着现代应用架构向微服务和分布式系统的转变,分布式锁成为确保数据一致性和防止竞争条件的关键工具。
使用场景¶
- 确保数据一致性:在分布式系统中,多个节点可能会同时尝试修改共享资源,如数据库中的数据。使用分布式锁可以确保一次只有一个节点能进行修改,从而保持数据一致性。
- 防止重复处理:在一些需要确保任务只被执行一次的场景中,分布式锁可以防止多个节点重复执行同一个任务。
- 顺序执行:当多个操作需要按照特定顺序执行时,分布式锁可以用来控制执行的顺序。
实现方式¶
Redis¶
如果只想在“尽力而为”的基础上上锁,而不是想要绝对正确性,可以使用Redis的简单单节点锁定算法。
- 获取锁:使用SET命令加上NX(只在键不存在时设置键)和EX(设置键的过期时间)选项尝试设置一个锁键。如果命令返回成功,表示获取锁成功。
- 保持锁:在持有锁的客户端执行操作期间,可以通过定期更新键的过期时间来保持锁。
- 释放锁:操作完成后,使用DEL命令删除锁键来释放锁。
- 安全释放锁:为了安全释放锁,客户端在设置锁的时候应该给锁赋予一个唯一值。释放锁之前先检查这个值,确认自己是锁的持有者,然后再删除锁键。这通常通过Lua脚本的原子操作完成。
注意,这个方法不能获得绝对正确性,因为由于 Stop the-world GC pause 的存在,即使在进行实际操作前检查锁是否依然处于有效期,也无法绝对保证锁的所有权。也因为这个原因,Redis官网提出的认为在集群部署下可以保证绝对正确性的 Redlock 算法被 How to do distributed locking — Martin Kleppmann’s blog 这篇博文质疑。博文作者提出可以用 Fencing 来确保锁的安全,即客户端每次获取锁的时候会得到一个自增的 Fencing token,客户端执行操作的时候会将 token 带上,被操作的系统会检查token的值是否比上一次操作的token的值大,如果不是则拒绝操作请求。Redisson的 RFencedLock 实现了这个算法。但这个算法要求被操作系统额外“记住” token 的信息,在很多情况下并不可行。
数据库¶
使用数据库主键唯一性实现分布式锁。
- 锁记录:在数据库中创建一个特定的表,用于存储锁的信息,如锁标识、持有者、过期时间等。
- 获取锁:尝试在锁表中插入一条记录。如果插入成功(即没有其他事务已经插入了相同的锁标识的记录),则表示获取锁成功。
- 释放锁:操作完成后,删除或更新锁表中的记录来释放锁。
- 处理死锁:设置一个合理的过期时间,并定期检查过期的锁,以防止死锁情况的发生。
使用数据库行锁或表锁,利用数据库事务的互斥性实现分布式锁。
- 获取锁:
SELECT * from database_lock_table WHERE id=xx FOR UPDATE;
- 释放锁:
COMMIT
ZooKeeper¶
TBD
Hazelcast¶
TBD
Etcd¶
TBD