Redis学习总结
Redis学习总结
介绍一下Redis
Redis是一种基于内存的数据库,对数据的读写都是在内存中完成的,因此读写速度非常快,常用于缓存、消息队列、分布式锁等场景。Redis提供了多种数据结构来支持不同的业务场景,比如String、hash、list、set、zset、bitmaps(位图)、hyperLogLog(基数统计)、GEO(地理信息)、Steam(流),并且对数据的操作都是原子性的,因为执行命令是单线程负责的,不存在并发竞争的问题。
除此之外,Redis 还支持事务 、持久化、Lua 脚本、多种集群方案(主从复制模式、哨兵模式、切片机群模式)、发布/订阅模式,内存淘汰机制、过期删除机制等等。
Redis和Memcached的区别?
相同点:
- 都是基于内存的数据集,一般当做缓存使用。
- 都有过期策略。
- 两者性能都非常高
不同点:
- Redis支持的数据类型更多(String、List、set、zset、hash),而Memcached只支持key-value数据类型。
- Redis支持数据的持久化,Memcached不支持。
- Redis原生支持集群模式,Memcached 没有原生的集群模式,需要依靠客户端来实现往集群中分片写入数据。
- Redis 支持发布订阅模型、Lua 脚本、事务等功能,而 Memcached 不支持
为什么用redis做mysql的缓存?
主要是因为redis具有高性能和高并发的优点。
- 高性能
假如用户第一次访问mysql,因为要到硬盘去读取数据,过程比较慢。将该用户访问的数据放到redis中,第二次访问时直接从缓存中获取,操作redis缓存就是直接操作内存,所以速度相当快。
- 高并发
单台设备的redis的QPS是mysql的10倍,redis的QPS能轻松破10万,而mysql都很难破1万。所以直接访问redis能够承受的请求远远大于mysql,可以考虑把部分mysql中的数据存到redis中,这样用户请求这一部分数据时就不用经过mysql。
redis常用的数据结构以及使用场景?
redis常用的数据结构有五种:String、List、Hash、Set、Zset
使用场景:
- String 类型的应用场景:缓存对象、常规计数、分布式锁、共享 session 信息等。
- List 类型的应用场景:消息队列(但是有两个问题:1. 生产者需要自行实现全局唯一 ID;2. 不能以消费组形式消费数据)等。
- Hash 类型:缓存对象、购物车等。
- Set 类型:聚合计算(并集、交集、差集)场景,比如点赞、共同关注、抽奖活动等。
- Zset 类型:排序场景,比如排行榜、电话和姓名排序等。
五种常见数据类型是怎么实现的?

