【建议收藏】Redis 知识干货汇总
为了让大家更好的学习 redis 相关的知识,我这里总结一下核心的面试知识点,大家来一起学习。
问:redis 是单线程吗?是否有线程安全问题?
redis 是使用单线程模型来处理客户端的请求,只是使用多线程来处理数据的读写和协议解析,执行命令还是使用单线程,不会有线程安全的问题。
redis 在 4.0 的时候引入了多线程来做大缓存的清除处理工作,主要是体现在大数据的异步删除功能上,例如 unlink key、flushdb async、flushall async 等,先清除 key ,接着异步清除对应的 value。
在 redis 6.0 之前的网络模型都是标准的单线程 reactor 模型。在 6.0 开始引入了一个非标准的多线程 reactor 模型,sub-reactor 此时会使用 socket 读取 client 请求,并处理命令的解析,然后具体写还是在主线程上执行。
问:redis 集群架构(4 种)
单机模式
优点:1、部署简单。2、数据一致性高<br/>缺点:1、内存容量有限 2、处理能力有限 3、无法高可用。
主从模式
优点:1、可靠性得到一定保障,当节点出问题,可由其他节点来提供。2、提升了读能力,分散主节点的读压力<br/>缺点:1、主节点的写能力和存储能力受单机限制。2、主节点宕机,切换从节点需要业务方手动切换,进行人工干预。
哨兵模式
优点:1、基于主从模式,主从可以自动切换。<br/>缺点:1、节点的承载能力有限,写能力和存储能力都有限。
redis cluser 模式
优点:高可用、可扩展性、分布式、支持容错,去中心化的集群方案。
问:redis 常用的数据结构和应用场景
常用数据结构:
string,hash,list,set,sorted set,geospatial,hyperloglog,bitmaps
string(字符串)
简介:<br/>string 类型是 redis 中最基本、最常用的数据类型。string 类型是二进制安全的,意思是 redis 的 string 可以包含任何数据。string 类型的值最大能存储 512MB。
string 字符串的内存分配机制:<br/>当字符串的长度小于 1MB 时,每次扩容都是加倍现有的空间。<br/>如果字符串长度超过 1MB 时,每次扩容时只会扩展 1MB 的空间。
使用命令:<br/>get、set、incr、decr、mget、del、expire 、incrby
应用场景:<br/>单值缓存<br/>对象缓存<br/>分布式锁<br/>共享 Session<br/>累加器<br/>限速<br/>
redis 字符串数据结构,capacity 和 len 两个属性都是泛型,为更合理的使用内存,不同长度的字符串采用不同的数据类型表示,且在创建字符串的时候 len 会和 capacity 一样大,不产生冗余的空间。
struct SDS{ T capacity; //数组容量 T len; //实际长度 byte flages; //标志位,低三位表示类型 byte[] content; //数组内容 }
list(列表)
简介:<br/>list 链表(redis 使用双端链表实现的 list),是有序的,value 可以重复,可以通过下标取出对应的 value 值,左右两边都能进行插入和删除数据。
list 底层实现机制:<br/>redis3.2 之前,当数据量较少的时候它的底层存储结构为一块连续内存,称之为 ziplist(压缩列表),它将所有的元素紧挨着一起存储,分配的是一块连续的内存;当数据量较多的时候将会变成 quicklist(快速链表)结构。<br/>redis3.2 之后,采用的一种叫 quicklist 的数据结构来存储 list,列表的底层都 quicklist 实现。
常用命令:<br/>lpush、rpush、lrange、pop、bpop
应用场景:<br/>消息队列栈文章列表
hash(哈希表)
简介:<br/>哈希类型是指一个键值对的存储结构。
hash 底层实现:<br/>ziplist(压缩列表):当哈希类型元素个数小于 hash-max-ziplist-entries 配置(默认 512 个)同时所有值都小于 hash-max-ziplist-value 配置(默认 64 字节)时使用。ziplist 使用更加紧凑的结构实现多个元素的连续存储,所以比 hashtable 更加节省内存。hashtable(哈希表):当 ziplist 不能满足要求时,会使用 hashtable。
常用命令:<br/>hget、hset、hgetall、hincr、hincrby
应用场景:<br/>多条件缓存购物车功能存储对象
set(集合)
简介:<br/>Set 就是一个集合,集合的概念就是一堆不重复值的组合。利用 Redis 提供的 Set 数据结构,可以存储一些集合性的数据。一个集合最多可以存储 2^32-1 个元素。
常用命令:<br/>sadd、smembers、sismemeber、scard、spop、srem
应用场景:<br/>好友、关注、粉丝、感兴趣的人集合去重
sort set(有序集合)
简介:<br/>有序集合和集合一样,不能有重复元素。但是可以排序,它给每个元素设置一个 score 作为排序的依据。最多可以存储 2^32-1 个元素。
常用命令:<br/>zadd、zrange、zrevrange、zcard、zrank、zrangebyscore、zrem、zscore
应用场景:<br/>成绩排行榜,游戏排行榜,用 Sorted Sets 来做带权重的队列限流
geospatial
简介:<br/>Redis 在 3.2 推出 Geo 类型,该功能可以推算出地理位置信息,两地之间的距离。
hyperloglog
简介:<br/>基数:数学上集合的元素个数,是不能重复的。这个数据结构常用于统计网站的 UV。
bitmap
简介:<br/>bitmap 就是通过最小的单位 bit 来进行 0 或者 1 的设置,表示某个元素对应的值或者状态。一个 bit 的值,或者是 0,或者是 1;也就是说一个 bit 能存储的最多信息是 2。bitmap 常用于统计用户信息比如活跃粉丝和不活跃粉丝、登录和未登录、是否打卡等。
问:聊聊 redis 分布式锁的使用和注意问题
分布式锁:控制分布式系统不同进程共同访问共享资源的一种锁的实现。
redis 实现分布式锁可以通过 setnx,setnx+expire,set EX PX NX 来实现。
如果用 setnx 来实现分布式锁需要 del 配合一起使用,setnx 设置成功会返回 1,如果系统用完了就需要把整个锁删除,由于某个原因没有删除锁,其他的系统就不能获取到此分布式锁,这样会容易造成死锁,锁永远得不到释放。
如果用 setnx+expire,当我们设置锁并且 expire 设置了过期时间,如果在 setnx 的时候,expire 缺失败了,这样也会出现死锁问题,锁得不到释放。
当使用 set EX NX 结合使用保证 setnx+expire 会是一个事务去执行,这样肯定不会死锁,但带来了可能的超时问题,如果分布式系统任务在有效期内没有完成,这样会造成其它分布式系统或线程乘虚而入,为了避免这个问题,redis 分布式锁不要用于较长时间的任务。
为了解决分布式锁的超时问题,我们通过语言层面来解决,实现一个线程或者系统如果获取了锁之后,可以再次对其请求加锁,也就是实现分布式锁的可重入性特征。
问:聊聊 redis 的 sorted set 的底层实现
Redis 使用跳跃表作为有序集合键的底层实现。
结构体如下:
typedef struct zskiplistNode { robj *obj; /*成员对象*/ double score; /*分值*/ struct zskiplistNode *backward; /*后退指针*/ struct zskiplistLevel { /*层*/ struct zskiplistNode *forward; /*前进指针*/ unsigned int span; /*跨度*/ } level[]; } zskiplistNode;
1、层 level 跳跃表节点的 level 数组可以包含多个元素,每个元素都包含一个指向其他节点的指针,程序可以通过这些层来加快访问其他节点的速度,一般来说,层的数量越多,访问其他节点的速度就越快。
每次创建一个新跳跃表节点的时候,程序根据幂次定律(power law,越大的数出现的概率越小)随机生成一个介于 1 和 32 之间的值作为 level 数组的大小,这个大小就是层的“高度”。
2、前进指针 forward,每个层都有一个指向表尾方向的前进指针(level[i].forward 属性),用于从表头向表尾方向访问节点。3、跨度 level,层的跨度(level[i].span 属性)用于记录两个节点之间的距离:两个节点之间的跨度越大,它们相距得就越远。指向 NULL 的所有前进指针的跨度都为 0,因为它们没有连向任何节点。
4、后退指针 backward,节点的后退指针(backward 属性)用于从表尾向表头方向访问节点:跟可以一次跳过多个节点的前进指针不同,因为每个节点只有一个后退指针,所以每次只能后退至前一个节点。
5、分值和成员:节点的分值(score 属性)是一个 double 类型的浮点数,跳跃表中的所有节点都按分值从小到大来排序。节点的成员对象(obj 属性)是一个指针,它指向一个字符串对象,而字符串对象则保存着一个 SDS 值。
在同一个跳跃表中,各个节点保存的成员对象必须是唯一的,但是多个节点保存的分值却可以是相同的:分至相同的节点将按照成员对象在字典中的大小来进行排序,成员对象较小的节点会排在前面(靠近表头的方向),而成员对象较大的节点则会排在后面(靠近表尾的方向)。
跳跃表:zskiplist 结构的定义
typedef struct zskiplist { struct zskiplistNode *header, *tail; //header指向跳跃表的表头节点,tail指向跳跃表的表尾节点 unsigned long length; //记录跳跃表的长度,也即是,跳跃表目前包含节点的数量(表头节点不计算在内) int level; //记录目前跳跃表内,层数最大的那个节点的层数(表头节点的层数不计算在内) } zskiplist;
跳跃表通过多个跳跃表节点就可以组成。header 和 tail 指针分别指向跳跃表的表头和表尾节点,通过这两个指针,程序定位表头节点和表尾节点的复杂度为 0(1)。通过使用 length 属性来记录节点的数量,程序可以在 0(1)复杂度内返回跳跃表的长度。
通过使用 length 属性来记录节点的数量,程序可以在 0(1)复杂度内返回跳跃表的长度。
level 属性则用于在 0(1)复杂度内获取跳跃表中层高最大的那个节点的层数量
跳跃表是有序集合的底层实现之一。
redis 的跳跃表实现由 zskiplist 和 zskiplistNode 两个结构组成,其中 zskiplist 用于保存跳跃表信息(比如表头节点、表尾节点、长度),而 zskiplistNode 则用于表 示用跃表节点。
问:聊聊 redis 中的多路复用模型
I/O 模型
BIO(同步阻塞)
NIO(同步非阻塞(多路复用))
AIO(异步非阻塞)
NIO(同步非阻塞(多路复用))
常见的事件轮询 API 有 select、poll、epoll,它们是操作系统提供给用户线程的 API,用于取代用户线程轮询。如果是用户线程轮询就要涉及用户态和内核态的频繁切换,这部分开销是巨大的。
select
select() 的机制中提供一种 fd_set 的数据结构,实际上是一个 long 类型的数组,每一个数组元素都能与一打开的文件句柄(不管是 Socket 句柄,还是其他文件或命名管道或设备句柄)建立联系,当调用 select() 时,由内核根据 IO 状态修改 fd_set 的内容,由此来通知执行了 select() 的进程哪一 socket 或文件可读写。
使用 select() 最大的优势是可以在一个线程内同时处理多个 socket 的 IO 请求。用户可以注册多个 socket,然后不断地调用 select() 读取被激活的 socket,即可达到在同一个线程内同时处理多个 IO 请求的目的。但 select 存在三个问题:一是为了减少数据拷贝带来的性能损坏,内核对被监控的 fd_set 集合大小做了限制,并且这个是通过宏控制的,大小不可改变(限制为 1024);二是每次调用 select(),都需要把 fd_set 集合从用户态拷贝到内核态,如果 fd_set 集合很大时,那这个开销很大;三是每次调用 select() 都需要在内核遍历传递进来的所有 fd_set,如果 fd_set 集合很大时,那这个开销也很大。
poll
poll 的机制与 select 类似,与 select 在本质上没有多大差别,管理多个描述符也是进行轮询,根据描述符的状态进行处理,只是 poll 没有最大文件描述符数量的限制。
select
epoll 在 Linux 2.6 内核正式提出,是基于事件驱动的 I/O 方式,相对于 select 来说,epoll 没有描述符个数限制,使用一个文件描述符管理多个描述符,将用户关心的文件描述符的事件存放到内核的一个事件表中,这样在用户空间和内核空间的 copy 只需一次。
为什么 redis 采用单线程 IO 多路复用
IO 多路复用为单线程处理多线程任务提供了思路,但并不是所有程序的可以保持高效的。redis 把数据存放在内存中,而且操作逻辑简单,CPU 运算速度比内存 IO 更是快了几个数量级,这让 redis 的瓶颈不会出现在 CPU,在到达 CPU 瓶颈之前网络带宽将先成为瓶颈,因此提高 CPU 利用率并不能有效提升 redis 效率。单线程的设计避免了线程的上下文切换,而且单线程设计的安全性也避免了锁,这些因素决定了 Redis 采用单线程 IO 多路复用。
问:讲讲 redis 数据持久化方式
redis 作为一个键值对内存数据库(NoSQL),数据都存储在内存当中,为了避免内存中数据丢失,redis 提供了对持久化的支持,我们可以选择不同的方式将数据从内存中保存到硬盘当中,使数据可以持久化保存。redis 提供了 RDB 和 AOF 两种不同的数据持久化方式。redis 不允许 save 和 bgsave 命令同时执行。当 RDB 与 AOF 两种方式都开启时,redis 会优先使用 AOF 日志来恢复数据,因为 AOF 保存的文件比 RDB 文件更完整。
RDB
RDB 是一种快照存储持久化方式,具体就是将 Redis 某一时刻的内存数据保存到硬盘的文件当中,默认保存的文件名为 dump.rdb,而在 Redis 服务器启动时,会重新加载 dump.rdb 文件的数据到内存当中恢复数据。
开启 RDB 持久化方式
开启 RDB 持久化方式很简单,客户端可以通过向 Redis 服务器发送 save 或 bgsave 命令让服务器生成 rdb 文件,或者通过服务器配置文件指定触发 RDB 条件。
save
save 命令是一个同步操作。当客户端向服务器发送 save 命令请求进行持久化时,服务器会阻塞 save 命令之后的其他客户端的请求,直到数据同步完成。如果数据量太大,同步数据会执行很久,而这期间 Redis 服务器也无法接收其他请求,所以,最好不要在生产环境使用 save 命令。
bgsave
bgsave 命令是一个异步操作。当客户端发服务发出 bgsave 命令时,Redis 服务器主进程会 forks 一个子进程来数据同步问题,在将数据保存到 rdb 文件之后,子进程会退出。所以,与 save 命令相比,Redis 服务器在处理 bgsave 采用子线程进行 IO 写入,而主进程仍然可以接收其他请求,但 forks 子进程是同步的,所以 forks 子进程时,一样不能接收其他请求。这意味着,如果 forks 一个子进程花费的时间太久(一般是很快的),bgsave 命令仍然有阻塞其他客户的请求的情况发生。
AOF(Append-only file)
与 RDB 存储某个时刻的快照不同,AOF 持久化方式会记录客户端对服务器的每一次写操作命令,并将这些写操作以 Redis 协议追加保存到以后缀为 aof 文件末尾,在 Redis 服务器重启时,会加载并运行 aof 文件的命令,以达到恢复数据的目的。
开启 AOF 持久化方式
AOF 的持久化流程:命令写入->append->AOF 缓冲器->sync->AOF 文件 AOF 文件会 rewrite AOF 文件,redis 重启的时候会 load AOF 文件。
# 开启aof机制 appendonly yes # aof文件名 appendfilename "appendonly.aof" # 写入策略,always表示每个写操作都保存到aof文件中,也可以是everysec或no appendfsync always # 默认不重写aof文件 no-appendfsync-on-rewrite no # 保存目录 dir ~/redis/
aof 三种写入策略
always 客户端的每一个写操作都保存到 aof 文件当,这种策略很安全,但是每个写请注都有 IO 操作,所以也很慢。
everysec appendfsync 的默认写入策略,每秒写入一次 aof 文件,因此,最多可能会丢失 1s 的数据。
no redis 服务器不负责写入 aof,而是交由操作系统来处理什么时候写入 aof 文件。更快,但也是最不安全的选择,不推荐使用。
AOF 的优点
AOF 只是追加日志文件,因此对服务器性能影响较小,速度比 RDB 要快,消耗的内存较少。
AOF 的缺点
AOF 方式生成的日志文件太大,即使通过 AFO 重写,文件体积仍然很大。恢复数据的速度比 RDB 慢。
问:讲讲 redis cluster 的集群方案,节点如何通信?
redis 集群方案基于分而治之的思想。Redis 中数据都是以 Key-Value 形式存储的,而不同 Key 的数据之间是相互独立的。因此可以将 Key 按照某种规则划分成多个分区,将不同分区的数据存放在不同的节点上。这个方案类似数据结构中哈希表的结构。在 redis 集群的实现中,使用哈希算法(公式是 CRC16(Key) mod 16383)将 Key 映射到 0~16383 范围的整数。这样每个整数对应存储了若干个 Key-Value 数据,这样一个整数对应的抽象存储称为一个槽(slot)。每个 redis cluster 的节点——准确讲是 master 节点——负责一定范围的槽,所有节点组成的集群覆盖了 0~16383 整个范围的槽。
redis cluster 节点通信
不同节点存储的数据相互独立,这些节点仍然需要相互通信以同步节点状态信息。redis 集群采用 P2P 的 Gossip 协议,节点之间不断地通信交换信息,最终所有节点的状态都会达成一致。
redis Cluster 的每个缓存节点都会开通一个独立的 TCP 通道,用于和其他节点通讯。
节点间进行消息通信时会选择不同的消息类型、不同的发送频率和时机等。消息类型主要有以下 5 种:
meet 消息:在节点互相握手阶段,当节点收到客户端的 cluster meet 命令时,会向新加入的节点发送 meet 消息,请求该节点加入当前集群。新节点在收到 meet 消息后会回复一个 pong 消息。
ping 消息:集群里每个节点会在每秒钟选择向一部分节点发送 ping 消息,接收到此消息的节点会回复一个 pong 消息。ping 消息内容包含本节点和其他节点的状态信息,以此达到状态同步。需要注意的是,选择发送 ping 的节点按照规则会随机找 5 个节点,在其中选择最久没有通信的一个节点,即在扫描节点列表的时候会选择最近一次收到 pong 消息的时间大于 cluster_node_timeout/2 的所有节点,这样做是为了防止这些节点长时间没有更新。
pong 消息:pong 消息包含自身的状态数据,在接收到 ping 或 meet 消息时会回复 pong 消息,也会主动向集群广播 pong 消息,如果遇到故障而在故障恢复后新的主节点会广播 pong 消息。这样其他节点可以获知该节点的最新信息。
fail 消息:当一个主节点判断另一个主节点进入 fail(失败)状态时,会向集群广播这一 fail 消息。接收到这一消息的节点会将该消息保存起来。
publish 消息:节点收到 publish 命令后会先执行该命令,然后向集群广播这一条消息,接收到这一消息的节点也会执行该 publish 命令。
问:redis 过期删除策略和内存淘汰机制
redis 过期删除策略
当内存没占满时,在 Redis 中过期的键是通过定时删除,惰性删除和定期删除来进行优化的。
定时删除
创建一个定时器,当 key 设置有过期时间,且过期时间到达时,由定时器任务立即执行对键的删除操作<br/>优点:节约内存,到时就删除,快速释放掉不必要的内存占用<br/>缺点:CPU 压力很大,无论 CPU 此时负载量多高,均占用 CPU,会影响 redis 服务器响应时间和指令吞吐量<br/>用处理器性能换取存储空间(拿时间换空间)
惰性删除
数据到达过期时间,不做处理。等下次访问该数据时,我们需要判断如果未过期,返回数据发现已过期,删除,返回不存在<br/>优点:节约 CPU 性能,发现必须删除的时候才<br/>删除缺点:内存压力很大,出现长期占用内存的数据<br/>总结:用存储空间换取处理器性能(拿时间换空间)
定期删除
定期删除,Redis 服务器启动初始化时,读取配置 server.hz 的值,默认为 10。每秒执行 server.hz 次 serverCorn()->databaseCorn()->activeExpireCycle()。其中 activeExpireCycle()对每个 Expires[*]逐一检测,每次执行时间为 250ms/server.hz
对某个 Expires[*]检测时,随机挑选几个 key 检测规则如下:
如果 key 超时,删除 key
如果一轮中删除 key 的数量>W*25%,循环该过程(重点抽查)
如果一轮中删除 key 的数量
其中 W 取值=ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP 的属性值
redis 的 del 和 unlink
redis 服务自身对 key 的删除,可以分为同步删除和异步删除。使用 DEL 命令会触发同步删除。如果使用 unlink 命令,redis 服务会先计算删除 key 的成本,从而更智能地做出同步删除或异步删除的选择。注意,只有 4.0 版本后,才有 unlink 命令。
redis 的 del 操作需要注意问题
如果 Key 是一个有很多元素的复杂类型,这个过程可能会堵塞一下 Redis 服务自身,从而影响用户的访问。
内存淘汰机制
当内存使用量超过了 maxmemory 配置的限制时,Redis 可以使用以下策略来淘汰数据:
放弃数据淘汰
no-enviction:禁止数据淘汰,会引发 OOM(Out Of Memroy)。Redis4.0 默认策略
检测全库数据(所有数据集 server.db[i].dict)
allkeys-lru:挑选最近最少使用的数据淘汰
allkeys-lfu:挑选最近使用次数最少的数据淘汰
allkeys-random:任意选择数据淘汰
检测易失数据(会过期的数据集 server.db[i].expries)
volatile-lru:挑选最近最少使用的数据淘汰
volatile-lfu:挑选最近使用次数最少的数据淘汰
volatile-random:任意选择数据淘汰
volatile-ttl:挑选即将过期的数据淘汰
问:有遇到 redis 大 key 的问题吗?
一般 redis 的大 key 也就是 value bytes > 10kb 则,这类的 key 算大 key。
过大的 Value 会引发数据倾斜、热点 Key、实例流量或 CPU 性能被占满等问题,应从设计源头上避免此类问题带来的性能影响。
如何分析大 key
–-bigkeys 、memory 命令 和 Rdbtools 工具 分析统计实例存在的 大 key
如何优雅的删除大 key
大 key 问题,主动删除无条件使用 unlink 异步,被动删除时配置 lazyfree 惰性删除。
总结:
根本上解决大 key 问题要从开始时就避免使用大 key。
问:缓存穿透、缓存击穿、缓存雪崩解决方案?
缓存穿透
描述:访问一个缓存和数据库都不存在的 key,此时会直接打到数据库上,并且查不到数据,没法写缓存,所以下一次同样会打到数据库上。此时,缓存起不到作用,请求每次都会走到数据库,流量大时数据库可能会被打挂。此时缓存就好像被“穿透”了一样,起不到任何作用。
解决方案
1、接口校验。在正常业务流程中可能会存在少量访问不存在 key 的情况,但是一般不会出现大量的情况,所以这种场景最大的可能性是遭受了非法攻击。可以在最外层先做一层校验:用户鉴权、数据合法性校验等,例如商品查询中,商品的 ID 是正整数,则可以直接对非正整数直接过滤等等。
2、缓存空值。当访问缓存和 DB 都没有查询到值时,可以将空值写进缓存,但是设置较短的过期时间,该时间需要根据产品业务特性来设置。
3、布隆过滤器。使用布隆过滤器存储所有可能访问的 key,不存在的 key 直接被过滤,存在的 key 则再进一步查询缓存和数据库。
缓存击穿
描述:某一个热点 key,在缓存过期的一瞬间,同时有大量的请求打进来,由于此时缓存过期了,所以请求最终都会走到数据库,造成瞬时数据库请求量大、压力骤增,甚至可能打垮数据库。
解决方案:
1、加互斥锁。在并发的多个请求中,只有第一个请求线程能拿到锁并执行数据库查询操作,其他的线程拿不到锁就阻塞等着,等到第一个线程将数据写入缓存后,直接走缓存。
2、热点数据不过期。直接将缓存设置为不过期,然后由定时任务去异步加载数据,更新缓存。
缓存雪崩
描述:大量的热点 key 设置了相同的过期时间,导在缓存在同一时刻全部失效,造成瞬时数据库请求量大、压力骤增,引起雪崩,甚至导致数据库被打挂。缓存雪崩其实有点像“升级版的缓存击穿”,缓存击穿是一个热点 key,缓存雪崩是一组热点 key。
解决方案:
1、过期时间打散。既然是大量缓存集中失效,那最容易想到就是让他们不集中生效。可以给缓存的过期时间时加上一个随机值时间,使得每个 key 的过期时间分布开来,不会集中在同一时刻失效。
2、热点数据不过期。该方式和缓存击穿一样,也是要着重考虑刷新的时间间隔和数据异常如何处理的情况。
3、加互斥锁。该方式和缓存击穿一样,按 key 维度加锁,对于同一个 key,只允许一个线程去计算,其他线程原地阻塞等待第一个线程的计算结果,然后直接走缓存即可。
问:如何使用 redis 做消息队列?
list 结构作为队列,rpush 生产消息,lpop 消费消息,需要自己实现阻塞。blpop 没有消息会阻塞,等到消息到来。
整理不易,大家多帮忙转发,点赞哦~,后面还会分享 mysql,网络,mq 等相关的面试知识哦~
版权声明: 本文为 InfoQ 作者【利志分享】的原创文章。
原文链接:【http://xie.infoq.cn/article/59eb873215db414509230c104】。文章转载请联系作者。
评论