redis
-
Redis 数据结构和底层实现
-
string:简单动态字符串SDS,Redis 的字符串是动态字符串,是可以修改的字符串,内部结构实现上类似于ArrayList,采用预分配冗余空间的方式来减少内存的频繁分配。
-
list:
列表相当于 LinkedList,注意它是链表而不是数组。这意味着 list 的插入和删除操作非常快,时间复杂度为 O(1),但是索引定位很慢,时间复杂度为 O(n)。
在列表元素较少的情况下会使用一块连续的内存存储,这个结构是 ziplist。它将所有的元素紧挨着一起存储,分配的是一块连续的内存。当数据量比较多的 时候才会改成 quicklist。
-
hash:相当于HashMap,它是无序字典,数组 + 链表二维结构。第一维 hash 的数组位置碰撞时,就会将碰撞的元素使用链表串接起来。
-
set:相当于HashSet,它内部的键值对是无序的唯一的,当于一个特殊的字典,字典中所有的 value 都是一个值 NULL。
-
zset:hash+skipList。跳跃列表就是类似于最下面一层所有的元素都会串起来。然后每隔几个元素挑选出一个代表来,再将这几个代表使用另外一级指针串起来。然后在这些代表里再挑出二级代表,再串起来。最终就形成了金字塔结构。<img src="/Users/anyao/Library/Application Support/typora-user-images/image-20200401193245067.png" alt="image-20200401193245067" style="zoom:50%;" />
-
-
zset 除了用跳表 还可以用什么实现?跳表的查询过程是怎么样的,查询和插入的时间复杂度?
-
平衡树。
-
查询过程:搜索路径。从 header 的最高层开始遍历找到第一个节点 (最后一个比「我」小的元素),然后从这个节点开始降一层再遍历找到第二个节点 (最 后一个比「我」小的元素),然后一直降到最底层进行遍历就找到了期望的节点 (最底层的最后一个比我「小」的元素)。
-
时间复杂度:log2n
-
-
skiplist与平衡树、哈希表的比较
skiplist和各种平衡树(如AVL、红黑树等)的元素是有序排列的,而哈希表不是有序的。因此,在哈希表上只能做单个key的查找,不适宜做范围查找。所谓范围查找,指的是查找那些大小在指定的两个值之间的所有节点。
在做范围查找的时候,平衡树比skiplist操作要复杂。在平衡树上,我们找到指定范围的小值之后,还需要以中序遍历的顺序继续寻找其它不超过大值的节点。如果不对平衡树进行一定的改造,这里的中序遍历并不容易实现。而在skiplist上进行范围查找就非常简单,只需要在找到小值之后,对第1层链表进行若干步的遍历就可以实现。
平衡树的插入和删除操作可能引发子树的调整,逻辑复杂,而skiplist的插入和删除只需要修改相邻节点的指针,操作简单又快速。
从内存占用上来说,skiplist比平衡树更灵活一些。一般来说,平衡树每个节点包含2个指针(分别指向左右子树),而skiplist每个节点包含的指针数目平均为1/(1-p),具体取决于参数p的大小。如果像Redis里的实现一样,取p=1/4,那么平均每个节点包含1.33个指针,比平衡树更有优势。
查找单个key,skiplist和平衡树的时间复杂度都为O(log n),大体相当;而哈希表在保持较低的哈希值冲突概率的前提下,查找时间复杂度接近O(1),性能更高一些。所以我们平常使用的各种Map或dictionary结构,大都是基于哈希表实现的。
从算法实现难度上来比较,skiplist比平衡树要简单得多。
-
Redis的持久化方式,底层实现原理。
Redis 的持久化机制有两种,第一种是快照,第二种是 AOF 日志。快照是一次全量备份,AOF日志是连续的增量备份。快照是内存数据的二进制序列化形式,在存储上非常紧凑,而 AOF 日志记录的是内存数据修改的指令记录文本。
- 快照原理
多进程 COW(Copy On Write) 。在持久化时会调用 glibc 的函数 fork 产生一个子进程,快照持久化完全交给子进 程来处理,父进程继续处理客户端请求。子进程刚刚产生时,它和父进程共享内存里面的代码段和数据段。子进程做数据持久化,它不会修改现有的内存数据结构,它只是对数据结构进行遍历读取,然后序列化写到磁盘中。
但是父进程不一样,它必须持续服务客户端请求,然后对内存数据结构进行不间断的修改。子进程做数据持久化,它不会修改现有的内存数据结构,它只是对数据结构进行遍历读取,然后序列化写到磁盘中。但是父进程不一样,它必须持续服务客户端请求,然后对内存数据结构进行不间断的修改。
- AOF 原理
AOF日志只记录对内存进行修改的 指令记录。在持久化时会调用 glibc 提供了 fsync(int fd)函数可以将指定文件的内容强制从内核缓存刷到磁盘。收到客户端修改指令后,先进行参数校验,如果没问题,就立即将该指令文本存储到 AOF 日志中,也就是先存到磁盘,然后再执行指令。
AOF 日志是以文件的形式存在的,当程序对 AOF 日志文件进行写操作时,实际上是将 内容写到了内核为文件描述符分配的一个内存缓存中,然后内核会异步将脏数据刷回到磁盘的。
-
缓存一致性:分布式锁
- 写操作
- 清除缓存;若失败则返回错误信息(本次写操作失败)。
- 对key加分布式锁。
- 更新数据库;若失败则返回错误信息(本次写操作失败)同时释放锁,此时数据弱一致。
- 更新缓存,即使失败也返回成功,同时释放锁,此时数据弱一致。
- 读操作
- 查询缓存,命中则直接返回结果。
- 对key加分布式锁。
- 查询数据库,将结果直接写入缓存,返回结果,同时释放锁。
- 写操作
-
线程模型
Redis基于Reactor模式开发了网络事件处理器,这个处理器被称为文件事件处理器。它的组成结构为4部分:多个套接字、IO多路复用程序、文件事件分派器、事件处理器。因为文件事件分派器队列的消费是单线程的,所以Redis才叫单线程模型。
<img src="/Users/anyao/Library/Application Support/typora-user-images/image-20200401212908515.png" alt="image-20200401212908515" style="zoom:50%;" />
文件事件处理器使用I/O多路复用(multiplexing)程序来同时监听多个套接字,并根据套接字目前执行的任务来为套接字关联不同的事件处理器。
当被监听的套接字准备好执行连接应答(accept)、读取(read)、写入(write)、关闭(close)等操作时,与操作相对应的文件事件就会产生,这时文件事件处理器就会调用套接字之前关联好的事件处理器来处理这些事件。
尽管多个文件事件可能会并发地出现,但I/O多路复用程序总是会将所有产生事件的套接字都推到一个队列里面,然后通过这个队列,以有序(sequentially)、同步(synchronously)、每次一个套接字的方式向文件事件分派器传送套接字:当上一个套接字产生的事件被处理完毕之后(该套接字为事件所关联的事件处理器执行完毕), I/O多路复用程序才会继续向文件事件分派器传送下一个套接字。
Redis的I/O多路复用程序的所有功能是通过包装select、epoll、evport和kqueue这些I/O多路复用函数库来实现的,因为Redis为每个I/O多路复用函数库都实现了相同的API,所以I/O多路复用程序的底层实现是可以互换的,如下图所示。
-
Redis为什么快
- 纯内存操作
- 单线程操作,避免了频繁的上下文切换
- 采用了非阻塞I/O多路复用机制
-
过期策略
redis只能存5G数据,可是你写了10G,那会删5G的数据。怎么删的,这个问题思考过么?还有,你的数据已经设置了过期时间,但是时间到了,内存占用率还是比较高,有思考过原因么?
-
redis采用的是定期删除+惰性删除策略。
-
为什么不用定时删除策略:定时删除,用一个定时器来负责监视key,过期则自动删除。虽然内存及时释放,但是十分消耗CPU资源。在大并发请求下,CPU要将时间应用在处理请求,而不是删除key,因此没有采用这一策略。
-
定期删除+惰性删除是如何工作的呢:定期删除,redis默认每个100ms检查,是否有过期的key,有过期key则删除。需要说明的是,redis不是每个100ms将所有的key检查一次,而是随机抽取进行检查(如果每隔100ms,全部key进行检查,redis岂不是卡死)。因此,如果只采用定期删除策略,会导致很多key到时间没有删除。
于是,惰性删除派上用场。也就是说在你获取某个key的时候,redis会检查一下,这个key如果设置了过期时间那么是否过期了?如果过期了此时就会删除。
-
采用定期删除+惰性删除就没其他问题了么:不是的,如果定期删除没删除key。然后你也没即时去请求key,也就是说惰性删除也没生效。这样,redis的内存会越来越高。那么就应该采用内存淘汰机制。
-
-
数据淘汰机制
实现 LRU 算法除了需要 key/value 字典外,还需要附加一个链表,链表中的元素按照一定的顺序进行排列。当空间满的时候,会踢掉链表尾部的元素。当字典的某个元素被访问 时,它在链表中的位置会被移动到表头。所以链表的元素排列顺序就是元素最近被访问的时间顺序。位于链表尾部的元素就是不被重用的元素,所以会被踢掉。位于表头的元素就是最近刚刚被人用过的元素,所以暂时不会被踢。
之所以不使用 LRU算法,是因为需要消耗大量的额外的内存。近似 LRU 算法则很简单,在现有数据结构的基础上使用随机采样法来淘汰元素,能达到和 LRU 算法非常近似的效果。Redis 为实现近似 LRU 算法,它给每个 key 增加了一个额外的小字 段,这个字段的长度是 24 个 bit,也就是最后一次被访问的时间戳。
当 Redis 执行写操作时,发现内存超出 maxmemory,就会执行一次 LRU 淘汰算法。这个算法也很简单,就是随机采样出 5(可以配置) 个 key,然后淘汰掉最旧的 key,如果淘汰后内存还是超出 maxmemory,那就继续随机采样淘汰,直到内存低于 maxmemory 为止。
-
如果Redis有1亿个key,使用keys命令是否会影响线上
Keys模糊匹配,请大家在实际运用的时候忽略掉。因为Keys会引发Redis锁,并且增加Redis的CPU占用,情况是很恶劣的。
SCAN命令是一个基于游标的迭代器(cursorbasediterator):SCAN命令每次被调用之后,都会向用户返回一个新的游标,用户在下次迭代时需要使用这个新游标作为SCAN命令的游标参数,以此来延续之前的迭代过程。
SCAN命令,以及其他增量式迭代命令,在进行完整遍历的情况下可以为用户带来以下保证:从完整遍历开始直到完整遍历结束期间,一直存在于数据集内的所有元素都会被完整遍历返回;这意味着,如果有一个元素,它从遍历开始直到遍历结束期间都存在于被遍历的数据集当中,那么SCAN命令总会在某次迭代中将这个元素返回给用户。 -
Redis连接时的connect与pconnect的区别
connect:脚本结束之后连接就释放了。
pconnect:脚本结束之后连接不释放,连接保持在php-fpm进程中。
所以使用pconnect代替connect,可以减少频繁建立redis连接的消耗。
-
RDB和AOF优缺点
- RDB的优点:
对性能影响最小。如前文所述,Redis在保存RDB快照时会fork出子进程进行,几乎不影响Redis处理客户端请求的效率。
每次快照会生成一个完整的数据快照文件,所以可以辅以其他手段保存多个时间点的快照(例如把每天0点的快照备份至其他存储媒介中),作为非常可靠的灾难恢复手段。
使用RDB文件进行数据恢复比使用AOF要快很多。
- RDB的缺点:
快照是定期生成的,所以在Redis crash时或多或少会丢失一部分数据。
如果数据集非常大且CPU不够强(比如单核CPU),Redis在fork子进程时可能会消耗相对较长的时间(长至1秒),影响这期间的客户端请求。快照是定期生成的,所以在Redis crash时或多或少会丢失一部分数据。
如果数据集非常大且CPU不够强(比如单核CPU),Redis在fork子进程时可能会消耗相对较长的时间(长至1秒),影响这期间的客户端请求。
- AOF的优点:
最安全,在启用appendfsync always时,任何已写入的数据都不会丢失,使用在启用appendfsync everysec也至多只会丢失1秒的数据。
AOF文件在发生断电等问题时也不会损坏,即使出现了某条日志只写入了一半的情况,也可以使用redis-check-aof工具轻松修复。
AOF文件易读,可修改,在进行了某些错误的数据清除操作后,只要AOF文件没有rewrite,就可以把AOF文件备份出来,把错误的命令删除,然后恢复数据。
- AOF的缺点:
AOF文件通常比RDB文件更大性能消耗比RDB高
-
分布式锁,实现过程,命令,可能出现的问题
- 使用
setnx
(setifnotexists)指令,只允许被一个客户端占坑。先来先占,用完了,再调用del
指令释放锁。 - 死锁:
set lock:code hole true ex 5
- 超时问题: set指令的value参数设置为一个随机数,释放锁时先匹配随机数是否一致,然后再删除key。但是匹配value和删除key不是一个原子操作,Redis也没有提供类似于del if equals这样的指令,这就需要使用Lua脚本来处理了,因为Lua脚本可以保证连续多个指令的原子性执行。
- 可重入性
可重入性是指线程在持有锁的情况下再次请求加锁,如果一个锁支持同一个线程的多次加锁,那么这个锁就是可重入的。Redis分布式锁如果要支持可重入,需要对客户端的set方法进行包装,使用线程的Threadlocal变量存储当前持有锁的计数。
- 使用
-
Redis 优点
- 性能极高:Redis能支持超过 100K+ 每秒的读写频率。
- 丰富的数据类型:Redis支持二进制案例的 Strings, Lists, Hashes, Sets 及 Ordered Sets 数据类型操作。
- 原子性:Redis的所有操作都是原子性的,同时Redis还支持对几个操作全并后的原子性执行。
- 丰富的特性:Redis还支持 publish/subscribe, 通知, key 过期等等特性。
-
Redis 红包池
<img src="https://www.amoswong.online/upload/2019/9/image-bb03552ab5054b7ca4cb7cea2e9f25fb.png" alt="img" style="zoom: 40%;" />
<img src="https://www.amoswong.online/upload/2019/9/image-2816244af85c46f99dac675c67399dcb.png" alt="image.png" style="zoom:40%;" />
<img src="https://www.amoswong.online/upload/2019/9/image-2a1307e92fe14cfda46c58da8203d7cb.png" alt="image.png" style="zoom:40%;" />
<img src="https://www.amoswong.online/upload/2019/9/image-01f625ef463d4764aaf2311bdca2510a.png" alt="image.png" style="zoom:40%;" />
-
各个数据类型最大存储量
-
Strings类型:一个String类型的value最大可以存储512M
-
Lists类型:list的元素个数最多为2^32-1个,也就是4294967295个。
-
Sets类型:元素个数最多为2^32-1个,也就是4294967295个。
-
Hash类型:键值对个数最多为2^32-1个,也就是4294967295个。
-
Sorted sets类型:跟Sets类型相似。
-
-
redis的1w条的插入和更新有什么区别
<img src="/Users/anyao/Library/Application Support/typora-user-images/image-20200401231231610.png" alt="image-20200401231231610" style="zoom:40%;" />
两个连续的写操作和两个连续的读操作总共只会花费一次网络来回,就好比连续的 write 操作合并了,连续的 read 操作也合并了一样。
这便是管道操作的本质,服务器根本没有任何区别对待,还是收到一条消息,执行一条 消息,回复一条消息的正常的流程。客户端通过对管道中的指令列表改变读写顺序就可以大 幅节省 IO 时间。管道中指令越多,效果越好。
-
zset 延时队列怎么实现的
延时队列可以通过 Redis 的 zset(有序列表) 来实现。我们将消息序列化成一个字符串作 为 zset 的 value,这个消息的到期处理时间作为 score,然后用多个线程轮询 zset 获取到期 的任务进行处理,多个线程是为了保障可用性,万一挂了一个线程还有其它线程可以继续处 理。因为有多个线程,所以需要考虑并发争抢任务,确保任务不能被多次执行。
zrem 方法是多线程多进程争抢任务的关键,它的返回值决定了当前实例有没有抢到任务, 因为 loop 方法可能会被多个线程、多个进程调用,同一个任务可能会被多个进程线程抢到,通过 zrem 来决定唯一的属主。
-
redis 缓存穿透,布隆过滤器
- 基本过程
如果客户端获取一个数据源中没有的key时,先从缓存中获取,获取结果为null,然后到数据源中获取,同样获取结果为null,这样所有的请求都会到达数据源。
- 解决方案
(1) 利用互斥锁,缓存失效的时候,先去获得锁,得到锁了,再去请求数据库。没得到锁,则休眠一段时间重试
(2) 采用异步更新策略,无论key是否取到值,都直接返回。value值中维护一个缓存失效时间,缓存如果过期,异步起一个线程去读数据库,更新缓存。需要做缓存预热(项目启动前,先加载缓存)操作。
(3)**缓存空对象**是一种简单粗暴的解决方法:当数据源中没有用户请求需要的数据时,会请求数据源,之前的做法是数据源返回一个null,而缓存中并不做回写,缓存空对象的做法就是把null回写到缓存中,暂时解决缓存穿透带来的压力。缓存空对象会造成:缓存空对象时会在缓存中设置很多的key,即使这些key的值都为空值,也会占用很多的内存空间,此时可以为这个key设置过期时间来降低这样的风险。
(4) 提供一个能迅速判断请求是否有效的拦截机制,比如,利用布隆过滤器,内部维护一系列合法有效的key。迅速判断出,请求所携带的Key是否合法有效。如果不合法,则直接返回。
使用**布隆过滤器**可以通过占用很小的内存来对数据进行过滤:布隆过滤器拦截是把所有的key或者离散数据保存到布隆过滤器中,然后使用布隆过滤器在缓存层之前再做一层拦截。如果请求没有被布隆过滤器拦截,则会到达缓存层获取需要的数据并返回,以达到实际效果。
<img src="/Users/anyao/Library/Application Support/typora-user-images/image-20200401233112687.png" alt="image-20200401233112687" style="zoom:30%;" />
原理:向布隆过滤器中添加 key 时,会使用多个 hash 函数对 key 进行 hash 算得一个整数索引值然后对位数组长度进行取模运算得到一个位置,每个 hash 函数都会算得一个不同的位置。再把位数组的这几个位置都置为 1 就完成了 add 操作。
-
缓存雪崩:即缓存同一时间大面积的失效,这个时候又来了一波请求,结果请求都怼到数据库上,从而导致数据库连接异常。
-
解决方案:
(一)给缓存的失效时间,加上一个随机值,避免集体失效。(二)使用互斥锁,但是该方案吞吐量明显下降了。
(三)双缓存。我们有两个缓存,缓存A和缓存B。缓存A的失效时间为20分钟,缓存B不设失效时间。
自己做缓存预热操作。然后细分以下几个小点 I 从缓存A读数据库,有则直接返回
II A没有数据,直接从B读数据,直接返回,并且异步启动一个更新线程。
III 更新线程同时更新缓存A和缓存B。
-
-
redis 故障转移过程
-
哨兵作用
- 监控:Sentinel 会不断的检查主服务器和从服务器是否正常运行。
- 通知:当被监控的某个redis服务器出现问题,Sentinel通过API脚本向管理员或者其他的应用程序发送通知。
- 自动故障转移:当主节点不能正常工作时,Sentinel会开始一次自动的故障转移操作,它会将与失效主节点是主从关系 的其中一个从节点升级为新的主节点,并且将其他的从节点指向新的主节点。
-
故障转移过程:当Master宕机的时候,Sentinel会选举出新的Master,并根据Sentinel中
client-reconfig-script
脚本配置的内容,去动态修改VIP(虚拟IP),将VIP(虚拟IP)指向新的Master。 -
缺点
- 主从切换的过程中会丢数据
- Redis只能单点写,不能水平扩容
-
-
Redis 集群
- 工作原理:
- 客户端与Redis节点直连,不需要中间Proxy层,直接连接任意一个Master节点
- 根据公式HASH_SLOT=CRC16(key) mod 16384,计算出映射到哪个分片上,然后Redis会去相应的节点进行操作
- 优点:
- 无需Sentinel哨兵监控,如果Master挂了,Redis Cluster内部自动将Slave切换Master
- 可以进行水平扩容
- 支持自动化迁移,当出现某个Slave宕机了,那么就只有Master了,这时候的高可用性就无法很好的保证了,万一master也宕机了,咋办呢? 针对这种情况,如果说其他Master有多余的Slave ,集群自动把多余的Slave迁移到没有Slave的Master 中。
- 缺点:
- 批量操作是个坑
- 资源隔离性较差,容易出现相互影响的情况。
<img src="https://img2018.cnblogs.com/blog/725429/201902/725429-20190210232938106-559726452.png" alt="img" style="zoom:50%;" />
- 工作原理:
-
准备同步,哨兵机制和集群的区别
-
主从模式:备份数据、负载均衡,一个Master可以有多个Slaves。
-
sentinel发现master挂了后,就会从slave中重新选举一个master。
-
cluster是为了解决单机Redis容量有限的问题,将数据按一定的规则分配到多台机器。
-
-
redis整数存储有什么优化
整数集合(intset)是集合键的底层实现之一: 当一个集合只包含整数值元素, 并且这个集合的元素数量不多时, Redis 就会使用整数集合作为集合键的底层实现。
contents 数组是整数集合的底层实现: 整数集合的每个元素都是 contents 数组的一个数组项(item), 各个项在数组中按值的大小从小到大有序地排列, 并且数组中不包含任何重复项。
-
redis的缺点
- 缓存和数据库双写一致性问题
- 缓存雪崩问题
- 缓存击穿问题
- 缓存的并发竞争问题
-
一个限流功能, 怎么做?
- 滑动窗口
这个限流需求中存在一个滑动时间窗口,想想 zset 数据结构的 score 值,是不是可以 通过 score 来圈出这个时间窗口来。而且我们只需要保留这个时间窗口,窗口之外的数据都 可以砍掉。那这个 zset 的 value 填什么比较合适呢?它只需要保证唯一性即可,用 uuid 会 比较浪费空间,那就改用毫秒时间戳吧。
用一个 zset 结构记录用户的行为历史,每一个行为都会作为 zset 中的一个 key 保存下来。同一个用户同一种行为用一个 zset 记录。
因为它要记录时间窗口内所有的行为记录,如果这 个量很大,比如限定 60s 内操作不得超过 100w 次这样的参数,它是不适合做这样的限流 的,因为会消耗大量的存储空间。
- 漏斗
将 Funnel 对象的内容按字段存储到一 个 hash 结构中,灌水的时候将 hash 结构的字段取出来进行逻辑运算后,再将新值回填到 hash 结构中就完成了一次行为频度的检测。
但是有个问题,我们无法保证整个过程的原子性。从 hash 结构中取值,然后在内存里 运算,再回填到 hash 结构,这三个过程无法原子化,意味着需要进行适当的加锁控制。而 一旦加锁,就意味着会有加锁失败,加锁失败就需要选择重试或者放弃。
如果重试的话,就会导致性能下降。如果放弃的话,就会影响用户体验。同时,代码的 复杂度也跟着升高很多。这真是个艰难的选择,我们该如何解决这个问题呢?Redis-Cell 救 星来了!
<img src="/Users/anyao/Library/Application Support/typora-user-images/image-20200402150757286.png" alt="image-20200402150757286" style="zoom:50%;" />
上面这个指令的意思是允许「用户老钱回复行为」的频率为每 60s 最多 30 次(漏水速 率),漏斗的初始容量为 15,也就是说一开始可以连续回复 15 个帖子,然后才开始受漏水速率的影响。
-
Hash 表,拉链法(长度大于8变形为红黑树),扩容*2 rehash,并发访问不安全拉链法中链表过长时变形为红黑树有什么优缺点? 优点:O(LogN) 的读取速度更快。
问题1:懂Redis事务么?
正常版:Redis事务是一些列redis命令的集合,blabla...
高调版: 我们在生产上采用的是Redis Cluster集群架构,不同的key是有可能分配在不同的Redis节点上的,在这种情况下Redis的事务机制是不生效的。其次,Redis事务不支持回滚操作,简直是鸡肋!所以基本不用!
问题2:Redis的多数据库机制,了解多少?
正常版:Redis支持多个数据库,并且每个数据库的数据是隔离的不能共享,单机下的redis可以支持16个数据库(db0 ~ db15)
高调版: 在Redis Cluster集群架构下只有一个数据库空间,即db0。因此,我们没有使用Redis的多数据库功能!
问题3:Redis集群机制中,你觉得有什么不足的地方吗?
正常版: 不知道
高调版: 假设我有一个key,对应的value是Hash类型的。如果Hash对象非常大,是不支持映射到不同节点的!只能映射到集群中的一个节点上!还有就是做批量操作比较麻烦!
问题4:懂Redis的批量操作么?
正常版: 懂一点。比如mset、mget操作等,blabla
高调版: 我们在生产上采用的是Redis Cluster集群架构,不同的key会划分到不同的slot中,因此直接使用mset或者mget等操作是行不通的。
问题5:那在Redis集群模式下,如何进行批量操作?
正常版:不知道
高调版:这个问题其实可以写一篇文章了,改天写。这里说一种有一个很简单的答法,足够面试用。即:
如果执行的key数量比较少,就不用mget了,就用串行get操作。如果真的需要执行的key很多,就使用Hashtag保证这些key映射到同一台redis节点上。简单来说语法如下
对于key为{foo}.student1、{foo}.student2,{foo}student3,这类key一定是在同一个redis节点上。因为key中“{}”之间的字符串就是当前key的hash tags, 只有key中{ }中的部分才被用来做hash,因此计算出来的redis节点一定是同一个!
ps:如果你用的是Proxy分片集群架构,例如Codis这种,会将mget/mset的多个key拆分成多个命令发往不同得redis实例,这里不多说。我推荐答的还是redis cluster。
问题6:你们有对Redis做读写分离么?
正常版:没有做,至于原因额。。。额。。。额。。没办法了,硬着头皮扯~
高调版:不做读写分离。我们用的是Redis Cluster的架构,是属于分片集群的架构。而redis本身在内存上操作,不会涉及IO吞吐,即使读写分离也不会提升太多性能,Redis在生产上的主要问题是考虑容量,单机最多10-20G,key太多降低redis性能.因此采用分片集群结构,已经能保证了我们的性能。其次,用上了读写分离后,还要考虑主从一致性,主从延迟等问题,徒增业务复杂度。
网友评论