String类型的内部实现
String类型的底层实现是SDS(简单动态字符串)。SDS相比于C的原生字符串,主要优点有:
- SDS不仅可以保存文本数据,还可以保持二进制数据。
- SDS获取字符串长度的时间复杂度为O(1)。SDS结构里用len属性来记录字符串长度,所以复杂度O(1)。而C语言的字符串没有len属性,所以获取长度要O(n)。
- Redis的SDS API是安全的,拼接字符串不会造成缓冲区溢出。因为SDS在拼接时会首先判断SDS空间是否满足要求,如果空间不够会自动扩容。
List的内部实现
List底层数据结构是由双向链表或者压缩列表实现的。
- 如果列表的元素个数小于 512 个(默认值,可由 list-max-ziplist-entries 配置),列表每个元素的值都小于 64 字节(默认值,可由 list-max-ziplist-value 配置),Redis 会使用压缩列表作为 List 类型的底层数据结构;
- 如果列表的元素不满足上面的条件,Redis 会使用双向链表作为 List 类型的底层数据结构;
但是在 Redis 3.2 版本之后,List 数据类型底层数据结构就只由 quicklist 实现了,替代了双向链表和压缩列表。
Hash类型的底层实现
Hash 类型的底层数据结构是由压缩列表或哈希表实现的:
- 如果哈希类型元素个数小于 512 个(默认值,可由 hash-max-ziplist-entries 配置),所有值小于 64 字节(默认值,可由 hash-max-ziplist-value 配置)的话,Redis 会使用压缩列表作为 Hash 类型的底层数据结构;
- 如果哈希类型元素不满足上面条件,Redis 会使用哈希表作为 Hash 类型的底层数据结构。
在 Redis 7.0 中,压缩列表数据结构已经废弃了,交由 listpack 数据结构来实现了。
Set类型内部实现
Set类型的底层实现是由哈希表或者整数集合实现的:
- 如果集合中的元素都是整数且元素个数小于 512 (默认值,set-maxintset-entries配置)个,Redis 会使用整数集合作为 Set 类型的底层数据结构;
- 如果集合中的元素不满足上面条件,则 Redis 使用哈希表作为 Set 类型的底层数据结构。
Zset类型的内部实现
Zset 类型的底层数据结构是由压缩列表或跳表实现的:
- 如果有序集合的元素个数小于 128 个,并且每个元素的值小于 64 字节时,Redis 会使用压缩列表作为 Zset 类型的底层数据结构;
- 如果有序集合的元素不满足上面的条件,Redis 会使用跳表作为 Zset 类型的底层数据结构;
在 Redis 7.0 中,压缩列表数据结构已经废弃了,交由 listpack 数据结构来实现了。
Redis线程模型
Redis是单线程吗?
不是。
redis单线程指的是【接收客户端请求->解析请求->进行数据读写操作->发送数据给客户端】这个过程是由一个线程(主线程)来完成的。
但是redis程序并不是单线程的,redis在启动的时候,还会启动后台线程(BIO)。
- redis在2.6版本,会启动两个后台线程,分别处理关闭文件和AOF刷盘两个任务。
- redis在4.0之后,新增了一个lazyfree线程,用来异步释放redis内存。
之所以为关闭文件,AOF刷盘和释放内存单独创建线程,因为这几个任务都十分消耗时间,如果把它们放到主线程中,那么主线程就容易发生阻塞,就无法处理后续的请求了。
后台线程相当于一个消费者,生产者把耗时任务放到队列中,消费者(BIO)不断轮询这个队列,拿出任务就去执行对应的方法。
redis采用单线程为什么还这么快
redis的QPS能达到10w/每秒级别,之所以这么快,原因如下:
- redis的大部分操作都在内存中执行。cpu不是redis的瓶颈,内存和网络带宽是瓶颈。
- redis单线程模型可以避免多线程的竞争。省去的多线程切换时的开销。
- redis采用了IO多路复用机制来处理socket请求,IO 多路复用机制是指一个线程处理多个 IO 流,就是我们经常听到的 select/epoll 机制。简单来说,在 Redis 只运行单线程的情况下,该机制允许内核中,同时存在多个监听 Socket 和已连接 Socket。内核会一直监听这些 Socket 上的连接请求或数据请求。一旦有请求到达,就会交给 Redis 线程处理,这就实现了一个 Redis 线程处理多个 IO 流的效果
redis6.0之后引入多线程
redis6.0之后引入的多线程是用了多个I/O线程来处理网络请求,这是因为随着网络硬件的性能提升,redis的瓶颈有时会出现在网络IO上,但是对于命令的执行,仍然是单线程的。
Redis持久化
redis如何保证数据不丢失
redis的数据是存在内存中的,每次重启redis,内存中的数据都会丢失。为了保证内存中的数据不丢失,redis实现了数据持久化的机制,将数据存到磁盘中,这样redis重启时就会从磁盘恢复原有的数据。
redis有三种持久化方式:
- AOF日志:每执行一条写操作命令,就把该命令以追加的方式写入到一个文件里
- RDB快照:将某一时刻的内存数据,以二进制的方式写入磁盘;
- 混合持久化
AOF日志如何实现?
Redis在执行一条命令时,会把该命令以追加的方式写到硬盘中,然后在redis重启时,会读取该文件的命令,然后逐一执行命令的方式来进行数据恢复。
为什么先执行命令,再把数据写入日志呢?
优点:
- 避免额外的检查开销:如果先将命令写入AOF日志,在执行命令的话,如果当前的命令出现了语法问题,那么如果不进行语法检查,该错误的命令记录到了AOF日志中的话,redis在使用日志恢复数据时,就可能会出错。
- 不会阻塞当前写操作命令的执行:因为当写操作命令成功后,才会将命令写到AOF日志中。
缺点:
- 数据可能会丢失
- 可能阻塞其他操作
AOF日志的写回策略有几种
- Always,这个单词的意思是「总是」,所以它的意思是每次写操作命令执行完后,同步将 AOF 日志数据写回硬盘;
- Everysec,这个单词的意思是「每秒」,所以它的意思是每次写操作命令执行完后,先将命令写入到 AOF 文件的内核缓冲区,然后每隔一秒将缓冲区里的内容写回到硬盘;
- No,意味着不由 Redis 控制写回硬盘的时机,转交给操作系统控制写回的时机,也就是每次写操作命令执行完后,先将命令写入到 AOF 文件的内核缓冲区,再由操作系统决定何时将缓冲区内容写回硬盘。

