关于分布式锁的知识点
本地锁还是分布式锁的区别?
本地锁和分布式锁都是用于多线程或多进程环境下对共享资源进行保护的机制,但它们的实现方式和应用场景有所不同。下面是它们的主要区别:
-
实现方式:
- 本地锁:本地锁是在单个进程或单个节点内部实现的。常见的本地锁包括内置锁(synchronized关键字)和显式锁(如ReentrantLock)等。
- 分布式锁:分布式锁是在分布式系统中实现的,可用于多个进程或多个节点之间的资源竞争。常见的分布式锁实现方式包括使用共享存储(如ZooKeeper、Redis等)或基于算法的实现(如Raft、Paxos等)。
-
适用范围:
- 本地锁:本地锁适用于单个进程或单个节点内的资源竞争,通常用于控制访问共享内存或公共对象。
- 分布式锁:分布式锁适用于分布式系统中的资源竞争,可用于控制多个进程或多个节点之间的资源访问。它通常用于协调分布式系统中的并发操作,如分布式事务、分布式任务调度等。
-
锁粒度:
- 本地锁:本地锁通常是细粒度的,只锁定需要保护的共享对象或资源。
- 分布式锁:分布式锁通常是粗粒度的,涵盖更大范围的资源,如分布式系统中的某个关键操作或资源。
-
性能和可靠性:
- 本地锁:本地锁的性能通常较高,因为它们避免了分布式通信和协调的开销。但是,如果涉及到多个进程或多个节点的资源竞争时,本地锁不能提供可靠的协调机制。
- 分布式锁:分布式锁的性能较低,因为它们需要进行分布式通信和协调。但是,分布式锁可以提供更可靠的协调机制,以确保在分布式环境中的资源访问顺序和一致性。
总之,本地锁适用于单个进程或单个节点内的资源竞争,而分布式锁适用于分布式系统中的资源竞争,它们的选择取决于具体的应用场景和需求。
为了防止误删到其他的锁,这里我们建议使用 Lua 脚本通过 key 对应的 value(唯一值)来判断。 java代码实现
写一个LUA脚本
当你使用Lua脚本来实现分布式锁时,可以使用以下Lua脚本:
local lockKey = KEYS[1]
local lockValue = ARGV[1]
if redis.call('GET', lockKey) == lockValue then
return 'OK'
else
return redis.call('SET', lockKey, lockValue, 'NX')
end
上述脚本的功能是:检查给定的lockKey的值是否等于lockValue,如果相等则返回OK表示已经获取到锁,否则使用SET命令设置新的锁值,并使用NX选项确保只在键不存在时才设置锁值。
在实际使用中,你可以将上述脚本保存在一个.lua文件中,然后在Java代码中读取该文件的内容,然后传递给jedis.eval方法进行执行。
以下是一个示例的Java代码实现:
import redis.clients.jedis.Jedis;
import java.nio.file.Files;
import java.nio.file.Paths;
public class RedisDistributedLock {
private static final String LOCK_KEY = "lock_key";
private static final String LOCK_VALUE = "lock_value";
private Jedis jedis;
private String luaScript;
public RedisDistributedLock() throws Exception {
// 初始化Jedis连接,这里假设已经正确配置好了连接信息
jedis = new Jedis("localhost", 6379);
// 从文件中加载Lua脚本
luaScript = new String(Files.readAllBytes(Paths.get("path/to/your/script.lua")));
}
public boolean acquireLock() {
String result = (String) jedis.eval(luaScript, 1, LOCK_KEY, LOCK_VALUE);
return "OK".equals(result);
}
public void releaseLock() {
String luaScript = "if redis.call('GET', KEYS[1]) == ARGV[1] then return redis.call('DEL', KEYS[1]) else return 0 end";
jedis.eval(luaScript, 1, LOCK_KEY, LOCK_VALUE);
}
public static void main(String[] args) throws Exception {
RedisDistributedLock lock = new RedisDistributedLock();
try {
if (lock.acquireLock()) {
// 获取到锁后执行需要保护的代码,确保在执行期间不会被其他线程或进程删除
// ...
} else {
// 获取锁失败的处理逻辑
// ...
}
} finally {
lock.releaseLock();
}
}
}
在示例代码中,我们将Lua脚本保存在一个文件中,然后通过Java的文件操作API(Files.readAllBytes和Paths.get)读取脚本的内容,并将其作为参数传递给jedis.eval方法来执行Lua脚本。你需要将path/to/your/script.lua替换为实际你保存Lua脚本的路径。
请注意,示例代码仅供参考,实际使用中你可能需要根据你的具体需求对其进行修改和适配。同时,确保在异常情况下能够正确释放锁,以避免出现死锁的情况。
Lua 脚本是为什么保证解锁操作的原子性?
Lua脚本在这里保证解锁操作的原子性,是因为在执行Lua脚本时,Redis会将整个脚本作为一个整体在服务器端执行,这确保了脚本中包含的一系列命令以原子操作的方式执行。
针对分布式锁的解锁操作,一般需要先检查当前锁的值是否与预期值相等,如果相等再执行删除锁的操作。通过把这两个步骤整合到一个Lua脚本中,可以保证这两个步骤作为一个整体进行执行,即使在高并发的情况下,也能保证这两个动作的原子性。
具体而言,Redis会使用类似于乐观锁的方式来确保Lua脚本的原子性。在执行Lua脚本期间,即使有其他客户端同时请求执行相同的Lua脚本,Redis会确保只有一个客户端能够成功执行完成整个脚本,而其他客户端会在执行过程中被阻塞,直到当前脚本执行完成。
因此,使用Lua脚本可以保证分布式锁的加锁和解锁操作是原子性的,从而确保了分布式锁在并发情况下的正确性和可靠性。

浙公网安备 33010602011771号