分布式锁简介
同一时间只允许一个住户使用。
避免重复工作,避免破坏数据正确性。
分布式锁一些条件
- 互斥:仅一个客户端持锁。
- 防止死锁:保证客户端持锁崩溃等其他情况,也可主动释放。
死锁段子:在异地需要买火车票回老家,但是身份证丢了无法购票,补办身份证又需要本人坐火车回老家户籍管理处,就这样生活太难。
- 加锁、解锁必须同一客户端。
- 容错:大部分Redis节点正常,客户端就可获取和释放锁。
常见实现方式:
- 基于Redis
- 基于Mysql
- 基于Zookeeper
Redis
1. setnx和expire的非原子性
setnx刚执行成功,还未执行expire,节点1 挂掉,出现死锁。
image.png
2. set
- set命令要用set key value px milliseconds nx;
- value要具有唯一性;
- 释放锁时要验证value值,不能误解锁;
set()得到2种结果:a,无锁,进行加锁操作,设置有效期。b,已锁不操作。
满足3个条件:互斥性:NX参数保证锁存在,不会二次调用。死锁:设置过期时间。容错性:暂考虑单机。
const IF_NOT_EXISTS = 'NX';
const MILLISECOND_EXPIRE_TIME = 'PX';
const LOCK_VALUE = 1;
//加锁
$redis->set($key, self::LOCK_VALUE , [self::IF_NOT_EXISTS, self::MILLISECOND_EXPIRE_TIME => $expire_time]);
$lua =<<<EOT
if redis.call("get",KEYS[1]) == ARGV[1] then
return redis.call("del",KEYS[1])
else
return 0
end
EOT;
$result = $redis->eval($lua, array($key, self::LOCK_VALUE), 1);
Redis分布式锁常见问题
最大的缺点-Redis主从复制: 锁丢失
- 案例:主从发生切换
- Redis在master拿到锁
- 加锁key没同步至slave
- master故障,主从迁移。slave升级为master
- 锁丢失
image.png
- Redlock算法
- 脑裂
- 系统计时