RDB快照如何实现?
AOF日志记录的是操作命令,不是实际的数据,所以用AOF日志时,需要将所有的命令都执行一遍,一旦日志非常多,就会造成redis的恢复操作十分缓慢。
为了解决这个问题,redis增加了RDB快照,RDB快照记录的是某一瞬间实际的内存数据。因此在redis恢复数据时,RDB快照恢复数据的效率会比AOF日志高些,因为直接将RDB文件读入内存就可以了,不用像AOF日志一样一条条去执行命令。
RDB做快照时会阻塞线程吗
redis提供了两个命令来生成RDB文件,save和bgsave。他们的区别在于是否是主线程里执行:
- 执行了save命令,就会在主线程里生成RDB文件,由于和执行操作命令在同一个线程,如果写入RDB命令文件的时间太长,会阻塞主线程。
- 执行了bgsave命令,创建一个子进程来生成RDB文件,这样可以避免主线程的阻塞。
注意:redis的快照是全量快照,每次执行快照时,都是把内存中的所有数据记录到磁盘中。所以执行快照是一个比较重的操作。如果频率太频繁,可能会对redis性能产生影响。如果频率太低,服务器故障时,丢失的数据会更多。
RDB在执行的时候,数据能更改吗?
可以滴!在执行bgsave的过程中,redis依然可以处理操作命令,也就是数据能被修改,关键的技术就是写时复制技术(Copy-On-Write, COW)。
执行bgsave命令时,会通过fork()命令创建子进程,此时子进程和主进程是共享同一片内存数据的,因为创建子进程时会复制主进程的页表,但是两个页表指向的物理内存都是同一个。
- 如果此时主进程执行读操作,则主进程和bgsave子进程互相不影响。

- 如果主进程执行写操作,则被修改的数据会复制一份副本,然后bgsave子进程会将副本数据写入RDB中,在这个过程中,原来的数据仍然能被主进程修改。

混合持久化
使用了混合持久化,AOF文件的前半部分是RDB格式的全量数据,后半部分是AOF格式的增量数据。
混合持久化优点:
- 混合持久化结合了 RDB 和 AOF 持久化的优点,开头为 RDB 的格式,使得 Redis 可以更快的启动,同时结合 AOF 的优点,有减低了大量数据丢失的风险。
混合持久化缺点:
- AOF 文件中添加了 RDB 格式的内容,使得 AOF 文件的可读性变得很差;
- 兼容性差,如果开启混合持久化,那么此混合持久化 AOF 文件,就不能用在 Redis 4.0 之前版本了。

