分享

Redis面试灵魂十问十答【你为什么使用redis】

hyj 2020-2-18 07:39:51 发表于 总结型 [显示全部楼层] 回帖奖励 阅读模式 关闭右栏 2 2858
本帖最后由 hyj 于 2020-2-18 07:48 编辑

问题导读

1、为什么要用Redis?
2、Redis有哪些数据库结构?
3、如果有大量的key需要设置同一时间过期,一般需要注意什么?


1、为什么要用Redis?
因为传统的关系型数据库如MySQL已经不能适用所有的场景了,比如秒杀的库存扣减,访问流量高峰等等,这些都很容易使数据库崩溃掉,所以引入了缓存中间件,目前比较常见的缓存中间件有Reids和Memcached,中和考虑它们的优缺点,最终选择了Redis来作为缓存中间件。

2、Redis有哪些数据库结构?
常用的五种基本数据类型:String(字符串)、Hash(字典)、List(列表)、Set(集合)、Zset(SortedSet:有序集合)


String类型
string是redis最基本的类型,你可以理解成与Memcached一模一样的类型,一个key对应一个value。
string类型是二进制安全的。意思是redis的string可以包含任何数据。比如jpg图片或者序列化的对象 。
string类型是Redis最基本的数据类型,一个键最大能存储512MB。


实例
[mw_shl_code=bash,true]redis 127.0.0.1:6379> SET name "redis.net.cn"
OK
redis 127.0.0.1:6379> GET name
"redis.net.cn"
[/mw_shl_code]

在以上实例中我们使用了 Redis 的 SET 和 GET 命令。键为 name,对应的值为redis.net.cn。

注意:一个键最大能存储512MB。


Hash类型

Redis hash 是一个键值对集合。
Redis hash是一个string类型的field和value的映射表,hash特别适合用于存储对象。


实例
[mw_shl_code=bash,true]redis 127.0.0.1:6379> HMSET user:1 username redis.net.cn password redis.net.cn points 200
OK
redis 127.0.0.1:6379> HGETALL user:1
1) "username"
2) "redis.net.cn"
3) "password"
4) "redis.net.cn"
5) "points"
6) "200"
redis 127.0.0.1:6379>
[/mw_shl_code]
以上实例中 hash 数据类型存储了包含用户脚本信息的用户对象。 实例中我们使用了 Redis HMSET, HEGTALL 命令,user:1 为键值。

每个 hash 可以存储 2^32 - 1^ 键值对(40多亿)。


List类型

Redis 列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素导列表的头部(左边)或者尾部(右边)。


实例


[mw_shl_code=bash,true]redis 127.0.0.1:6379> lpush redis.net.cn redis
(integer) 1
redis 127.0.0.1:6379> lpush redis.net.cn mongodb
(integer) 2
redis 127.0.0.1:6379> lpush redis.net.cn rabitmq
(integer) 3
redis 127.0.0.1:6379> lrange redis.net.cn 0 10
1) "rabitmq"
2) "mongodb"
3) "redis"
redis 127.0.0.1:6379>
[/mw_shl_code]
列表最多可存储 2^32 - 1^ 元素 (4294967295, 每个列表可存储40多亿)。


Set类型
Redis的Set是string类型的无序集合。
集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是O(1)。

sadd 命令

添加一个string元素到key对应的set集合中,成功返回1,如果元素已经在集合中返回0,key对应的set不存在返回错误。

[mw_shl_code=bash,true]sadd key member
[/mw_shl_code]

实例
[mw_shl_code=bash,true]redis 127.0.0.1:6379> sadd redis.net.cn redis
(integer) 1
redis 127.0.0.1:6379> sadd redis.net.cn mongodb
(integer) 1
redis 127.0.0.1:6379> sadd redis.net.cn rabitmq
(integer) 1
redis 127.0.0.1:6379> sadd redis.net.cn rabitmq
(integer) 0
redis 127.0.0.1:6379> smembers redis.net.cn

1) "rabitmq"
2) "mongodb"
3) "redis"
[/mw_shl_code]

注意:以上实例中 rabitmq 添加了两次,但根据集合内元素的唯一性,第二次插入的元素将被忽略。
集合中最大的成员数为 2^32 - 1^ (4294967295, 每个集合可存储40多亿个成员)。


Zset类型
Redis zset 和 set 一样也是string类型元素的集合,且不允许重复的成员。
不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。
zset的成员是唯一的,但分数(score)却可以重复。

zadd 命令
添加元素到集合,元素在集合中存在则更新对应score
[mw_shl_code=bash,true]zadd key score member
[/mw_shl_code]


[mw_shl_code=bash,true]redis 127.0.0.1:6379> zadd redis.net.cn 0 redis
(integer) 1
redis 127.0.0.1:6379> zadd redis.net.cn 0 mongodb
(integer) 1
redis 127.0.0.1:6379> zadd redis.net.cn 0 rabitmq
(integer) 1
redis 127.0.0.1:6379> zadd redis.net.cn 0 rabitmq
(integer) 0
redis 127.0.0.1:6379> ZRANGEBYSCORE redis.net.cn 0 1000

1) "redis"
2) "mongodb"
3) "rabitmq"
[/mw_shl_code]

3、如果有大量的key需要设置同一时间过期,一般需要注意什么?

