怎样实现redis缓存与数据库同步?
(1)问题分析
考官主要考察面试者对于项目中缓存使用的能力
(2)核心答案讲解
答1:
我们会先去redis中判断数据是否存在,如果存在,则直接返回缓存好的数据。而如果不存在的话,就会去数据库中,读取数据,并把数据缓存到Redis中。适用场合:如果数据量比较大,但不是经常更新的情况(比如用户排行)
答2:
只要使用了缓存就涉及到缓存同步的问题。缓存同步其实就是当缓存的信息发生变化,也就是对后台对缓存的数据进行增、删、改操作后,数据库中的数据发生了变化同时要把缓存中的数据对应删除即可。当页面再次请求数据时,缓存中不能命中就会从数据库中查询并且添加到缓存中,即实现了缓存同步。
(3)问题扩展
回顾redis雪崩和redis穿透
(4)结合项目中使用
情景一:广告数据
情景二:做搜索的分类对应的品牌数据、规格数据
Redis如何实现分布式阻塞队列?
1. Redis分布式锁实现原理
分布式锁本质上要实现的目标就是在Redis里面占一个“茅坑”,当别的进程也要来占时,发现已经有人蹲在那里了,就只好放弃或者稍后再试。占坑一般是使用setnx(set if not exists)指令,只允许被一个客户端占坑。先来先占,用完了,再调用del指令释放茅坑。
死锁问题:如果逻辑执行到中间出现异常了,可能会导致del指令没有被调用,这样就会陷入死锁,锁永远得不到释放,解决这个问题我们在拿到锁之后,再给锁加上一个过期时间,比如 5s,这样即使中间出现异常也可以保证 5 秒之后锁会自动释放。
2. 普通非阻塞锁实现
public class RedisLock {
private Jedis jedis;
public RedisLock(Jedis jedis) {
this.jedis = jedis;
}
public boolean lock(String key) {
return jedis.set(key, "", "nx", "ex", 5L) != null;
}
public void unlock(String key) {
jedis.del(key);
}
}
2.1 存在问题
如果某一个进程没有拿到锁得到了false的结果那么次进程是否执行当前任务?显然对于一般情况来说我们的任务都是必须执行的那么此时我们就要考虑该何时执行了,在传统的锁中我们如果没有拿到锁线程就进入了阻塞状态那么此处我们是否可以改进同样实现阻塞唤醒机制。
3. 分布式阻塞锁具体实现
3.1 解决思路
(1)首先我们改造lock锁,当不能创建key时就利用当前key阻塞当前线程
(2)当某一个线程释放锁时通过redis的pub/sub发送一个消息消息内容为key
(3)所有使用锁的应用监听lock通道的消息,在收到消息时通过key唤醒对应线程
3.2具体实现
package com.hgy.common.redis;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPubSub;
import java.util.HashMap;
public class RedisLock extends JedisPubSub {