Redis集群
如何实现服务高可用?
高可用要设计redis的多服务节点,比如redis的主从复制、哨兵模式、切片集群。
主从复制
redis高可用的基础保证就是主从复制,将从前的一台redis服务器,同步数据到多台从服务器中,即一主多从的模式,且主从服务器之间采用的是「读写分离」的方式。
所有的数据修改都只在主服务器上进行,然后将最新的数据同步给从服务器,这样使得主从服务器的数据一致。
主从服务器之间的命令复制是异步的。主服务器收到新的写命令后,会发送给从服务器。但是,从服务器并不会等到服务器实际执行完命令之后,再把结果返回给客户端,而是主服务器自己本地执行完命令之后机会向客户端返回结果。如果从服务器还没执行完主服务器同步过来的命令,主从服务器之间的数据就会不一致。
所以,无法实现强一致性保证(主从数据时时刻刻保持一致),数据不一致是难以避免的。

哨兵模式
在使用主从服务时,当redis的主从服务器出现故障宕机时,需要手动进行恢复。为了解决这个问题,redis增加了哨兵模式(Redis Sentinel),因为哨兵模式做到了可以监控主从服务器,并且提供了主从节点故障转移的功能。
切片集群模式(Redis Cluster)
当 Redis 缓存数据量大到一台服务器无法缓存时,就需要使用 Redis 切片集群(Redis Cluster )方案,它将数据分布在不同的服务器上,以此来降低系统对单主节点的依赖,从而提高 Redis 服务的读写性能。
集群脑裂导致数据丢失怎么办?
- 什么是脑裂?
由于网络问题,主节点与所有的从节点都失联了,但是此时的主节点和客户端的网络是正常的,客户端仍然在向主节点写入数据,此时这些数据被旧主节点缓存在缓冲区中,因为主从节点间的网络问题,这些数据都是无法同步给从节点的。
这时,哨兵也发现了主节点失联了,它就认为主节点挂了,从剩下的从节点中选出一个节点作为新的主节点(leader),这时集群就出现了两个主节点–脑裂。
然后,网络突然好了,哨兵会将原来的主节点降级为从节点,然后这个原来的主节点(现在的从节点)会向新的主节点请求数据同步。因为第一次同步是全量同步的方式,此时的从节点会清除自己本地的数据,然后再做全量同步。所以,之前客户端写入原来的主节点的数据就会丢失了,就是集群产生脑裂导致数据丢失的问题。
- 解决方法
脑裂出现的主要原因是哨兵集群认为主节点已经出现故障了,重新选举其他节点作为主节点,但是主节点的故障是假故障。所以应对脑裂的方法是限制去原主库接收请求,redis提供了两个配置项:
- min-slaves-to-write:与主节点通信的从节点数量必须大于等于该值主节点,否则主节点拒绝写入。
- min-slaves-max-lag:主节点与从节点通信的ACK消息延迟必须小于该值,否则主节点拒绝写入。
这两个配置项必须同时满足,不然主节点拒绝写入。
脑裂无法彻底解决,因为redis的主从集群内部没有共识算法来维护多个节点的强一致性,他不像Zookeeper那样,每次写入大多数节点成功后才算成功,当脑裂发生时,Zookeeper节点被孤立,此时无法写入大多数节点,写请求会直接失败,因此Zookeeper才能保证集群的强一致性。
Redis过期删除和内存淘汰
redis使用的过期删除策略是什么?
redis使用的过期删除策略是「惰性删除+定期删除」。可以看到,惰性删除策略和定期删除策略都有各自的优点,所以 Redis 选择「惰性删除+定期删除」这两种策略配和使用,以求在合理使用 CPU 时间和避免内存浪费之间取得平衡。
- 惰性删除策略:不主动删除过期键,每次从数据库访问key时,都检测key是否过期,如果过期则删除该key。
优点:每次访问时才会检测key是否过期,所以只会使用很少的系统资源,因此,惰性删除策略对cpu友好。
缺点:过期了的key仍然保留在数据库中,造成内存空间浪费。所以,惰性删除对内存不友好。
- 定期删除策略:每隔一段时间随机从数据库中取出一定数量的key进行检查,并删除其中的过期key。
Redis 的定期删除的流程:
- 从过期字典中随机抽取 20 个 key;
- 检查这 20 个 key 是否过期,并删除已过期的 key;
- 如果本轮检查的已过期 key 的数量,超过 5 个(20/4),也就是「已过期 key 的数量」占比「随机抽取 key 的数量」大于 25%,则继续重复步骤 1;如果已过期的 key 比例小于 25%,则停止继续删除过期 key,然后等待下一轮再检查。
可以看到,定期删除是一个循环的流程。那 Redis 为了保证定期删除不会出现循环过度,导致线程卡死现象,为此增加了定期删除循环流程的时间上限,默认不会超过 25ms。
Redis的内存淘汰策略有哪些?
1、不进行数据淘汰的策略
noeviction(Redis3.0之后,默认的内存淘汰策略) :它表示当运行内存超过最大设置内存时,不淘汰任何数据,而是不再提供服务,直接返回错误。
2、进行数据淘汰的策略
针对「进行数据淘汰」这一类策略,又可以细分为「在设置了过期时间的数据中进行淘汰」和「在所有数据范围内进行淘汰」这两类策略。 在设置了过期时间的数据中进行淘汰:
- volatile-random:随机淘汰设置了过期时间的任意键值;
- volatile-ttl:优先淘汰更早过期的键值。
- volatile-lru(Redis3.0 之前,默认的内存淘汰策略):淘汰所有设置了过期时间的键值中,最久未使用的键值;
- volatile-lfu(Redis 4.0 后新增的内存淘汰策略):淘汰所有设置了过期时间的键值中,最少使用的键值;
在所有数据范围内进行淘汰:
- allkeys-random:随机淘汰任意键值;
- allkeys-lru:淘汰整个键值中最久未使用的键值;
- allkeys-lfu(Redis 4.0 后新增的内存淘汰策略):淘汰整个键值中最少使用的键值。
Redis缓存设计
缓存雪崩
通常redis中的数据都会设一个过期时间,当缓存数据过期后,用户访问的数据不在redis缓存中,业务系统会重新生成缓存,此时会访问数据库,并将数据更新到redis中,这样后续请求都可以直接命中redis缓存。
当大量的缓存数据在同一时刻过期(失效)时,此时如果有大量的用户请求,都无法在redis中处理,于是这些请求会全部访问数据库,从而导致数据库的压力骤增,严重会造成数据库宕机,从而形成一系列连锁反应,造成整个系统崩溃,这就是缓存雪崩。
解决方法:
- 将缓存失效时间随机打散:我们可以在原有的失效时间基础上增加一个随机值,这样每个缓存的过期时间就不会重复了,降低了缓存集体失效的概率。
- 设置缓存不过期:通过后台服务来更新缓存,从而避免因为缓存失效造成的缓存雪崩。
缓存击穿
redis缓存中的某个热点数据(被频繁访问的数据)过期了,此时大量的请求访问了该热点数据,就无法从缓存中读取,将直接访问数据集,数据库很容易被高并发的请求击垮,这就是缓存击穿问题。
解决方法:
- 互斥锁方案(Redis 中使用 setNX 方法设置一个状态位,表示这是一种锁定状态),保证同一时间只有一个业务线程请求缓存,未能获取互斥锁的请求,要么等待锁释放后重新读取缓存,要么就返回空值或者默认值。
- 不给热点数据设置过期时间,由后台异步更新缓存,或者在热点数据准备要过期前,提前通知后台线程更新缓存以及重新设置过期时间;
缓存穿透
当用户访问的数据,既不在缓存中,又不在数据库中,导致请求在访问缓存时发现缓存缺失,再去访问数据库,发现数据库中也没有要访问的数据,没办法构建缓存数据来服务后续的请求。那么当有大量的这样的请求来到时,数据库的压力骤增,这就是缓存穿透问题。
解决方法:
- 非法请求的限制:当有大量恶意请求访问不存在的数据的时候,也会发生缓存穿透,因此在 API 入口处我们要判断求请求参数是否合理,请求参数是否含有非法值、请求字段是否存在,如果判断出是恶意请求就直接返回错误,避免进一步访问缓存和数据库。
- 设置空值或者默认值:当我们线上业务发现缓存穿透的现象时,可以针对查询的数据,在缓存中设置一个空值或者默认值,这样后续请求就可以从缓存中读取到空值或者默认值,返回给应用,而不会继续查询数据库。
- 使用布隆过滤器快速判断数据是否存在,避免通过查询数据库来判断数据是否存在:我们可以在写入数据库数据时,使用布隆过滤器做个标记,然后在用户请求到来时,业务线程确认缓存失效后,可以通过查询布隆过滤器快速判断数据是否存在,如果不存在,就不用通过查询数据库来判断数据是否存在,即使发生了缓存穿透,大量请求只会查询 Redis 和布隆过滤器,而不会查询数据库,保证了数据库能正常运行,Redis 自身也是支持布隆过滤器的。
如何设计一个缓存策略,可以动态缓存热点数据呢?
通过数据最新访问时间来做排名,并过滤掉不常访问的数据,只留下经常访问的数据。
比如设计一个电商平台中缓存只存用户经常访问的Top 1000的商品:
- 先通过缓存做一个排序队列,存放1000件商品,系统会根据最近访问时间做一个排序,越是访问时间越近的排在越前面;
- 同时系统会定时过滤掉最后200个商品,然后再从数据库随机选200个商品进入缓存队列中;
- 这样当请求每次到达时,会先从队列中获取商品ID,如果命中,就根据ID再从另一个缓存数据结构中读取实际的商品信息并返回。
在redis中可以用zadd方法和zrange方法来完成排序队列和获取200个商品的操作。
常见的缓存更新策略
- Cache Aside(旁路缓存)策略
- Read/Write Through(读穿/写穿)
- Write Back(写回)
实际开发中用的是Cache Aside策略。
- Cache Aside策略
应用程序直接与数据库、缓存交互,并负责对缓存的维护,该策略又可以细分为读策略和写策略。
写策略的步骤:
- 先更新数据库中的数据,再删除缓存中的数据。
读策略的步骤:
- 如果读取的数据命中了缓存,则直接返回数据;
- 如果读取的数据没有命中缓存,则从数据库中读取数据,然后将数据写入到缓存,并且返回给用户。
注意,写策略的步骤的顺序不能倒过来,即不能先删除缓存再更新数据库,原因是在「读+写」并发的时候,会出现缓存和数据库的数据不一致性的问题。
!!!如何保证缓存和数据库数据一致性
引入了缓存,那么在更新数据时,也要更新缓存,这两个更新操作存在前后顺序的问题,也就是说依然存在由并发操作所带来的问题:
- 先更新数据库,再更新缓存;

