T31-Redis分布式锁学习笔记

267 阅读2分钟

1. 分布式锁

在同一个JVM内部,大家往往采用synchroinized或者Lock的方式来解决多线程间的安全问题,但是在分布式架构下,在JVM之间,那么需要一种更加高级的锁机制,来处理跨JVM进程之间的线程安全问题,解决方案就是,使用分布式锁

1.1 Redis分布式锁原理

借助setnx和expire两个命令完成

setnx 当key不存在,将key设置为value,存在不做任何操作,返回0 expire 设置key过期时间

  1. key不存在时创建,并设置value和过期时间,返回值为1,成功获取到锁
  2. 如果key存在时直接返回0, 抢锁失败
  3. 持有锁的线程释放锁时手动删除key,或者过期时间到,key自动删除,锁释放

1.2 加锁的问题

setnx成功 expire失败,如果没有手动释放,那么这个锁永远被占用,其他线程永远抢不到锁

解决方案

  1. 使用set的命令时,同时设置过期时间,命令 set lock "1234" EX 100 NX
  2. 使用lua脚本,将加锁的命令放在lua脚本中原子性执行

1.2 Jedis分布式锁实现

image.png

1.3 锁过期问题

锁过期:预估业务操作10秒钟,锁设置20秒,各种元英,比如STW问题,业务操作执行超过20秒,业务会在无锁状态下运行,就会发生数据混乱 解决方案:

  1. 乐观锁方式,增加版本号 (增加版本号需要调整业务逻辑,与之配合,所以会入侵代码)
  2. watch dog自动延期(客户端一旦加锁成功,就会启动一个watch dog看门狗,他是一个后台线程,会每隔10秒检查一下,如果客户端还持有锁key,那么就会不断延长锁key的生存时间)

image.png

2. Redission分布式锁

2.1 简介

锁的存储结构是Hash key :锁的名字 字段: UUID+threadID 值: 表示重入的次数

2.2 加锁原理

image.png

2.3 释放锁原理

image.png

  1. 判断KEY是否存在
  2. 如果不存在,返回nil
  3. 如果存在,使用hincrby-1 减1
  4. 减完后,counter>0值仍大于0,则返回0
  5. 减完后,counter<=0 删除key
  6. 用publish广播释放消息

2.4 watch dog 自动延期

image.png

3 分段锁

可以使用分段的方式(思想来源map、reduce, ConcurrentHashMap),以空间换时间,为了达到每秒600个订单,可以将锁分成600/5=120个段,反过来,每个段1秒可以操作5次,120个段,每秒操作600次 image.png