redis遍布式锁的难题与处理方式
摘要: redis遍布式锁的难题与处理方式|频道:Redis|点一下: 次遍布式锁在遍布式自然环境中,以便确保业务流程数据信息的一切正常浏览,避免出现反复恳求的难题,会应用遍布式锁来阻止事...
遍布式锁
在遍布式自然环境中,以便确保业务流程数据信息的一切正常浏览,避免出现反复恳求的难题,会应用遍布式锁来阻止事后恳求。大家先写一段不太好的业务流程编码:
public void doSomething(String userId){ User user=getUser(userId); if(user==null){ user.setUserName("xxxxx"); user.setUserId(userId); insert(user); return; update(user); }
上边的编码非常简单,查寻db中有木有相匹配的user数据信息,假如有得话,实行升级实际操作,假如沒有则插进。
大家了解,上边的编码是进程躁动不安全的,在多段程的自然环境中,便会出現难题。以便可以确保数据信息的恰当性,在单机版自然环境下,大家可使用synchronized的方式,来确保进程安全性,实际改动:
public synchronized void doSomething(String userId){ User user=getUser(userId); if(user==null){ user.setUserName("xxxxx"); user.setUserId(userId); insert(user); return; update(user); }
在单机版器的自然环境下,可以处理进程安全性的难题,那在遍布式自然环境下呢? 这一情况下必须采用遍布式锁.
遍布式锁必须依靠别的部件来完成,常见的有redis和zookeeper。下边大家就用redis的完成,来讲明下难题,遍布式锁实际的完成方式以下
public void doSomething(String userId){ String lock=RedisUtils.get("xxxx"+userId); if(StringUtils.isNotEmpty(lock)){//表明当今userId早已被锁住 return; RedisUtils.set("xxxx"+userId,userId,1000);//锁住10s User user=getUser(userId); if(user==null){ insert(user); RedisUtils.delete("xxxx"+userId); return; update(user); RedisUtils.delete("xxxx"+userId); }
上边的编码处理了在遍布式自然环境中的高并发的难题。但一样必须考虑到一个难题,假如insert实际操作和update实际操作出现异常了,遍布式锁不容易释放出来,事后的恳求还会继续被阻拦。
因此大家再提升,提升对出现异常的捕捉。
public void doSomething(String userId){ try { String lock=RedisUtils.get("xxxx"+userId); if(StringUtils.isNotEmpty(lock)){//表明当今userId早已被锁住 return; RedisUtils.set("xxxx"+userId,userId,1000);//锁住1s User user=getUser(userId); if(user==null){ insert(user); return; update(user); catch(Exception ex){ finally{ RedisUtils.delete("xxxx"+userId); }
如今即便是程序出现异常了,锁会全自动释放出来。但redis的get和set也会存有高并发难题,大家再再次提升,应用redis中的setnx方式
public void doSomething(String userId){ try { boolean lock=RedisUtils.setnx("xxxx"+userId,userId,1000);//锁住1s if(!lock){//表明当今userId早已被锁住 return; User user=getUser(userId); if(user==null){ insert(user); return; update(user); catch(Exception ex){ finally{ RedisUtils.delete("xxxx"+userId); }
上边的编码仿佛沒有甚么难题了,但也存有非常大的安全隐患。 大家剖析下,假定第一个恳求回来,实行锁住取得成功,程序刚开始运作,可是insert和update实际操作堵塞了1s,第二个恳求回来,锁的缓存文件早已到期,第二个实行锁住取得成功,这一情况下第一个恳求进行了锁被释放出来,第二个恳求的锁就被第一次恳求释放出来了,第三次的恳求便会导致进程躁动不安全难题。
如何再去提升呢?难题关键是出現在第一次恳求误删锁的难题,因此大家在清除锁的情况下要分辨可否清除。
构思:大家在锁住的情况下,value应用当今的時间戳,删掉时分辨是不是到期假如但是期也不要删掉,实际编码以下:
public void doSomething(String userId){ try { boolean lock=RedisUtils.setnx("xxxx"+userId,LocalDateTime.now(),1000);//锁住10s if(!lock){//表明当今userId早已被锁住 return; User user=getUser(userId); if(user==null){ insert(user); return; update(user); catch(Exception ex){ finally{ LocalDateTime lockTIme= RedisUtils.get("xxxx"+userId); pare(LocalDateTime.now()) 0){ //表明早已到期,能够删掉key RedisUtils.delete("xxxx"+userId); }
那样即便出現堵塞,第二次的時间戳遮盖了第一次的锁住,那样即便第一次进行了,都不会释放出来锁。
小结
之上便是本文的所有內容了,期待文中的內容对大伙儿的学习培训或是工作中具备一定的参照学习培训使用价值,感谢大伙儿对本网的适用。