❤️ Redis面试必问知识点整理,使用过程中的坑,建议收藏

Posted 香菜聊游戏

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了❤️ Redis面试必问知识点整理,使用过程中的坑,建议收藏相关的知识,希望对你有一定的参考价值。

现在许多游戏服务器直接用redis 作为数据库进行使用,这对于小的游戏是个不错的选择,因为redis 的速度和使用的简单,不少稍微大的游戏使用redis 作为中间件,持久化到mysql,将一些热点数据放到redis,也是不错的补充,今天就介绍下redis在使用的过程中的一些注意点

❤️ 1、数据类型

Redis支持五种数据类型:string(字符串),hash(哈希),list(列表),set(集合)及zset(sorted set:有序集合)。

我的理解,Redis 本身就是一个大的map,key 就是 字符串

value的类型分为上面五种,

string 就是value 是字符串

hash  表示value 是个map,redis 这个大map 里面的一个value 是个map,相当于是个嵌套map,或者理解为mysql 的table

list 数据类型是list,代表一个列表,当然也可以作为一个队列

set 就是一个无序的集合,相当于Java里的set

zset 是一个排序的集合,每个元素会有一个得分,自动排序,相当于treeset

下图左边表示redis 的key

右边表示对应的数据类型在内存中的形式

❤️ 2、缓存击穿

缓存穿击穿的是,是指一个key非常热点,在不停的扛着大并发,大并发集中对这一个点进行访问,当这个key在失效的瞬间,持续的大并发就穿破缓存,直接请求数据库,就像在一个屏障上凿开了一个洞。

这个问题是很多面试会问的,其实问题很简单,无法命中缓存,直接读取数据库,导致压力过大。

建议给的方案就是将缓存永不过期,一直保存在内存中。

❤️ 3、分布式锁

流程逻辑:

①当多个app发现此时需要去写数据的时候,我们只能让一个线程去写,通过setnx(lockKey),如果返回true,则说明拿到了锁。

②此时其他的app setnx(lockKey),则会返回false,那么我们不能让他们去查询数据库,此时可以让他们休眠一段时间再试(查询缓存,没有则获取锁)。

③拿到锁的app在执行完业务逻辑后,应该释放锁(删除缓存中的lockKey),为了防止拿到锁的app挂掉,那么释放锁的代码将变成不可达代码,造成死锁,那么我们应该给锁加一个有效期expire()。

④但是我们设置有效期后,可能业务逻辑还没有处理完,那么锁到期了,此时别的线程拿到了锁,而当前线程释放锁就将别人的锁释放掉,所以要判断当前锁是否被替换掉,没替换才删除。此时我们要区分不同的线程的锁,通常用uuid作为锁的value。

在游戏中的业务场景:在跨服玩法的设计时将多个服的数据集中存在redis,有数据需要多个服同时修改,分布式锁的使用可以解决此类问题

❤️ 4、pipeline

redis客户端执行一条命令分4个过程:发送命令-〉命令排队-〉命令执行-〉返回结果

使用pipeline 可以将多次 IO 往返的时间缩减为一次,前提是 pipeline 执行的指令之间没有

使用Pipeline执行速度比逐条执行要快,特别是客户端与服务端的网络延迟越大,性能体能越明显。

@Test
public void testRedis() {
// 工具类初始化
JedisUtils jedis = new JedisUtils("127.0.0.1", 6379, "12345678");

for (int i = 0; i < 100; i++) {
// 设值
jedis.set("n" + i, String.valueOf(i));
}
System.out.println("keys from redis return =======" + jedis.keys("*"));

}

//使用pipeline提交所有操作并返回执行结果
@Test
public void testPipelineSyncAll() {
// 工具类初始化
Jedis jedis = new Jedis("127.0.0.1", 6379);
jedis.auth("12345678");
// 获取pipeline对象
Pipeline pipe = jedis.pipelined();
pipe.multi();
pipe.set("name", "james"); // 调值
pipe.incr("age");// 自增
pipe.get("name");
pipe.discard();
// 将不同类型的操作命令合并提交,并将操作操作以list返回
List<Object> list = pipe.syncAndReturnAll();

for (Object obj : list) {
// 将操作结果打印出来
System.out.println(obj);
}
// 断开连接,释放资源
jedis.disconnect();
}

❤️ 5、Redis key 的过期时间和永久有效分别怎么设置?

redis通过exipre或则pexpire命令,可以以秒或则毫秒为精度为某个key设置过期时间,在经过指定的时间之后,redis服务器就会删除生存时间为0的key

Redis PERSIST 命令用于移除给定 key 的过期时间,使得 key 永不过期

❤️ 6、Redis 如何做内存优化?

1、避免不需要的数据存在redis

2、压缩数据,比如有些字符串可以在java内用枚举表示,不用字符串

3、之前游戏存储的数据是json,可以给数据指定简单的key,缩减数据大小

❤️ 7、缓存雪崩

大量缓存集中在某一个时间段失效的时候,也会给后端系统(比如DB)带来很大压力

如果大量的 key 过期时间设置的过于集中,到过期的那个时间点,redis 可能会出现短暂的卡顿现象。

解决办法:对过期时间增加一个随机值,将过期时间分散开来

❤️ 8、跨服排行榜

跨服排行榜是一个常见的功能,跨服排行榜的数据一般放在redis中,将每个玩家的数据放到zset中。

redis 默认实现是,相同分数的成员按字典顺序排序(0 ~9 , A ~Z,a ~ z),所以相同分数排序就不能根据时间优先来排序。

这个实现很简单,但是非常常见的一个bug就是相同分数的情况下,先上榜的玩家要排在前面,在初次写这种排行榜的时候要注意一点,

一般的做法是当前玩家的积分 + 小数,

注意在读取积分的时候,要去除小数位,不然小数位相当之后可能会超过1,就会影响玩家的真实分数,这也是常犯的问题。

最简单的方案就是对时间取小数,1/时间戳,finalScore = socre + 1/时间戳

注意:这个时候socre 必须为整数,不可以为小数,否则和小数相加,可能会导致进位

❤️ 9、发布订阅

在之前做的一个公会战功能中,多个服务器需要抢摊位,服务器之间摊位信息的变化就是使用的发布订阅功能,在修改完redis 数据之后,发布修改消息,其他服拉去redis 的内容数据,进行数据同步

❤️ 10、lua操作

redis 的事务支持不太好,但是redis 支持lua,如果在开发中需要操作多个key,又需要事务性,这个时候可以考虑使用lua,redis 对lua文件的执行是事务性的。

总结

随便写写,好累,今天就到这了,休息

以上是关于❤️ Redis面试必问知识点整理,使用过程中的坑,建议收藏的主要内容,如果未能解决你的问题,请参考以下文章

带你整理面试过程中关于Redis 中的持久化的相关知识点

带你整理面试过程中关于Redis 的删除策略的相关知识点

互联网公司面试必问的Redis题目

「Java面试必看」Netty面试必问的知识点整理

带你整理面试过程中关于Redis 的相关知识的补充

互联网公司面试经常会问的Redis题目