【Redis面试题整理一】

一、Redis定义

Redis 是一种基于内存的数据库,对数据的读写操作都是在内存中完成,读写速度非常快,被广泛应用于缓存方向。并且,Redis 存储的是 KV 键值对数据

二、Redis为什么不存在并发竞争

对数据类型的操作都是原子性的,因为执行命令由单线程负责的,不存在并发竞争的问题。

三、Redis 和 Memcached 有什么区别?

共同点:

1、都是基于内存的数据库,一般都用来当做缓存使用。
2、都有过期策略。
3、两者的性能都非常高。

区别:

1、Redis 支持的数据类型更丰富(String、Hash、List、Set、ZSet),而 Memcached 只支持最简单的 key-value 数据类型;

2、Redis 支持数据的持久化,可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进行使用,而 Memcached 没有持久化功能,数据全部存在内存之中,Memcached 重启或者挂掉后,数据就没了;

3、Redis 目前是原生支持 cluster 模式的;Memcached 没有原生的集群模式,需要依靠客户端来实现往集群中分片写入数据。Memcached 是多线程,非阻塞 IO 复用的网络模型;Redis 使用单线程的多路 IO 复用模型。(Redis 6.0 针对网络数据的读写引入了多线程)

5、Redis 支持发布订阅模型、Lua 脚本、事务等功能,而 Memcached 不支持。并且,Redis 支持更多的编程语言。

6、Redis 同时使用了惰性删除与定期删除;Memcached 过期数据的删除策略只用了惰性删除。

四、为什么用 Redis 作为 MySQL 的缓存?

主要是因为 Redis 具备「高性能」和「高并发」两种特性。

1、Redis 具备高性能

假如用户第一次访问 MySQL 中的某些数据。这个过程会比较慢,因为是从硬盘上读取的。将该用户访问的数据缓存在 Redis 中,这样下一次再访问这些数据的时候就可以直接从缓存中获取了,操作 Redis 缓存就是直接操作内存,所以速度相当快。

如果 MySQL 中的对应数据改变的之后,同步改变 Redis 缓存中相应的数据即可,不过这里会有 Redis 和 MySQL 双写一致性的问题,后面我们会提到。

2、 Redis 具备高并发

单台设备的 Redis 的 QPS(Query Per Second,每秒钟处理完请求的次数) 是 MySQL 的 10 倍,Redis 单机的 QPS 能轻松破 10w,而 MySQL 单机的 QPS 很难破 1w。

所以,直接访问 Redis 能够承受的请求是远远大于直接访问 MySQL 的,所以我们可以考虑把数据库中的部分数据转移到缓存中去,这样用户的一部分请求会直接到缓存这里而不用经过数据库。

五、Redis数据类型

Redis 提供了丰富的数据类型,常见的有五种数据类型:String(字符串),Hash(哈希),List(列表),Set(集合)、Zset(有序集合)。

随着 Redis 版本的更新,后面又支持了四种数据类型: BitMap(2.2 版新增)、HyperLogLog(2.8 版新增)、GEO(3.2 版新增)、Stream(5.0 版新增)。 Redis 五种数据类型的应用场景:

String 类型的应用场景:缓存对象、常规计数、分布式锁、共享 session 信息等。
List 类型的应用场景:消息队列(但是有两个问题:1. 生产者需要自行实现全局唯一 ID;2. 不能以消费组形式消费数据)等。
Hash 类型:缓存对象、购物车等。
Set 类型:聚合计算(并集、交集、差集)场景,比如点赞、共同关注、抽奖活动等。
Zset 类型:排序场景,比如排行榜、电话和姓名排序等。
Redis 后续版本又支持四种数据类型,它们的应用场景如下:

BitMap(2.2 版新增):二值状态统计的场景,比如签到、判断用户登陆状态、连续签到用户总数等;
HyperLogLog(2.8 版新增):海量数据基数统计的场景,比如百万级网页 UV 计数等;
GEO(3.2 版新增):存储地理位置信息的场景,比如滴滴叫车;
Stream(5.0 版新增):消息队列,相比于基于 List 类型实现的消息队列,有这两个特有的特性:自动生成全局唯一消息ID,支持以消费组形式消费数据。

