🔒

分布式锁

Date
Created
Mar 4, 2023 06:20 AM
Descrption
分布式锁应用场景和原理解析
Tags
server
go
notion image

分布式锁应用场景

电商场景下库存扣减时的并发问题

为什么需要分布式锁

在微服务的场景下,系统提供的普通锁无法满足扣减的并发问题,因为微服务部署在不同的机器上面,不同的机器无法共享系统的锁

为什么分布式锁可以解决问题

分布式锁可以在多个机器上共享,所以可以保证多个机器上的并发控制无问题

分布式锁的实现方案

基于Mysql的分布式锁:

优点:简单,不需要额外的组件,mysql的维护成本比较低
缺点:性能并不高
  • 悲观锁
  • 乐观锁
悲观锁
缺点:并发不高,容易宕机
乐观锁
缺点:并发成都比悲观锁好一些,但是也是频繁操作数据库,对服务器的压力也比较大

基于Redis的分布式锁:

用Redis来存储是否对表加锁的标志位。
注意点
需要注意的是,在获取redis的标志位也是需要保证其原子性的,可以使用Redis Setnx命令:
Redis Setnx(SET if Not eXists) 命令在指定的 key 不存在时,为 key 设置指定的值。
语法
redis Setnx 命令基本语法如下:
redis 127.0.0.1:6379> SETNX KEY_NAME VALUE
可用版本
>= 1.0.0
返回值
设置成功,返回 1 。 设置失败,返回 0 。
例子:
redis> EXISTS job # job 不存在 (integer) 0 redis> SETNX job "programmer" # job 设置成功 (integer) 1 redis> SETNX job "code-farmer" # 尝试覆盖 job ,失败 (integer) 0 redis> GET job # 没有被覆盖 "programmer"
同时redis的get和set为了保持其原子性,可以用lua脚本来实现。

分布式锁需要解决的问题:

  • 互斥性:任意时刻只能有一个客户端拥有锁,不能同时多个客户端获取
  • 安全性:锁只能被持有该锁的用户删除,而不能被其他用户删除
  • 死锁:获取锁的客户端因为某些原因儿宕机,而未能释放锁,其他客户端无法获取此锁,需要有机制来避免该类问题的发生
  • 容错:当部分节点宕机,客户端仍能获取锁或者释放锁
如何解决上述问题的发生:
  • 设置过期时间
if self.redis_client.set(self.name, 1, nx=True, ex=15): return True
但是设置过期时间还是有弊端,如果当前的线程在一段时间后没有执行完,然后key过期了:
  1. 线程不安全了
  1. 另一个线程进来以后会将当前的key给删除掉
  1. 如果当前的线程没有执行完,那这个线程还应该在适当的时候去续租,将过期时间重新设置;一般在任务的3分支2的时候将过期时间重新设置为15s
  • 还可以借助三方库来实现
 
基于redis的分布式锁的优缺点
优点:
性能高
简单
redis本身使用很频繁,这样的话不需要去额外维护一套组件
缺点:
依赖了第三方组件
单机的redis挂掉的可能性相对较高