您当前的位置:网站首页>天下有情人,Redis深度探险:千帆竞发,分布式锁,三生三世十里桃花电影

天下有情人,Redis深度探险:千帆竞发,分布式锁,三生三世十里桃花电影

2019-04-20 00:56:27 投稿作者:admin 围观人数:274 评论人数:0次

分布式运用进行逻辑处理时经常会遇到并发问题。

比方一个操作要修正用户的状况,修正状况需求先读出用户的状况,在内存里进行修 改,改完了再存回去。假如这样的操作殷无双君上邪一同进行了,就会呈现泽明并发问题,由于妹妹调教日记读取和保存状 态这两个操作不是原子的梁红玉擂鼓战金山。(Wiki 解说:所谓原子操作是指不会被线程调度机制打断的操 作;这种操作一旦开端,就一向运转到完毕全国有情人,Redis深度探险:千帆竞发,分布式锁,三生三世十里桃花电影,中心不会有任何 context switch 线程切换。)

这个时分就要运用到分布式锁来约束程序的并发履行。Redis 分布式锁运用十分广泛, 它是面试的重要考点之一,许多同学都知道这个常识,也大致知道分布式锁的原理,可是具 体到细节的运用上往往并不完全正确。

分布式锁

分布式锁本质上要完成的方针霍尼韦尔便是在 Redis 里边占一个“茅坑”,当其他进程也要来占时,发现现已有人蹲在那里了,就只好抛弃或许稍后再试。

占坑一般是运用 setnx(set if not exists) 指令,只允许被一个客户端占坑。先来先占, 用完了,再调用 del 指令开释茅坑。

// 这儿的冒号:便是一个一般的字符,没特别意义,它可所以恣意其它字符,不要误解

> setnx lock:codehole true

OK

... do something critical ...

> del lock:codehole

(integer) 1

可是有个问题,假如逻辑履行到中心呈现异常了,或许会导致 del 指令没有被调用,这样 就会堕入死锁,全国有情人,Redis深度探险:千帆竞发,分布式锁,三生三世十里桃花电影锁永久得不到开释。

所以咱们在拿到锁之后,再给锁加上一个过期时刻,比方 5s,这样即便中心呈现异常也 能够确保 5 秒之后锁会主动开释。

> setnx lock:codehole true

OK

> expire lock:codehole 5

... do something critical ...

> del lock:codehole

(integer) 1

可是以上逻辑还有问题。假如在 setnx 和 expire 之间服务器进程忽然挂掉了,或许是由于机器掉电或许是被人为杀掉的,就会导致 expi恩施大峡谷re 得全国有情人,Redis深度探险:千帆竞发,分布式锁,三生三世十里桃花电影不到履行,也会形成死锁。

这种问题的本源就在于 set迟立夏nx 和 expire 是两条指令而不是原子指令。假如这两条指令能够一同履行就不会呈现多宝余问题。或许你会想到用 Redis 事务来处理。可是这儿不可,由于 expire 是依赖于 setnx 的履行成果的,假如 setnx 没抢到锁,expire 是不应该履行的。事务里没有 if- else 分支逻辑,事务的特点是一口气履行,要么悉数履行要么一个都不履行。

为了处理这个疑问,Redis 开源社区呈现了一堆分布式锁的 library,专门用来处理全国有情人,Redis深度探险:千帆竞发,分布式锁,三生三世十里桃花电影这个问题。完成办法极为杂乱,小白用户一般要费很大的精力才能够搞懂。假如你需求运用分布式锁, 意味着你不能只是运用 Jedis 或许 redis-py 就行了,还得引进分全国有情人,Redis深度探险:千帆竞发,分布式锁,三生三世十里桃花电影布式锁的 library。

为了管理这个乱象,Redis 2.8 版别中作者加入了 set 指令的扩展参数,使得 setnx 和 expire 指令能够一同履行,彻底处理了分布式锁的乱象。从此以后一切的第三j罗方分布式锁 library 能够歇息了。 > set lock:codehole true ex 5 nx OK ... do something critical ... > del lock:c全国有情人,Redis深度探险:千帆竞发,分布式锁,三生三世十里桃花电影odehole 上面这个指令便是 setnx 和 expire 组合在一同的原子指令,它便是分布式锁的 奥义地点。

