保证锁可靠性的措施
- 使用SETNX + EX(PX)组合:使用
SET key value NX EX seconds
(或SET key value NX PX milliseconds
)命令。SETNX
确保只有在键不存在时才设置值,实现互斥;EX
(或PX
)设置锁的过期时间,防止死锁。
- UUID防误解锁:每个获取锁的客户端生成一个唯一的UUID作为锁的值。解锁时,先检查锁的值是否与自己的UUID一致,只有一致时才执行删除操作,防止误删其他客户端的锁。可以使用Lua脚本来保证检查和删除操作的原子性,如:
if redis.call("GET", KEYS[1]) == ARGV[1] then
return redis.call("DEL", KEYS[1])
else
return 0
end
- 多节点部署(Redlock算法):对于网络抖动和节点故障,采用Redlock算法。它基于多个独立的Redis节点,客户端需在大多数(N/2 + 1,N为节点总数)节点上成功获取锁才算获取成功。释放锁时,需要向所有节点发送释放锁的请求。示例代码(Python + redlock-py库):
from redlock import Redlock
# 定义Redis节点
redis_client1 = Redis(host='localhost', port=6379)
redis_client2 = Redis(host='localhost', port=6380)
redis_client3 = Redis(host='localhost', port=6381)
redlock = Redlock([redis_client1, redis_client2, redis_client3])
lock = redlock.lock("resource_name", 1000) # 尝试获取锁,锁持有时间1000毫秒
if lock:
try:
# 执行业务逻辑
pass
finally:
redlock.unlock(lock) # 释放锁
优化性能的措施
- 减少网络开销:尽量减少获取和释放锁的网络请求次数。可以批量处理一些操作,避免频繁的锁获取和释放。
- 合理设置过期时间:根据业务实际情况合理设置锁的过期时间,避免过期时间过长导致性能下降,或过短导致锁频繁失效。对于一些复杂且耗时较长的业务,可以在锁快要过期时,使用Lua脚本进行续期操作。
- 使用连接池:在客户端使用连接池来管理与Redis的连接,减少连接创建和销毁的开销,提高并发性能。例如在Java中使用Jedis连接池:
JedisPoolConfig config = new JedisPoolConfig();
JedisPool jedisPool = new JedisPool(config, "localhost", 6379);
try (Jedis jedis = jedisPool.getResource()) {
String result = jedis.set("lock_key", "lock_value", "NX", "EX", 10);
if ("OK".equals(result)) {
// 获取锁成功,执行业务逻辑
}
} catch (Exception e) {
e.printStackTrace();
} finally {
jedisPool.close();
}
- 采用异步操作:在释放锁等操作时,可以考虑使用异步方式,减少主线程等待时间,提高整体性能。例如在Node.js中使用
async
和await
结合redis
模块实现异步释放锁:
const redis = require('redis');
const client = redis.createClient();
async function releaseLock(key, value) {
const script = `if redis.call("GET", KEYS[1]) == ARGV[1] then return redis.call("DEL", KEYS[1]) else return 0 end`;
return new Promise((resolve, reject) => {
client.eval(script, 1, key, value, (err, reply) => {
if (err) {
reject(err);
} else {
resolve(reply);
}
});
});
}
// 获取锁并执行业务逻辑后,异步释放锁
releaseLock('lock_key', 'lock_value').then(() => {
console.log('锁已成功释放');
}).catch((err) => {
console.error('释放锁失败', err);
});