1. 分布式锁
在同一个JVM内部,大家往往采用synchroinized或者Lock的方式来解决多线程间的安全问题,但是在分布式架构下,在JVM之间,那么需要一种更加高级的锁机制,来处理跨JVM进程之间的线程安全问题,解决方案就是,使用分布式锁
1.1 Redis分布式锁原理
借助setnx和expire两个命令完成
setnx 当key不存在,将key设置为value,存在不做任何操作,返回0 expire 设置key过期时间
- key不存在时创建,并设置value和过期时间,返回值为1,成功获取到锁
- 如果key存在时直接返回0, 抢锁失败
- 持有锁的线程释放锁时手动删除key,或者过期时间到,key自动删除,锁释放
1.2 加锁的问题
setnx成功 expire失败,如果没有手动释放,那么这个锁永远被占用,其他线程永远抢不到锁
解决方案
- 使用set的命令时,同时设置过期时间,命令 set lock "1234" EX 100 NX
- 使用lua脚本,将加锁的命令放在lua脚本中原子性执行
1.2 Jedis分布式锁实现
1.3 锁过期问题
锁过期:预估业务操作10秒钟,锁设置20秒,各种元英,比如STW问题,业务操作执行超过20秒,业务会在无锁状态下运行,就会发生数据混乱 解决方案:
- 乐观锁方式,增加版本号 (增加版本号需要调整业务逻辑,与之配合,所以会入侵代码)
- watch dog自动延期(客户端一旦加锁成功,就会启动一个watch dog看门狗,他是一个后台线程,会每隔10秒检查一下,如果客户端还持有锁key,那么就会不断延长锁key的生存时间)
2. Redission分布式锁
2.1 简介
锁的存储结构是Hash key :锁的名字 字段: UUID+threadID 值: 表示重入的次数
2.2 加锁原理
2.3 释放锁原理
- 判断KEY是否存在
- 如果不存在,返回nil
- 如果存在,使用hincrby-1 减1
- 减完后,counter>0值仍大于0,则返回0
- 减完后,counter<=0 删除key
- 用publish广播释放消息
2.4 watch dog 自动延期
3 分段锁
可以使用分段的方式(思想来源map、reduce, ConcurrentHashMap),以空间换时间,为了达到每秒600个订单,可以将锁分成600/5=120个段,反过来,每个段1秒可以操作5次,120个段,每秒操作600次