java面试(缓存Redis)

为什么使用缓存

高性能,高可用,高并发。

什么是缓存击穿?缓存穿透?缓存雪崩?

击穿:redis中没有查询到数据。解决:设置热点数据永不过期。加载DB时防止并发。

穿透:redis和mysql中都没有查询到数据。解决:参数校验,将没有数据的情况也存入redis中,引入布隆过滤器

雪崩:redis中大量数据同时过期。解决:设置不同的过期时间。

布隆过滤器:

  1. 如果判断一个元素不在集合中则一定不在
  2. 如果判断一个元素在集合中则存在一定误判率
  3. 布隆过滤器只能加数据不能减数据

对数据进行修改时,如何保证Redis与数据库的数据一致?

  • 先删缓存,后写数据库。存在脏读。
    解决:
    1.先把缓存修改为特殊值(-999),客户端读到特殊值延时查询。
    2.延时双删,先删缓存,再写数据库,再删缓存。
  • 先写数据库,再删缓存。存在缓存删除失败,数据不一致。
    解决:
    1.缓存设置过期时间
    2.引入MQ,保证原子操作。mq设置两个消费者同时删除缓存和db,删除失败消息还在,MQ重试机制会删除。
    通用解决:
    将热点数据设置为永不过期,但再value中写入逻辑过期,另起线程扫描key,对于逻辑上过期的缓存进行删除。
    总结:始终只能保证一定时间内的最终一致性。

如何设计一个分布式锁?并优化

分布式锁:所有进程都能访问到的地方,设置一个锁资源,让进程来竞争。(数据库,zookeeper,Redis)

Redis实现分布式锁:

SETNX key value:当key不存在时,就将key设置为value。并返回1.如果key存在,就返回0.

EXPIRE key locktime:设置key的有效时长。

DEL key:删除

GETSET key value:先GET,在SET,先返回key对应的值,如果没有,将key设置成value。

1.最简单的分布式锁:SETNX 加锁,DEL解锁。

问题1:锁获取失败后不回主动解锁,这个锁就被锁死了。

解决1:给锁设置时长。

问题2:SETNX和EXPIRE不是原子性的,获取锁进程没到EXPIRE指令就挂了。

解决2:将锁的内容设置为过期时间(客户端时间+过期时长),SETNX获取失败时,拿这个时间和当前时间比对,如果过期,就先删除锁,再重新上锁。

问题3:在高并发场景下,会产生多个进程同时拿到锁的情况。

解决3:SETNX失败后,获取锁上时间戳,然后用GETSET,将自己的过期时间更新上去,并获取旧值,和之前获取的时间戳不一致,就表示这个锁被其他线程占用。

java 复制代码
public boolean tryLock(RedisConnection conn) {
	long nowTIme = System.currentTimeMills();
	long expireTime = nowTime + 1000;
	if(conn.SETNX("myKey", "1") == 1) {
		conn.EXPIRE("myKey", 1000);
		return true;
	} else {
		long oldVal = conn.get("myTime");
		if(oldVal != null && oldVal < nowTime) {
			long currnetVal = conn.GETSET("myKey", expireTime);  //CAS,如果和之前获取的锁不一样,就失败
			if(oldVal == currentVal) {
				conn.SET("mykey", 1000);
				return true;
			}
			return false;
		}
		return false;
	}
}

注:上面优化根本问题是SETNX和EXPIRE指令无法保证原子性。Redis2.6提供了直接执行Lua脚本方式。通过Lua脚本保证原子性。Redission就是在此基础上实现公平锁,非公平锁等。

Redis如何设置Key的过期时间?

redis设置过期时间:1.EXPIRE 2.SETEX

实现原理:

1.定期删除:每隔一段时间执行一次删除过期key的操作。

2.懒汉式删除:当使用get,getset等指令获取数据时,判断key是否过期,过期先把key删除,在执行后面操作。

定期删除会遍历每个databasae(默认16个),检查当前库中指定个数的key(默认20个)。随机抽查这些key,过期就删除。

海量数据下,如何快速查找一条记录?

1.使用布隆过滤器,快速过滤不存在的记录。使用redis的bitmap结构来实现布隆过滤器。

2.在Redis中建立数据缓存。以普通字符串形式存储(userId->UserInfo)。以hash来存储(userId key -> Username field)。以整个hash来存储所有的数据。UserInfo -> field用userId,value就用user.json。一个hash最多支持2^32-1个键值对。

缓存击穿:对不存在的数据也建立key。

缓存过期:将热点数据设置成永不过期,定期重建缓存,使用分布式锁重建缓存。

3.查询优化:redis按槽位分配数据,自己实现槽位激素那,找到记录应分配的机器,直接去目标机器上去找。

相关推荐
.NET修仙日记12 分钟前
2026 .NET 面试八股文:高频题 + 答案 + 原理(面试加分技巧)
面试·职场和发展·.net·.net core·微软技术
知识领航员15 分钟前
2026年推荐6个AI音乐工具
java·人工智能·python·eclipse·django·php·pygame
mfxcyh21 分钟前
如何把对象数据转化为数组
java·服务器·前端
念越37 分钟前
从网络基础到Socket编程:TCP/UDP原理 + Java实战详解
java·网络·tcp/ip·udp
我是无敌小恐龙2 小时前
Java基础入门Day10 | Object类、包装类、大数/日期类、冒泡排序与Arrays工具类 超详细总结
java·开发语言·数据结构·算法·贪心算法·排序算法·动态规划
极客先躯2 小时前
高级java每日一道面试题-2025年12月07日-实战篇[Dockerj]-Docker daemon 的配置文件在哪里?常用的配置项有哪些?
java·docker·配置文件的实际位置·配置文件的格式规则·常用配置项全景与分类·配置如何生效·daemon 配置折射架构思维
庞轩px2 小时前
第三篇:缓存穿透、击穿、雪崩——从原理到解决方案
redis·缓存·缓存穿透·缓存击穿·缓存雪崩·布隆过滤器·互斥锁/逻辑过期
云烟成雨TD2 小时前
Spring AI Alibaba 1.x 系列【49】状态图运行时引擎:CompiledGraph 源码解析
java·人工智能·spring
Tutankaaa2 小时前
从10队到50队:知识竞赛软件的高并发场景如何设计?
java·经验分享·后端·spring
下次再写2 小时前
微服务架构实战:Spring Boot + Spring Cloud 从入门到精通
java·spring boot·spring cloud·微服务架构·服务注册与发现·分布式系统·api网关