1.redis实现分布式事务锁
为什么可以用redis实现分布式事务所,因为redis是单进程单线程执行的的程序。
SETNX命令(SET if Not eXists)
语法:
SETNX key value
功能:
当且仅当 key 不存在,将 key 的值设为 value ,并返回1;若给定的 key 已经存在,则 SETNX 不做任何动作,并返回0。
GETSET命令
语法:
GETSET key value
功能:
将给定 key 的值设为 value ,并返回 key 的旧值 (old value),当 key 存在但不是字符串类型时,返回一个错误,当key不存在时,返回nil。
GET命令
语法:
GET key
功能:
返回 key 所关联的字符串值,如果 key 不存在那么返回特殊值 nil 。
DEL命令
语法:
DEL key [KEY …]
功能:
删除给定的一个或多个 key ,不存在的 key 会被忽略。
通过redis这四个命令实现分布式事务锁
服务器A,服务器B
方案一
服务器A访问某个事务
执行SETNX("KEY","A")
成功则获得锁,执行业务方法,否则等待重试。
业务方法执行完毕后就执行DEL("KEY")
真的这么简单就实现了吗?
假如说服务器A执行了SETNX,在执行业务时down机了。此时不久造成死锁了。
优化方案二
服务器A通过GET("KEY")获取值
假如说是NULL,则执行SETNX("KEY",curr_time)获得锁。
否则判断值+超时时间(需要设置业务处理超时时间)小于当前时间
如果大于则回到第一步(最好等待一段时间),否则执行GETSET("KEY",curr_time)获得锁。
执行业务
执行完毕DEL("A")
解决了down机问题,假如down机超时了,就会自动强制获得锁。
新问题,假如说服务器A通过GET获得了锁,此时B也拿到了。A与B分别都判断通过了,执行GETSET同时获得了锁。
优化方案三
在执行GETSET时,将获取出来的值与上次GET的值进行对比,是否一致,假如一致,则获得锁,继续执行。否则回到第一步。
这里就解决了同时获得锁的问题。
存在新问题,假如说服务器A执行业务时超时了,然而实际上并没有超时。此时服务器B强行拿到锁,服务器A还行执行业务,B又进来了。就会出现问题。这个无法避免,需要运维人员合理配置好超时时间。配置过大会导致服务器卡顿,造成大量队列阻塞。配置过小,会导致出现事务问题,出现脏数据。等待时间也要合理配置,以及等待次数等等。