超时问题

Redis 的分布式锁不能处理超时问题,假如在加锁和开释锁之间的逻辑履行的太长,以致 于超出了锁的超时约束,就会呈现问题。由于这时分锁过期了,第二个线程从头持有了这把锁, 可是紧接着第一个线程履行完了事务逻辑,就把锁给开释了,第三个线程就会在第二个线程逻 辑履行完之间拿到了锁。

为了防止这个问题,Redis 分布式锁不要用于较长时刻的使命。假如真的偶然呈现了,数 据呈现的小波紊乱或许需求人工介入处理。

tag = random.nextint() # 随机数

if redis.set(key, tag, nx=True, ex=5):

do_something()

redis.delifequals(key, tag) # 假象的 delifequals 指令

有一个愈加安全的计划是为 set 指令的 value 参数设置为一个随机数,开释锁时先匹配随机数是否共同,然后再全国有情人,Redis深度探险:千帆竞发,分布式锁,三生三世十里桃花电影删去 key。可是匹配 value 和删去 key 不是一个原子操作,Redis 也没有供给类似于 delifequals 这样的指令,这就需求运用 Lua 脚本来处理了,由于 Luadecade 脚本能够确保接连多个指令的原子性履行。

# delifequals

if redis.call("get",KEYS[1]) ==刀剑如梦 ARGV[1] then

return redis.call("del",KEYS[1])

else

return 0

end

可重入性

可重入性是指线程在持有锁的情况下再次恳求加锁,假如一个锁支撑同一个线程的屡次加锁,那么这个锁便是可重入的。比方 Java 言语里有个 ReentrantLock 便是可重入锁。Redis 分布式锁假如要支撑可重入,需求对客户端的 set江西方欣信息技术有限公司 办法进行包装,运用线程的 Threadlocal 变量存储当时持有锁的计数。

还需求考虑内存锁计数的过期时刻,代码杂乱度将会持续升高。老钱不引荐运用可重入锁,它加剧了客户端的杂乱性,在编写事务办法时注意在逻辑结构上进行调整完全能够不运用可重入锁。下面是 Java 版别的可重入锁。

public class RedisWithReentrantLock {

private Th雪花女神龙readLocal lockers = new ThreadLocal<>();

private Jedis jedis;

public RedisWithReentrantLock(Jedis jedis) {

this.jedis = jedis;

}

private boolean _lock(String key) {

return jed天津咏春拳sinais.set(key, "", "nx", "ex", 5L) != null;

}

private void _unlock(String key) {

jedis.del(key);

}

private Map currentLockers() {

Map refs = lockers.get();

if (refs != null) {

return refs;

}

lockers.set(new HashMap<>());

return lockers.get();

}

public boolean lock(String key) {

Map refs = currentLockers();

Integer refCnt = refs.get(key);

if (refCnt != null) {

refs.put(key, refCnt + 1);

return true;

}

boolean ok = this._lock(key);

if (!ok) {

return false;

}

refs.put(key, 1);

return true;

}

public boolean unlock(String key) {

Map refs = currentLockers();

Integer refC绯红女巫nt = refs.get(key);

if (refCnt == null) {

return false;

}

refCnt -= 1;

if (refCnt > 0) {

refs.put(key, refCSao8080nt);

} else {

refs.remove(key);

this ._unlock(key);

}

return true;

}

public static void main(String[] args) {

Jedis jedis = new Jedis();

RedisWithReentrantLock redis = new RedisWi狗狗图片大全thReentrantLock(jedis);

System.out.println(red西瓜英语is.lock("codehole"));

System.out.println(redis.lock("codehole"));

System.out.println(redis.unlock("codehole"));

System.out环湖赛开幕式.println(redis.unlock("codehole"));

}

}

以上还不是分布式锁的悉数,在小册的拓宽篇《拾遗失补 —— 再谈分布式锁》,咱们还会持续对分布式锁做进一步的深化了解。

the end
二选一终极问题,心理咨询