一个有意思的面试问题:分布式系统下,缓存失效后如何保证所有不同机器只有一个线程去读取DB更新缓存?

先来分析一下这个问题:

实际上是缓存击穿问题的拓展版,如果一个很热的key失效了,瞬间又有大量的请求落到这个key上,导致所有请求击穿到DB,发生事故。

对于单机的情况,我们可以加个锁来解决这个事情,并做一个双重校验,来保证只有一个线程去更新这个key,大致逻辑如下:

public String get(String key) {
	if(existInRedis(key)) {
		return getValueFromRedis(key); // 实际上这里要搞成原子的,存在直接返回
	} else {
		lock.lock();
		if(!existInRedis(key)) {
			String val = getValueFromMySQL(key);
			lock.unlock();
			setRedisValue(key, val);
			return val;
		} else {
			lock.unlock();
			return getValueFromRedis(key);
		}
	}
}

那么对于多机的情况呢?

使用分布式锁可以吗?

我们很自然的想到用分布式锁来实现这个问题,比如redis。但是分布式锁有一个很大的问题就是他并非阻塞的,当获取不到分布式锁时,线程就知道自己获取失败了,而对于本文的情形,获取失败了实际上并不太好处理,获取失败了我们返回什么呢?我们并不知道该返回的value是什么,因此必须要等待获得锁的线程刷新缓存后,读取缓存,然后返回。

那么我们怎么知道Redis什么时候更新好了呢?

我第一个想到的办法是获取锁失败的线程周期性的尝试读取Redis,看是否已经更新好,比如每隔0.5秒去读一次Redis,读到了直接返回,没读到则等待0.5秒继续尝试。不过面试官否决了这个答案,我考虑的话可能是因为对Redis造成了额外的负担?不过我觉得这个负担是可以接受的,如果有大佬知道还有哪些缺陷可以留言跟我交流下。

阻塞式分布式锁

那么可不可以对分布式锁进行改进,为其增加阻塞的功能呢?

如果是Redis分布式锁,单独依赖Redis似乎没办法实现这个功能,因为这个问题本质上是一个进程间通信的问题,而且是跨机器的进程间通信,基于Redis的分布式锁如果想要实现这个功能,就必须要依赖于客户端的代码提供相应的支持,这种方案肯定是实现起来很麻烦的。

其实也有简单的实现方法,就是类似于上面提到的那种,自己不断尝试获得锁,获得不到则阻塞,类似:

private static final String key = "DISTRIBUTED_LOCK"; 
public boolean lock() {
	while(true){
		if(redis.setnx(key)) {
			return true;
		} else {
			sleep(1000);
		}
	}
}
public boolean unlock() {
	redis.remove(key);
}

但这样实际和上面的方法没什么区别。不想使用这种周期性的重试就只能依赖回调,而Redis并没有这种功能。

我们想到了zookeeper。

zookeeper分布式锁

zookeeper支持事件回调:
当一个client访问zookeeper时,client与zookeeper保持长连接。应用可以通过client的api注册一些callback,当对应的事件发生时,client会执行对应的callback。

有了zookeeper的回调功能,再来看我们的需求,那么真是有些雪中送炭的感觉了,有现成的轮子的感觉就是好:)。

整体的业务逻辑伪代码就可以是这样:

private Object o = new Object(); 
public boolean lock() {
	if(zookeeper.addNode(callBack) != first){ // 注册回调函数,判断当前节点是否为第一个节点,是则继续执行,不是则阻塞,第一个节点执行完毕后,回调函数唤醒所有wait线程。
		o.wait();
	}
}
private void callBack() {
	o.notifyAll();
}

看来面试官是想问zookeeper相关,确实了解的不是很多,这样看来,zookeeper实现的分布式锁确实要比redis强大很多。

Over~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值