赞
踩
互斥性
在任意时刻,只有一个客户端持有锁。
不死锁
分布式锁本质上是一个基于租约(Lease)的租借锁,如果客户端获得锁后自身出现异常,锁能够在一段时间后自动释放,资源不会被锁死。
一致性
硬件故障或网络异常等外部问题,以及慢查询、自身缺陷等内部因素都可能导致Redis发生高可用切换,replica提升为新的master。此时,如果业务对互斥性的要求非常高,锁需要在切换到新的master后保持原状态。
这个策略源于 mysql 的 mvcc 机制,使用这个策略其实本身没有什么问题,唯一的问题就是对数据表侵入较大,我们要为每个表设计一个版本号字段,然后写一条判断 sql 每次进行判断,增加了数据库操作的次数,在高并发的要求下,对数据库连接的开销也是无法忍受的。
for update
● 阻塞锁? for update语句会在执行成功后立即返回,在执行失败时一直处于阻塞状态,直到成功。
● 锁定之后服务宕机,无法释放?使用这种方式,服务宕机之后数据库会自己把锁释放掉
问题1:InnoDB 引擎在加锁的时候,只有通过索引进行检索的时候才会使用行级锁,否则会使用表级锁
解决1:添加唯一索引
问题2:MySQL 认为全表扫效率更高,比如对一些很小的表,它就不会使用索引
解决2:增大表数据
问题3:长时间占用数据库链接
向数据库插入请求信息获取锁,释放时删除
特点1:依赖数据库可用性
解决1:主备双向同步
特点2:无失效时间
解决2:定时任务清理
特点3:非阻塞
解决3:循环获取
特点4:非重入
解决4:记录主机和线程信息
特点5:非公平
解决5:中间表记录等待线程
SET resource_1 random_value NX EX 5
示例代码为resource_1这个key设置了5秒的过期时间,value作为唯一标识,如果客户端不释放这个key,5秒后key将过期,锁就会被系统回收,此时其它客户端就能够再次为资源加锁并访问资源了。
解锁需要判断当前线程是否持有这个锁再删除
解决:
用Lua脚本实现原子性
if redis.call("get",KEYS[1]) == ARGV[1] then
return redis.call("del",KEYS[1])
else
return 0
end
当客户端发现在锁的租期内无法完成操作时,就需要延长锁的持有时间,进行续租(renew)。同解锁一样,客户端应该只能续租自己持有的锁。在Redis中可使用如下Lua脚本来实现续租:
if redis.call("get",KEYS[1]) == ARGV[1] then
return redis.call("expire",KEYS[1], ARGV[2])
else
return 0
end
使用了多个 Redis 实例来实现分布式锁,这是为了保证在发生单点故障时仍然可用。
极端情况仍有可能产生问题
Zookeeper 提供了一种树形结构的命名空间,/app1/p_1 节点的父节点为 /app1。
永久节点:不会因为会话结束或者超时而消失;
临时节点:如果会话结束或者超时就会消失;
有序节点:会在节点名的后面加一个数字后缀,并且是有序的,例如生成的有序节点为 /lock/node-0000000000,它的下一个有序节点则为 /lock/node-0000000001,以此类推。
为一个节点注册监听器,在节点状态发生改变时,会给客户端发送消息。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。