🌸个人主页:https://blog.csdn.net/2301_80050796?spm=1000.2115.3001.5343
🏵️热门专栏:
🧊 Java基本语法(97平均质量分)https://blog.csdn.net/2301_80050796/category_12615970.html?spm=1001.2014.3001.5482
🍕 Collection与数据结构 (93平均质量分)https://blog.csdn.net/2301_80050796/category_12621348.html?spm=1001.2014.3001.5482
🧀线程与网络(96平均质量分) https://blog.csdn.net/2301_80050796/category_12643370.html?spm=1001.2014.3001.5482
🍭MySql数据库(93平均质量分)https://blog.csdn.net/2301_80050796/category_12629890.html?spm=1001.2014.3001.5482
🍬算法(97平均质量分)https://blog.csdn.net/2301_80050796/category_12676091.html?spm=1001.2014.3001.5482
🍃 Spring(97平均质量分)https://blog.csdn.net/2301_80050796/category_12724152.html?spm=1001.2014.3001.5482
🎃Redis(97平均质量分)https://blog.csdn.net/2301_80050796/category_12777129.html?spm=1001.2014.3001.5482
🐰RabbitMQ(97平均质量分) https://blog.csdn.net/2301_80050796/category_12792900.html?spm=1001.2014.3001.5482
感谢点赞与关注~~~
目录
- [1. 分布式锁](#1. 分布式锁)
-
- [1.1 什么是分布式锁](#1.1 什么是分布式锁)
- [1.2 分布式锁的基本实现](#1.2 分布式锁的基本实现)
- [1.3 引入过期时间](#1.3 引入过期时间)
- [1.4 引入校验id](#1.4 引入校验id)
- [1.5 引入lua脚本](#1.5 引入lua脚本)
- [1.6 引入看门狗](#1.6 引入看门狗)
- [1.7 引入Redlock算法](#1.7 引入Redlock算法)
- [2. Redis常见面试题](#2. Redis常见面试题)
1. 分布式锁
1.1 什么是分布式锁
在一个分布式的系统中,也会涉及到多个结点访问同一个公共资源的情况,此时就需要通过锁来做互斥控制,避免出现类似与"线程安全"的问题.
而我们之前在多线程章节学习过的synchronized这样的锁只能在当前进程中生效,在分布式这种多个进程多个主机的场景之下就无能为力了.
此时我们就会使用到分布式锁.
1.2 分布式锁的基本实现
我们在实现分布式锁的时候,思路非常简单,就是通过一个键值对来标识锁的状态.
举个例子,我们在购买车票的时候,存在多个服务器节点,都可能需要处理这个买票的逻辑:先查询指定车次的余票,如果余票>0,则设置余票值-=1.
如果有一个客户端请求服务器1去执行买票逻辑,同时也有一个客户端请求服务器2去执行买票逻辑,两台服务器都发现余票的数量大于0,就都会对余票-=1.显然上述的场景是存在线程安全问题的.
那么此时如果进行加锁呢,我们可以在上述的架构中引入一个Redis,作为分布式锁的管理器.所谓分布式锁,也是一个/一组单独的服务器程序,给其他的服务器提供加锁的服务.
买票服务器在进行买票操作的过程中,就需要先加锁,就是往Redis上设置一个特殊的key-value完成上述买票的操作,再把这个key-value删除掉.其他的服务器也想买票的时候,也去Redis上设置key-value,如果发现key-value已经存在,就会认为"加锁失败" .这样就可以保证第一个服务器执行"查询->更新"的过程中,第二个服务器就不会执行"查询",也就解决了超卖的问题.
我们使用Redis中的setnx操作就可以完成"加锁"的效果,其中nx就是在一个键值对存在的时候,就不会再对当前的key进行设置,针对解锁就可以使用del命令来完成.
1.3 引入过期时间
但是上述的场景中仍然存在一些问题,如果某个服务器加锁成功了(setnx操作),执行后续逻辑的过程中,程序发生了崩溃,也就是没有执行到解锁.这样的情况就会导致Redis上设置的key无人删除,也就导致了其他服务器无法获取到锁了.
为了解决这样的问题,我们就可以设置key的同时引入过期时间,即这个锁最多持有多久就应该被释放掉.
我们就可以使用set ex nx
这样的命令来完成.
注意不可以使用setnx+expire
来操作,因为Redis上的多个命令之间无法保证是原子性的,可能setnx成功了,expire失败了.也就没有设置过期时间.
1.4 引入校验id
是否可能会出现这样的一种场景,服务器1执行了加锁操作,服务器2执行了解锁操作.对于Redis中写入的加锁键值对,其他的结点也是可以删除的.
为了解决上述的问题,我们可以引入一个校验id.比如可以把设置的键值对的值,不再是简单的设置为一个随机的值,而是设置为服务器的编号,形如"001".
在进行加锁的时候,设置key-value,key对应着要对数据库中哪个资源进行加锁,value就可以存储刚才的服务器的编号,标识出当前这个锁是那个服务器加上的.这样在后续解锁的时候,就可以进行校验了.
解锁的时候,首先查询一下这个锁对应的服务器编号,然后判定一下这个编号是否就是当前执行的解锁的服务器编号,如果是,才能真正执行del,如果不是,就会删除失败.
上述的逻辑可以使用伪代码来表示:
String key = [要加锁的资源 id];
String serverId = [服务器的编号];
// 加锁, 设置过期时间为 10s
redis.set(key, serverId, "NX", "EX", "10s");
// 执⾏各种业务逻辑, ⽐如修改数据库数据.
doSomeThing();
// 解锁, 删除 key. 但是删除前要检验下 serverId 是否匹配.
if (redis.get(key) == serverId) {
redis.del(key);
}
1.5 引入lua脚本
但是很明显,解锁的逻辑是两步操作,"get"和"del",这样的操作并非是原子的.一个服务器内部也可能是多线程的,此时就可以同一个服务器,两个线程都在执行上述的解锁操作.
为了使得上述操作是原子的,我们我们就可以引入lua脚本.lua脚本非常的轻量,我们可以使用lua脚本编写一些逻辑,把get操作和del操作设置为一个原子的操作(Redis执行lua脚本的过程中,也是原子的,相当于执行一条命令一样),之后就可以把这个脚本上传到Redis服务器上,然后就可以让客户端来控制Redis执行上述脚本了.
lua
if redis.call('get',KEYS[1]) == ARGV[1] then
return redis.call('del',KEYS[1])
else
return 0
end;
1.6 引入看门狗
上述方案仍然存在一个重要的问题,当我们设置了key的过期时间之后,任然可能存在一定的问题,比如当前任务还没有执行完,key就先过期了,这就导致锁提前失效了.
过期时间设置多长时间合适,看具体的场景,如果设置太短就可能出现上面的问题,如果设置的太长,也会导致锁释放不及时的问题.
针对上述的场景,最好的方式就是**"动态续约"**,动态续约往往也需要服务器这边有一个专门的线程,负责续约这个事情,把这个负责续约的线程,叫做"看门狗".
初始情况下,设置一个过期时间(比如设置1s),在还剩300ms的时候,如果当前任务还没有执行完毕,就把过期时间再次续上1s,等到时间快到了,任务还没有执行完,就会再次延续时间.
就像我们吃自助餐一样,老板呼吁我们少量多次地拿
1.7 引入Redlock算法
使用Redis作为分布式锁,Redis本身有没有可能挂掉呢,是有可能的.
服务器1向master节点进行加锁操作.这个写入key的过程刚刚完成,master挂了;slave节点升级成了新的master节点.但是由于刚才写⼊的这个key尚未来得及同步给slave呢,此时就相当于服务器1的加锁操作形同虚设了,服务器2仍然可以进行加锁(即给新的master写入key.因为新的master不包含刚才的key).
为了解决上述的问题,Redis提供了Redlock算法.
我们引入了一组Redis的结点,其中Redis的结点都包含了一个主节点和若干从结点,并且组合组之间存储的数据都是一致的,相互之间是"备份"的关系...加锁的时候,按照一定的顺序,写多个master结点,在上锁的时候需要设定操作的"超时时间".比如50ms.即如果setnx操作超过了50ms还没有成功,就视为加锁失败.
如果给某个接地那加锁失败,就立即在尝试下一个结点.
当加锁成果的节点数超过总结点数的一半,才会视为加锁成功.
这样的话,即使某些结点挂了,也不会影响锁的正确性.
同样,释放锁的时候,也需要把所有的接地那都进行解锁(即使是之前超时的结点,也要尝试解锁,尽量保证逻辑严密).
2. Redis常见面试题
- 介绍一下什么是Redis,有什么特点?
Redis是一个高性能的key-value的内存数据库,Redis主要使用内存存储数据,不同与传统数据库的表结构,Redis存储数据通常是使用键值对的形式来存储数据的. - Redis的五大数据类型
- Redis数据类型底层的编码方式是怎么样的?
- raw
这个是最基本的字符串,类似与Java中的byte[ ]数组. - int
redis有时会实现一些计数的功能,当value是整数的时候,redis就会直接用int类型来保存. - embstr
当字符串比较短的时候,redis就会把字符串优化为embstr. - hashtable
最基本的hash表,这里不要误会,这和Java中那个线程安全的HashTable不一样. - ziplist
当hash表中的元素比较少的时候,会被压缩为ziplist. - linkedlist
链表 - ziplist
压缩列表,但是在redis3.2之后就引入了quicklist,它兼顾了linkedlist和ziplist两者的优点. - intset
当set中存储的都是整数的时候,会被优化为intset. - skiplist
跳表,这个跳表有点类似与链表中的复杂链表,结点中存储着下一个结点的引用,然后还存储着随机一个结点的引用.
我们可以使用object encoding
来查看value中存储数据的实际类型.
shell
127.0.0.1:6379> set key1 1
OK
127.0.0.1:6379> object encoding key1
"int"
- zset为什么使用跳表,而不是使用红黑树来实现.
跳表的插入/查询/删除时间复杂度都是O(logN),和红黑树是⼀样的.
但是跳表实现起来更简单.而且不需要重新平衡这样的操作. - Redis的常见应用场景有哪些?
- 缓存: 一些经常被访问到的热点数据,可以使用Redis进行缓存,降低查询数据库的次数.
- 排行榜: 可以基于Redis的Zset轻松实现.
- 分布式锁: 见上面
- 分布式会话: 使用Redis存储会话信息,可以使得用户访问到系统的不同模块时都使用同一个公共的会话.比如存储用户登录的令牌.
- 消息队列: Redis自身支持Stream数据类型,可以作为简单的消息队列使用.
- Redis为什么是单线程模型.
Redis内部的逻辑比较简单,一般的性能瓶颈都是出现在内存上或者IO上,因此使用多线程并没有太大的收益,反而可能会引来一些线程安全问题.
但是说redis只有单线程,其实也有使用多线程的地方,在网络io中使用的就是多路复用的情况. - Redis的持久化机制.
RDB和AOF - RDB持久化触发的条件是怎样的.
可以手动触发,使用save命令或者是bgsave命令来触发.
自动触发,可以在配置文件中通过save m n
即可设定m秒内发生n次修改,就触发持久化.重新启动Redis服务器会触发持久化.Redis进行主从复制的时候,主结点会自动生成RDB快照,然后把RDB快照通过网络发送给从结点. - AOF的文件同步策略有哪些?
AOF机制在备份数据的时候,不是直接让工作线程写入硬盘,而是先写入一个内存中的缓存区,积累一波之后,再统一写入硬盘.这就使得写硬盘的次数大大减少.
Redis就给出了一些同步硬盘中AOF文件的频率选项,也就是更新缓冲区数据的频率选项.
- AOF的重写机制是怎样的.
,在AOF中记录的是我们操作Redis的中间过程.实际上,Redis在重新启动的时候,关注的是最终的结果.这时候,AOF就可以把一些冗余的数据进行优化,剔除其中的冗余操作,并且合并一些操作,达到给AOF瘦身的效果,这就是AOF的重写机制.
比如set key 111和set key 222,这两个操作只会在AOF中保存set key 222这个操作. - key的过期删除策略是怎样的?
redis中有好多的键值对,redis怎么知道哪个过期了,哪个没有过期呢?绝对不是一个一个的遍历来实现的,这样的效率非常低下.redis采用了一下的策略:
- 惰性删除
假设一个key已经到了过期的时间,但是我们先不要删除这个key,当下次访到这个key之后,查看这个key是否过期,如果过期了,就删除掉. - 定期删除
redis会对过期的key进行定期删除,但是这里的删除并不是吧key全部遍历之后在找到过期的key进行删除,而是抽取一些key来判断是否过期,之后进行对应的删除. - 内存淘汰策略
FIFO(first in first out) 先进先出
把缓存中存在时间最久的(先进来的数据)数据淘汰掉.
LRU(least recently used) 淘汰最近未使用的
记录每个key的最近访问时间,把最近访问的时间最早的key淘汰掉.
LFU(least frequently used) 淘汰访问次数最少的
记录每个key最近一段时间的访问次数,把访问次数最少的淘汰掉.
Random 随机淘汰
随机抽取一名幸运儿淘汰掉
举例说明: 去商店买东西
当我们拿了一件商品之后,在结账的时候,我们发现这件商品过期了,这时候我们会告诉老板这件商品过期了,老板这时候就会把这件商品扔掉,这就是惰性删除.其次老板会定期抽取一部分的商品来查看商品是否过期,过期就会人掉,这就是定期删除.
- Redis的内存淘汰策略是怎样的?/如果Redis的内存用完了,会出现什么情况?
FIFO(first in first out) 先进先出
把缓存中存在时间最久的(先进来的数据)数据淘汰掉.
LRU(least recently used) 淘汰最近未使用的
记录每个key的最近访问时间,把最近访问的时间最早的key淘汰掉.
LFU(least frequently used) 淘汰访问次数最少的
记录每个key最近一段时间的访问次数,把访问次数最少的淘汰掉.
Random 随机淘汰
随机抽取一名幸运儿淘汰掉 - 如果大量的key在同一时间过期,会产生什么问题?如何处理?
会出现缓存雪崩问题,Redis上大规模的key失效,导致了缓存命中率陡然下降,让数据库的压力陡然上升,甚至发生宕机.
- 造成上述这种问题的一般有两种情况:
Redis服务器宕机,或者是Redis的集群大量服务器宕机
短时间之内,设置了很多过期时间相同的key.过期的时候也正好是同一时间过期. - 解决上述问题的主要方式是:
加强监控报警,保证Redis集群的可用性.
我们可以选择不给Redis的数据设置过期时间或者是设置过期时间的时候,加入一些随机的因子(避免同一时间过期).
- Redis为什么把数据放到内存中?
访问内存的速度比访问硬盘的速度3-4个数量级.为了使得Redis访问数据的操作加快,我们选择把数据存储到内存中.当然使用内存存储的劣势还是较为明显的:
- 内存的存储空间比硬盘中小的多,不可存储太多的数据
- 内存的数据不可以持久存储,掉电后数据就会丢失.
- Redis的主从同步/主从复制是怎么回事?
顾名思义就是有的服务式"主"结点.有的是"从"结点.这时候,从结点就必须听主节点的,从结点的数据要跟随主节点变化,从结点的数据需要和主节点保持一致.也就是主节点原有的数据,在与从结点建立连接之后,就需要把主节点上面的数据复制到从结点上,后续主结点这边对于数据有任何修改,都会把这样的修改也同步给从结点上.
后续在对数据进行操作的时候.主节点可以读数据,也可以写数据,而从结点只能写数据.一方面,主节点的数据就有了多个备份,主节点挂掉之后任然可以通过从结点来读取数据.
另一方面访问Redis大多数还是读操作,通过从结点就可以分担主节点的读操作能力. - 介绍一下Redis哨兵.
当主节点出现故障时,哨兵能够自动完成故障的发现和故障的转移,并通知应用方,从而实现真正的高可用.
哨兵通过与数据存储结点建立tcp长连接,并定期向数据节点发送心跳包,来保证数据节点还在运行状态.如果从结点挂掉了,其实没有关系,让主节点在进行一下同步就好了.如果是主节点挂了,这时候哨兵就要发挥作用了.大体的恢复流程如下.
- 多个哨兵节点发送心跳包,主节点无响应,达成主节点挂掉的共识.
- 在这些哨兵中,会选举出一个Leader,这个Leader负责从现有的从结点中,挑选一个结点作为新的主节点.
- 挑选出新的主节点之后,哨兵节点就会自动控制被选中的这个节点,执行slaveof no one,并控制其他从节点,修改slaveof到这个新的主节点上.
- 如果Redis哨兵发现主节点宕机了,接下来会做哪些事情?
- 判断主观下线
哨兵节点通过给redis-server发送心跳包,判定redis服务器是否正常工作,心跳包如果没有得到响应,这个哨兵就会认为当前服务器挂了.但也只是当前服务器认为挂了而已. - 判断客观下线
如果多个哨兵通过投票,都认为服务器挂了,而且超过了我们之前在yml配置文件中写入的法定票数.哨兵们就会一直认为服务器挂掉了. - 选取哨兵节点中的Leader
多个节点中的哨兵需要通过Raft算法来选举出一个哨兵节点中的Leader,来负责数据节点中的slave到master的提拔过程,这个算法总结下来就是,手快有手慢无. - Leader哨兵提拔合适的slave为master,提拔的原则就是:
首先看优先级,每个redis的数据节点,都会在配置文件中,有一个优先级设置----slave-priority,谁的优先级越高,谁就越会胜出. - 之后看从结点的数据复制的偏移量,即offset,offset越大说明从主节点中复制的数据越多,和主节点的数据越接近,就会优先选取offset大的来当master.
其次看的就是runid,runid在每个Redis节点启动的时候,都会自动随机生成一串数字.也就是随便挑一个了. - 新的主节点设置好了,Leader会控制这个主节点执行slave no one的命令,称为master,之后再控制其他的从结点,slaveof这个新的主节点.
- Redis的集群是干什么的?
集群模式之下,主要解决存储空间不足的问题,即拓展存储空间 .
集群中主要要解决的问题就是要引入多台机器,每台机器存储一部分数据.只要机器规模足够大,就可以存储任意数据的大小了.比如整个数据全集是1TB,引入三组Master/Salve来存储(注意是三组集群,每组集群中由一个主节点和若干从结点组成,集群中各个服务器存储的数据是相同的).那么每一组机器只需要存储整个数据全集的1/3即可. - Redis的哈希槽是怎么回事?
其实这种算法就是把一致性哈希和哈希求余两种方式结合一下.也就是把哈希值映射到16384个槽位上.
然后再把这些槽位均匀地分给每个分片(每个集群就是一个数据分片),每个分片的接地那都需要记录自己持有哪些分片.每个分片都会使用"位图"这样的数据结构来表示出当前有多少槽位号,每一位用0/1来区分自己这个分片当前是否持有该槽位号.
如果后需要进行扩容,比如新增一个分片,就可以针对原有的槽位进行重新分配.可以把之前的每个分片持有的槽位各拿出一点,分给新的分片. - Redis集群会有操作丢失吗?
Redis并不能保证数据的强一致性,这意味着实际中集群在特定的条件下可能会丢失写操作.
比如在写成功一个key之后,正好主节点宕机,此时由于这个数据还没有来得及同步到从结点上,也没有来得及写入AOF日志或者是RDB文件,就会丢失. - 介绍一下一致性哈希算法.
在一致性哈希这种算法中,数据是连续存储的.
- 首先把数据空间全部映射到一个圆环上,数据按照顺时针方向增长.
- 假设当前存在三个分片,把分片放到圆环的某个位置.
- 假定有一个key,通过哈希函数计算得到的哈希值H,之后把计算出的H映射到圆盘上对应数据的位置.之后从H所在的位置,顺时针向下找,找到的第一个分片,就是该key所从属的分片.
- 在这种一致性哈希算法情况下,如果想要对数据进行扩容,我们需要如何处理呢?原有分片在环上的位置不动,只需要在换上安排一个新的分片即可.
- 如何理解Redis的事务,和MySQL的事务有什么区别?
Redis相比于MySQL的事务,就显得非常逊色.
- 首先原子性,Redis的事务,是否存在原子性,存在争议.Redis的事务虽然和MySQL一样,都是把多个操作打包为了一个操作.但是MySQL的事务在其中的操作出现一些异常的时候,数据都会回滚回去.要不全都执行成功,要不全部执行不成功.但是Redis中的事务如果有操作执行失败的时候,不具有回滚机制.也就是Redis的事务不具有要不都执行成功,要不都执行不成功的特性.Redis的这个原子性也就相当于MySQL原子性的一个半成品.
- 其次一致性,由于Redis没有约束,也没有回滚机制,如果事务执行过程中某个操作出现了失败,就可能引起数据不一致的情况.
- 不具备持久性,Redis本身就是内存数据库,数据是存在内存中的.虽然事务具有持久化的机制,但是这和事务没有关系.
- 最后不具备隔离性,Redis是一个单线程模型的服务程序,不存在并发的过程,所有的请求/事务都是串行化执行的.
- Redis和事务相关的命令都有哪些?
- 开启事务multi.
- 执行事务exec
- 放弃当前事务discard
- 监视执行事务前后key是否发生改变watch.
- 如何使用Redis实现分布式锁?
参考上文. - 什么是缓存穿透,缓存雪崩,缓存击穿?
- 缓存穿透: 我们在Redis中查询某个key,在Redis中没有,在mysql中也没有,这个key肯定也不会被更新到Redis中.这次查询没有,下次查还没有,如果像这样的数据存在很多,并且还反复查询,一样会给mysql带来很大的压力.
- 缓存雪崩:由于在短时间内,Redis上大规模的key失效,导致了缓存命中率陡然下降,让数据库的压力陡然上升,甚至发生宕机.
- 缓存击穿:缓存击穿相当于缓存雪崩的一种特殊情况,雪崩是大量的key发生了过期,但是穿透与雪崩的区别就是,只是热点数据突然发生了过期,这就会使得大量的请求打在了数据库上,导致数据库发生宕机.相比雪崩,击穿过期的是热点数据,访问频率更高,影响更大.
- 什么是热key问题?如何解决?
有些key的访问频率非常高,称为热key.
有些热key可能达到非常热的情况,以一己之力就可以把Redis打挂.
解决方式就是:
- 进一步扩大Redis集群的规模,尤其是对热key所属分片,部署更多的slave节点分担读取压力.
- 应用服务器对热key识别出来,把热key使用单独的Redis集群部署,并赋予更多的机器.
- 应用服务器对热key识别出来,并单独进行二次哈希,也就相当于把一个key分散到多个Redis集群上存储.
- 使用应用程序本地缓存,降低Redis的压力.
- 如何实现Redis高可用
主从模式+哨兵模式+集群模式. - Redis和MySQL如何保证双写一致性?
- 首先什么是双写一致性?
当用户修改数据的时候,需要修改数据库,同时也需要更新缓存中的数据.
否则再直接读缓存中的数据就是脏数据.
但是如果直接写数据库并且写Redis,此时万一有一方写入失败,就容易出现数据不一致的情况. - 如何解决?
方案一: 延时双删,先删除缓存中的数据,再更新数据库,再次删除缓存数据.
双删的目的就是多一重保证,如果第一次删除失败了,第二次删除任然可以兜底.
只要把Redis的数据删了,后续访问该数据的时候就会自动的从数据库中读取,并且把结果也写入Redis了,就可以保证Redis和数据库的一致性.
如果直接修改Redis,由于修改Redis和修改MySQL的操作并非是原子的,多个应用服务器并发执行的时候,就可能会出现类似于"线程安全"问题.
比如服务器1和服务器2都写Redis和MySQL,其中服务器1写Redis的结果被服务器2的结果覆盖掉了,服务器2写MySQL的结果被服务器1写MySQL的结果覆盖掉了,此时就会发生数据不一致.
但是否可能第二次删除也失败了呢?答案是有可能的,只是概率大大降低了.
方案二:删除缓存重试
先删除缓存数据,如果删除失败,则把失败的key放到一个mq中,稍后进行重试.如果删除不成功,就会一直在mq中重试.
上述的操作看起来有些复杂,但是有一些大佬把这些操作已经封装好了.我们可以直接使用.
例如阿里巴巴提供了开源工具canal,可以比较方便的获取到mysql的binlog,并基于此实现上述逻辑.
- 生成RDB期间,Redis是否可以处理写请求?
分为两种情况,
- 执行save指令,这个操作会阻塞Redis主线程,此时无法处理外界的请求.
- 执行bgsave命令,Redis会生成子进程,子进程去写RDB文件,父进程还是用来接收客户端的请求.
- Redis用到的网络协议是怎样的?
RSP协议,这个是Redis专门实现的应用层协议,用于Redis客户端和服务器之间的通信.是一种纯文本协议. - Redis如何遍历key?
使用keys *
虽然可以一次性获取到所有的key,但是开销比较大,会把Redis卡死.
更靠谱的方法是使用渐进式遍历,即使用scan
命令.
SCAN cursor [MATCH pattern] [COUNT count]
每次scan都能返回一批keys同时告诉我们下次应该从那个光标(一个整形)开始进行scan.需要使用多次scan才可以完成整个遍历.如果返回0,说明遍历完成. - 如何解决Redis的bigkey问题
bigkey问题指的是某个key对应的value占据比较多的空间.比如value是字符串类型,是一个非常长的字符串,或者是一个hash类型,里面的元素特别多.
- 解决方案
核心思路就是拆分,把一个大的key拆分为多个小的key,每个key对应value的一部分数据.