1、String数据类型

Redis自己构建了一种 简单动态字符串(Simple Dynamic String,SDS)。相比于 C 的原生字符串,Redis 的 SDS 不光可以保存文本数据还可以保存二进制数据,并且获取字符串长度复杂度为 O(1)(C 字符串为 O(N)),除此之外,Redis 的 SDS API 是安全的,不会造成缓冲区溢出。

命令

SET key value 设置指定 key 的值
SETNX key value 只有在 key 不存在时设置 key 的值
GET key 获取指定 key 的值
MSET key1 value1 key2 value2 …设置一个或多个指定 key 的值
MGET key1 key2 … 获取一个或多个指定 key 的值
STRLEN key 返回 key 所储存的字符串值的长度
INCR key 将 key 中储存的数字值增一
DECR key 将 key 中储存的数字值减一
EXISTS key 判断指定 key 是否存在
DEL key (通用)删除指定的 key
EXPIRE key seconds (通用)给指定 key 设置过期时间

应用场景

需要存储常规数据的场景举例:缓存 session、token、图片地址、序列化后的对象(相比较于 Hash 存储更节省内存)。相关命令:SET、GET。
------直接缓存整个对象的 JSON,命令例子: SET user:1 ‘{“name”:“xiaolin”, “age”:18}’。
------采用将 key 进行分离为 user:ID:属性,采用 MSET 存储,用 MGET 获取各属性值,命令例子: MSET user:1:name xiaolin user:1:age 18 user:2:name xiaomei user:2:age 20。

需要计数的场景举例:用户单位时间的请求数(简单限流可以用到)、页面单位时间的访问数、计算访问次数、点赞、转发、库存数量等等。相关命令:SET、GET、 INCR、DECR 。

分布式锁利用 SETNX key value 命令可以实现一个最简易的分布式锁(存在一些缺陷,通常不建议这样实现分布式锁)。

2、List数据类型

Redis quicklist是Redis 3.2版本以后针对链表和压缩列表进行改造的一种数据结构,是 zipList 和 linkedList 的混合体,相对于链表它压缩了内存,进一步提高了效率。
双向链表,带来了部分额外的内存开销

命令
RPUSH key value1 value2 … 在指定列表的尾部(右边)添加一个或多个元素LPUSH key value1 value2 … 在指定列表的头部(左边)添加一个或多个元素LSET key index value 将指定列表索引 index 位置的值设置为 value
LPOP key 移除并获取指定列表的第一个元素(最左边)
RPOP key 移除并获取指定列表的最后一个元素(最右边)
LLEN key 获取列表元素数量
LRANGE key start end 获取列表 start 和 end 之间 的元素

应用场景

信息流展示举例:最新文章、最新动态。相关命令:LPUSH、LRANGE。

消息队列
Redis List 数据结构可以用来做消息队列,只是功能过于简单且存在很多缺陷,不建议这样做。
及时处理消息,就需要在程序中不停地调用 RPOP 命令;为了解决这个问题,Redis提供了BRPOP 命令。BRPOP命令也称为阻塞式读取,客户端在没有读到队列数据时,自动阻塞,直到有新的数据写入队列,再开始读取新数据。和消费者程序自己不停地调用RPOP命令相比,这种方式能节省CPU开销
处理重复消息自行为每个消息生成一个全局唯一ID,生成之后,在用 LPUSH 命令把消息插入 List 时,需要在消息中包含这个全局唯一 ID。
留存消息(防止宕机导致的消息未及时处理问题),List 类型提供了 BRPOPLPUSH 命令,这个命令的作用是让消费者程序从一个 List 中读取消息,同时,Redis 会把这个消息再插入到另一个 List(可以叫作备份 List)留存。

缺点
List 不支持多个消费者消费同一条消息,不支持消费组的实现。但stream流可以实现

你可能感兴趣的:(redis,数据库,缓存)