Redis面试必问知识点整理,使用过程中的坑,建议收藏【绽放吧!数据库】

香菜聊游戏 发表于 2021/07/27 22:12:03 2021/07/27
【摘要】 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
右边表示对应的数据类型在内存中的形式


0.png



图片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文件的执行是事务性的。


总结

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

【绽放吧!数据库】有奖征文火热进行中:https://bbs.huaweicloud.com/blogs/285617

【版权声明】本文为华为云社区用户原创内容,未经允许不得转载,如需转载请发送邮件至:cloudbbs@huaweicloud.com;如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容。
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。