面试官:分布锁有哪些解决方案?

  1. Reids的分布式锁,很多大公司会基于Reidis做扩展开发。setnx key value ex 10s,Redisson。

  2. 基于Zookeeper。临时节点,顺序节点。

  3. 基于数据库,比如Mysql。主键或唯一索引的唯一性。

image.png
image.png

***********************

面试官:Redis如何做分布式锁?

假设有两个服务A、B都希望获得锁,执行过程大致如下:

Step1: 服务A为了获得锁,向Redis发起如下命令: SET productId:lock 0xx9p03001 NX EX 30000 其中,"productId"由自己定义,可以是与本次业务有关的id,"0xx9p03001"是一串随机值,必须保证全局唯一,“NX"指的是当且仅当key(也就是案例中的"productId:lock”)在Redis中不存在时,返回执行成功,否则执行失败。"EX 30000"指的是在30秒后,key将被自动删除。执行命令后返回成功,表明服务成功的获得了锁。

Step2: 服务B为了获得锁,向Redis发起同样的命令: SET productId:lock 0000111 NX EX 30000 由于Redis内已经存在同名key,且并未过期,因此命令执行失败,服务B未能获得锁。服务B进入循环请求状态,比如每隔1秒钟(自行设置)向Redis发送请求,直到执行成功并获得锁。

Step3: 服务A的业务代码执行时长超过了30秒,导致key超时,因此Redis自动删除了key。此时服务B再次发送命令执行成功,假设本次请求中设置的value值为0000222。此时需要在服务A中对key进行续期,watch dog。

Step4: 服务A执行完毕,为了释放锁,服务A会主动向Redis发起删除key的请求。注意: 在删除key之前,一定要判断服务A持有的value与Redis内存储的value是否一致。比如当前场景下,Redis中的锁早就不是服务A持有的那一把了,而是由服务2创建,如果贸然使用服务A持有的key来删除锁,则会误将服务2的锁释放掉。此外,由于删除锁时涉及到一系列判断逻辑,因此一般使用lua脚本,具体如下:

if redis.call("get", KEYS[1])==ARGV[1] then return redis.call("del", KEYS[1]) else return 0 end 
image.png
image.png

面试官:MySQL如何做分布式锁?

在Mysql中创建一张表,设置一个 主键或者UNIQUE KEY 这个 KEY 就是要锁的 KEY(商品ID),所以同一个 KEY 在mysql表里只能插入一次了,这样对锁的竞争就交给了数据库,处理同一个 KEY 数据库保证了只有一个节点能插入成功,其他节点都会插入失败。
DB分布式锁的实现:通过主键id 或者 唯一索性 的唯一性进行加锁,说白了就是加锁的形式是向一张表中插入一条数据,该条数据的id就是一把分布式锁,例如当一次请求插入了一条id为1的数据,其他想要进行插入数据的并发请求必须等第一次请求执行完成后删除这条id为1的数据才能继续插入,实现了分布式锁的功能。
这样 lock 和 unlock 的思路就很简单了,伪代码:

def lock exec sql: insert into locked—table (xxx) values (xxx) if result == true : return true else : return false def unlock exec sql: