集群环境下不能使用 jvm 级别的锁,而是需要使用 分布式锁
分布式系统思路
1、MysqL 方案。不能重复插入有唯一约束的数据。PK、UK
MysqL 可抗 10w QPS,关键看配置
实际中用的不多,中小型公司使用
https://help.aliyun.com/document_detail/109378.html
2、Redis 方案
加锁:SETNX
key value
放锁:del
key
一步一步简单实现:
使用 redis 简单实现分布式锁
使用 setnx 操作
SETNX(key, value) :如果存在则返回false,如果不存在则放入则 true
类似乐观锁
利用了 redis 单线程的特点
fun() {
result = setnx(key, value);
if (result == false) return;//没拿到则返回
拿到则执行操作.
delete(key);
}
出现的问题:
- 程序抛异常:使用 try-finally 最终删除 key
- 系统宕机、进程被
kill
:设置超时时间 expire,expire+setnx 的原子操作
设置超时时间。小型规模应用默认 10s 业务可以处理完
try {
redis.setIfAbseat(key, 10, value);
}finally{
redis.delete(key)
}
如果是高并发情况,可能会出现超过10s的情况,会出现严重错误。当前线程删除其他线程的 lock
继续完善:
try {
redis.setIfAbsent(key, 10, uuid);
} finally {
if(redis.get(key).equals(uuid)) {//此处有原子问题
redis.delete(key)
}
}
思考:10s是否够用?可能不够用,设置成30s?是否够用?
解决方法:锁续命,使用一个附加线程定时检查剩余时间,如果还持有锁则增加超时时间。直到业务处理完其他线程可以获得锁。
如果从头开始写,很容易出现bug。
Redis 单节点故障
使用集群:一主,二从,三哨兵
主从存在的问题:
服务拿到锁后主节点挂了,数据还没同步到从节点,
从节点选举为主节点后,加锁成功,导致两个线程拿到一把锁,导致超卖
解决方案:red lock 红锁
使用 5 台 redis(无主从之分),服务需要拿到一半以上的(>=3)的锁时才算加锁成功
尽量不使用偶数台 redis。
-
挂了一台没有加锁的怎么办,不对获取锁的业务线程产生影响,可以当他还是启动的
-
挂了一台加锁的(正在使用的)怎么办?延迟启动,如果挂了又立马启动,可能会导致其他线程拿到一半以上的锁,破坏了互斥条件,导致超卖。
延迟启动: 至少等到加锁的业务线程结束后再启动挂掉的 redis
JVM 中 STW 导致 Redis 锁释放怎么办
STW 过程中 “看门狗”(即守护线程)不去续期,导致锁失效。此时别的线程拿到锁,同时操作产生问题
实际:好多公司只用一台 Redis
总结
- 单台服务器,使用 JVM 锁
- 多台服务器,分布式锁
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。