如果有大量的key过期时间设置的过于集中,到时期的那个时间点,redis可能会出现短暂的卡顿现象。严重的话会出现缓存雪崩,我们一般需要在时间上加一个随机值,使得过期时间分散一些。
电商首页经常会使用定时任务刷新缓存,可能大量的数据失效时间都十分集中,如果失效时间一样,又刚好在失效的时间点大量用户涌入,就有可能造成缓存雪崩

4、Redis分布式锁是什么回事?


先拿setnx来争抢锁,抢到之后,再适用expire给锁加上一个过期时间防止锁忘记释放。

如果在setnx之后执行expire之前进程意外crash(崩溃)或者重启维护了,那会怎么样?
set指令有一个参数,可以同时把setnx和expire合成一条指令来使用,保证了setnx和expire的原子性。
[mw_shl_code=bash,true]set key value [EX seconds] [PX milliseconds] [NX|XX]
EX seconds:设置失效时长,单位秒
PX milliseconds:设置失效时长,单位毫秒
NX:key不存在时设置value,成功返回OK,失败返回(nil)
XX:key存在时设置value,成功返回OK,失败返回(nil)
[/mw_shl_code]


5、假如Redis里面有1亿个key,其中有10w个key是以某个固定的已知的前缀开头的,如何将他们全部找出来?

使用keys指令可以扫出执行模式的key列表。

如果这个redis正在给线上的业务提供服务,那使用keys指令会有什么问题?

重点:这里面要get到redis关键的一个特性:redis是单线程的。

Redis是单线程的,keys指令会导致线程阻塞一段时间,线上服务会停顿,直到指令执行完毕,服务才能恢复。这个时候可以使用scan指令,scan指令可以无阻塞的提取出指定模式的key列表,但是会有一定重复概率,在客户端做一次去重就可以了,但是整体所花费的时间会比直接用keys指令长。

不过,增量式迭代命令也不是没有缺点的:举个例子,使用SMEMBERS命令可以返回集合键当前包含的所有元素,但是对于SCAN这类增量式迭代命令来说,因为在对键进行增量式迭代的过程中,键可能会被修改,所以增量式迭代命令只能对返回的元素提供有限的保证。

6、怎么使用Redis做异步队列?


一般使用list结构作为队列,rpush生产消息,lpop消费消息。当lpop没有消息的时候,要适当sleep一会儿再重试。


可不可以不用sleep呢?

list还有个指令为blpop,在没有消息的时候,它会阻塞住直到消息到来。


能不能生产一次消费多次呢?

使用pub/sub主题订阅者模式,可以实现1:N的消息队列。


pub/sub有什么缺点?

在消费者下线的情况下,生产的消息会丢失,得使用专业的消息队列如RocketMQ等。


追问Redis如何实现延时队列?

使用SortedSet,拿时间戳作为score,消息内容为key调用zadd来生产消息,消费者用zrangebyscore指令获取N秒之前的数据轮询进行处理。


7、Redis是怎么持久化的?服务主从数据怎么交互的?

RDB做镜像全量持久化,AOF做增量持久化。因为RDB会耗费较长的时间,不够实时,在停机的时候会导致大量丢失数据,所以需要AOF来配合使用。在redis实例重启时,会使用RDB持久化文件重新构建内存,再使用AOF重放近期操作指令来实现完整恢复重启之前的状态。


这里很好理解,把RDB理解为一整个表全量的数据,AOF理解为每次操作的日志就好了,服务器重启的时候先把表的数据全部搞进去,但是他可能不完整,你再回放一下日志,数据不就完整了嘛。不过Redis本身的机制是 AOF持久化开启且存在AOF文件时,优先加载AOF文件;AOF关闭或者AOF文件不存在时,加载RDB文件;加载AOF/RDB文件城后,Redis启动成功; AOF/RDB文件存在错误时,Redis启动失败并打印错误信息

如果突然机器断电会怎么样?
取决于AOF日志sync属性的配置,如果不要求性能,在每条写指令时都sync一下磁盘,就不会丢失数据。但是在高性能的要求下每次都sync是不现实的,一般都使用定时sync,比如1秒一次,这个时候最多就会丢失1s的数据。

RDB的原理是什么?
fork和cow。fork是指redis通过创建子进程来进行RDB操作,cow指的是copy on write,子进程创建后,父子进程共享数据段,父进程继续提供读写服务,写脏的页面数据会逐渐和子进程分离开来。


8、Pipeline(管道)有什么好处,为什么要用pipeline?

可以将多次IO往返的时间缩减为一次,前提是pipeline执行的指令之间没有因果相关性。使用redis-benchmark进行压测的时候可以发现影响redis的QPS峰值的一个重要因素是pipeline批次指令的数目。


9、Reids的同步机制是什么?

Redis可以使用主从同步,从从同步。第一次同步时,主节点做一次bgsave,并同时将后续修改操作记录到内存buffer,待完成后将RDB文件全量同步到复制节点,复制节点接受完成后将RDB镜像加载到内存。加载完成后,再通知主节点将期间修改的操作记录同步到复制节点进行重放就完成了同步过程。后续的增量数据通过AOF日志同步即可,有点类似数据库的binlog。


10、Redis集群的高可用怎么保证,集群的原理是什么?


Redis Sentinal(哨兵模式)着眼于高可用,在master宕机时会自动将slave提升为master,继续提供服务。
Redis Cluster着眼于扩展性,在单个redis内存不足时,使用Cluster进行分片存储。



————————————————
作者:「ZH..」
原文链接:https://blog.csdn.net/qq_37868757/article/details/104326669



已有(2)人评论

跳转到指定楼层
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

关闭

推荐上一条 /2 下一条