可以看到,此时数据库数据为2,缓存数据为1,数据不一致。
- 先更新缓存,再更新数据库;

数据库数据为1,缓存数据为2,出现了缓存和数据库中的数据不一致的问题。
在更新数据时,不更新缓存,而是删除缓存中的数据。然后,到读取数据时,发现缓存中没了数据之后,再从数据库中读取数据,更新到缓存中。这个策略是Cache Aside策略。
- 先删除缓存,在更新数据库

仍然存在缓存和数据库的数据不一致性。
- 先更新数据库,再删除缓存

从理论上说,先更新数据库,再删除缓存还是会出现数据不一致的问题,但在实际中,这个问题出现的概率并不高。因为缓存写入通常远远快于数据库的写入。
如何保证「先更新数据库,在删除缓存」这两个操作都能执行成功呢?
- 重试机制
可以引入消息队列,将第二个操作(删除缓存)要操作的数据加入到消息队列,由消费者来操作数据。
- 如果删除缓存操作失败,那么从消息队列中重新读取数据,然后再次删除缓存,这个就是重试机制。重试超过一定次数,需要向业务层发送错误消息。
- 如果删除成功,那么将数据从消息队列中移除,避免重复操作。

- 订阅Mysql binlog,再操作缓存
第一步更新数据库会产生一条binlog日志,可以通过订阅binlog日志,拿到具体要操作的数据,然后再执行缓存删除。
阿里巴巴的canal中间件就是基于这个实现的。
Canal 模拟 MySQL 主从复制的交互协议,把自己伪装成一个 MySQL 的从节点,向 MySQL 主节点发送 dump 请求,MySQL 收到请求后,就会开始推送 Binlog 给 Canal,Canal 解析 Binlog 字节流之后,转换为便于读取的结构化数据,供下游程序订阅使用。
下图是 Canal 的工作原理:
所以,如果要想保证「先更新数据库,再删缓存」策略第二个操作能执行成功,我们可以使用「消息队列来重试缓存的删除」,或者「订阅 MySQL binlog 再操作缓存」,这两种方法有一个共同的特点,都是采用异步操作缓存。
Redis实战
Redis如何实现延迟队列
延迟队列是指把当前要做的事情,往后推迟一段时间再做。延迟队列的常见使用场景有以下几种:
- 在淘宝、京东等购物平台上下单,超过一定时间未付款,订单会自动取消;
- 打车的时候,在规定时间没有车主接单,平台会取消你的单并提醒你暂时没有车主接单;
- 点外卖的时候,如果商家在10分钟还没接单,就会自动取消订单;
在 Redis 可以使用有序集合(ZSet)的方式来实现延迟消息队列的,ZSet 有一个 Score 属性可以用来存储延迟执行的时间。
使用 zadd score1 value1 命令就可以一直往内存中生产消息。再利用 zrangebysocre 查询符合条件的所有待处理的任务, 通过循环执行队列任务即可。
Redis的大key如何处理?
什么是大key?
大 key 并不是指 key 的值很大,而是 key 对应的 value 很大。
一般而言,下面这两种情况被称为大 key:
- String 类型的值大于 10 KB;
- Hash、List、Set、ZSet 类型的元素的个数超过 5000个;
大key会造成什么问题?
大 key 会带来以下四种影响:
- 客户端超时阻塞。由于 Redis 执行命令是单线程处理,然后在操作大 key 时会比较耗时,那么就会阻塞 Redis,从客户端这一视角看,就是很久很久都没有响应。
- 引发网络阻塞。每次获取大 key 产生的网络流量较大,如果一个 key 的大小是 1 MB,每秒访问量为 1000,那么每秒会产生 1000MB 的流量,这对于普通千兆网卡的服务器来说是灾难性的。
- 阻塞工作线程。如果使用 del 删除大 key 时,会阻塞工作线程,这样就没办法处理后续的命令。
- 内存分布不均。集群模型在 slot 分片均匀情况下,会出现数据和查询倾斜情况,部分有大 key 的 Redis 节点占用内存多,QPS 也会比较大。
如何找到大key
1、redis-cli –bigkeys 查找大key
2、使用 SCAN 命令查找大 key
3、使用 RdbTools 工具查找大 key
如何删除大key
Redis管道有什么用?
Pipeline是客户端提供的一种批处理技术,用于一次性处理多个Redis命令,从而提高整个交互的性能。
普通命令模式:
管道模式:
使用管道技术可以解决多个命令执行时的网络等待。它是把多个命令整合到一起发送给服务器端处理之后统一返回给客户端,这样就免去了每条命令执行后都要等待的情况,从而有效地提高了程序的执行效率。
但使用管道技术也要注意避免发送的命令过大,或管道内的数据太多而导致的网络阻塞。
要注意的是,管道技术本质上是客户端提供的功能,而非 Redis 服务器端的功能。
Redis事务支持回滚吗?
Redis 中并没有提供回滚机制,虽然 Redis 提供了 DISCARD 命令,但是这个命令只能用来主动放弃事务执行,把暂存的命令队列清空,起不到回滚的效果。
如何用Redis实现分布式锁
Redis 本身可以被多个客户端共享访问,正好就是一个共享存储系统,可以用来保存分布式锁,而且 Redis 的读写性能高,可以应对高并发的锁操作场景。
Redis 的 SET 命令有个 NX 参数可以实现「key不存在才插入」,所以可以用它来实现分布式锁:
- 如果 key 不存在,则显示插入成功,可以用来表示加锁成功;
- 如果 key 存在,则会显示插入失败,可以用来表示加锁失败。

Redis分布式锁的优缺点?
07月16日 TODO
