1. redis 为什么快?
在底层上,redis 应用了 IO 多路复用技术,像 select、epoll 等。能较好的保障吞吐量。而且 redis 采纳了单线程解决申请,防止了线程切换和锁竞争锁带来的额定耗费。
加上 redis 自身也对一些数据结构进行了优化设计,所以 redis 的性能十分好,官网给出的测试报告是单机能够反对约 10w/s 的 QPS。
2. Redis 有哪些应用场景?利用是怎么样的?
Redis 的应用场景有很多,最罕用的莫过于数据缓存了。但因为它提供了多种数据类型,因而咱们还能够进行其余场景的开发,比方:
- 排行榜:有序汇合(sorted set)每次写入都会进行排序,而且不含反复值,所以咱们能够将用户的惟一标识,比方 userId 作为 key,分数作为 score,而后就能够进行
ZADD
操作,以失去排行榜。 - 签到:签到往往只有 2 种状态,已签到和未签到。这就跟 0 和 1 一样,所以 redis 的
setbit
、getbit
这种对位的操作就适宜签到场景。 - 计数:redis 是单线程操作,这种计数性能,比方点赞数、粉丝数的操作能够交给 redis 以防止并发竞争问题。当然,也得思考长久化问题。
3. Redis 通信协议 是怎么样的?
redis 采纳文本序列化协定,和 http 协定一样,一个申请一个响应,客户端接到响应后再持续申请。也能够发动屡次申请,而后一次响应回所有执行后果,即所谓的 pipeline 管道技术。
redis 的文本序列化协定比较简单,通过一些标准格局去解析文本,大略如下:
- \r\n 示意解析完结
- 简略字符串,以“+”结尾
- 谬误 Errors,以“-”结尾
- 整数类型,以“:”结尾
- 大字符串类型,以“$”结尾
- 数组类型,以“*”结尾
例如,客户端向服务器发送命令:
SET key value
将被解析为:
*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$5\r\nvalue\r\n
下面的命令能够看成:
*< 参数数量 > CR LF
$< 参数 1 的字节数量 > CR LF
< 参数 1 的数据 > CR LF
...
$< 参数 N 的字节数量 > CR LF
< 参数 N 的数据 > CR LF
而服务器的回复则有很多类型,个别由响应数据的第一个字节决定:
状态回复(status reply)的第一个字节是 "+"
谬误回复(error reply)的第一个字节是 "-"
整数回复(integer reply)的第一个字节是 ":"
批量回复(bulk reply)的第一个字节是 "$"
多条批量回复(multi bulk reply)的第一个字节是 "*"
例如,响应回来的状态回复如下:
+OK
4. redis 对外提供了哪些数据类型,它们的底层数据结构又是怎么样的?
为了让开发者能更好的应用缓存,redis 反对了 5 种数据类型。底层是由 6 种数据结构组成的。
5 种数据类型
字符串:字符串类型是 redis 里最根底的数据类型,像 set name "hello"
操作后,在 get name
时返回的就是字符串,而且还反对了对位的操作。个别一个键能存储 512MB 的值。
hash:哈希类型次要是用来存储对象的,个别咱们如果有一整个对象要存储,外面蕴含了多个字段,则能够应用 hash 来存储,因为 redis 提供了对这些字段的提取和设置,缩小了开发者对它的二次解决,比方序列化反序列化操作。
list:一个简略的字符串列表,它容许咱们从两端进行 push,pop 操作, 还反对肯定范畴的列表元素。能够看成是双向列表。
set:汇合是一个不反复值的组合,为咱们提供了交加、并集、差集等操作,像找出独特好友这种需要就能够应用汇合操作了。
sorted set:有序汇合,在下面汇合的根底上提供了排序功能,通过一个 score 属性来进行排序。
6 种底层数据结构
下面的数据类型实际上在 redis 底层是有对应的数据结构来实现的,都是 redis 通过精心设计的,能很好的进步解决效率。
简略动静字符串:redis 是应用 C 语言写的,而 C 语言里的字符串类型比拟原始,比方应用 \0
作为字符结束符。所以 redis 实现了属于本人的字符串类型,比方字符串长度,事后分配内存,动静拓展等特点,也保障了解决安全性。
链表:一个双端链表,有 prev,next 指针去获取前后节点,带有 len 属性,能保留多种类型的值。
字典 :通过哈希算法来实现 key-value 的映射操作,采纳 链地址法
解决了 hash 抵触,个别工夫复杂度能达到 O(1)。
跳跃表:一个多层有序链表,每一层都是对上面一层的有序提取,能升高搜寻次数,有点像有序二叉树的搜寻一样。
整数汇合:一个有序的整数汇合,不会有反复元素。
压缩列表(ziplist):通过非凡编码的一块间断内存,能无效的节俭内存。
疾速列表:将 ziplist 组织为了一个双向链表,因为 ziplist 的外部连续性,能升高链表的内存碎片问题,进步内存利用率。
5. redis 淘汰策略有哪些?
redis 的淘汰策略次要是 LRU 淘汰、TTL 淘汰和随机淘汰这三种机制。
- LRU 淘汰:最近起码应用的淘汰掉
- TTL 淘汰:越早过期的越先淘汰掉。
- 随机淘汰:采纳随机算法淘汰掉。
因为 redis 能够对键设置过期工夫,也能够不设置,所以淘汰策略还得再细分:
- volatile-lru:针对设置了过期工夫的 key 执行 LRU 淘汰策略,没有设置过期工夫的不会被淘汰。
- volatile-ttl:只针对设置了过期工夫的 key 执行 TTL 淘汰。
- volatile-random:只针对设置了过期工夫的 key 执行随机淘汰。
- allkeys-lru:针对所有键进行 LRU 淘汰策略
- allkeys-random:针对所有键进行随机淘汰策略
- no-enviction:不执行淘汰策略,如果有写入操作,则报错;读申请能够持续进行。
在 Redis 的配置文件 redis.conf
里咱们能够进行淘汰策略的设置:
# 数据达到多大后执行淘汰策略
maxmemory 300mb
# 淘汰策略的设置
maxmemory-policy volatile-lru
6. redis 的长久化机制有哪些?
RDB
在指定的工夫距离里将 Redis 内存里的数据镜像下来,保留到文件里。它会先 fork 一个子过程,将数据的写入交给子过程,而父过程不会波及到磁盘的 IO 操作,所以 RDB 的性能十分好。如果是在 Unix 零碎上,还能充分利用写时复制机制,节俭对物理内存的应用。
因为 RDB 文件只存储了某个时刻的内存数据,并没有什么逻辑命令,所以在进行重启复原时,能很快的加载进来。
尽管 RDB 的 fork 能使得 Redis 的长久化独立进行,然而一旦数据量比拟大的,就会始终占用 CPU,可能会影响到父过程的进行。
AOF
将服务器对数据的写操作追加到文件里,相当于将所有的逻辑操作都记录了下来。AOF 容许咱们以每秒的速度进行长久化,这样的话能够很大水平的缩小数据的失落。同时它采纳追加的形式进行写文件,这样即便长久化失败,影响较少,而且可能应用 redis-check-aof 进行修复。
不过日志可能会越来越大,须要靠重写来缩小对磁盘的占用。
RDB + AOF
将 RDB 和 AOF 联合起来,组合它们各自的长处。4.0 版本以上才反对。其文件时前半部分时 RDB 格局,后半局部是 AOF 格局。
7. redis 的分布式锁:RedLock 原理是什么?有哪些毛病?
RedLock 原理
客户端顺次向各个 redis 节点获取锁,一旦超过一半的机器上锁了,并且没有超过规定的工夫,则客户端认为是上锁胜利了。同时开始计算锁的过期工夫,过期则告诉所有服务器解锁,如果这次获取锁失败,也告诉所有服务器解锁。并且解锁时会依据过后带过去的一个 token 统一才解锁,避免误会锁。
RedLock 毛病
- 受限于 redis 的长久化机制,当某个 redis 节点重启时失落了锁记录,则有可能导致新的申请又获取到了超过一半的响应,则此时将有两个操作者同时领有锁资源。官网针对此倡议:提早重启,期待超时
- 下面的流程波及到了工夫的判断,如果不同机器的时间差相差太远,则会呈现超时解锁,提前开释资源的问题。
8. redis 的高可用方案设计?
主从模式
在不同的机器上部署着同一 Redis 程序。在这多台机器里,咱们会抉择一个节点作为主节点,它负责数据的写入。其余节点作为从节点,定时的和主节点同步数据。一旦主节点不能应用了,那么就能够在从节点中筛选一个作为主节点,从新上岗服务。
哨兵模式
下面的主从模式须要人工的进行故障节点切换,这种形式对于谋求完满的程序员来说,必定是不够的。所以有了主动切换的哨兵模式。
哨兵模式次要实现了上面几个性能:
- 监控:一直的检测主从节点是否能失常工作。
- 主动转移故障:当某个 master 不能失常工作时,Sentinel 会启动一个故障转移过程,将其中的一个正本晋升为 master,并告诉其余从节点对应新的 master 相干信息。
- 告诉:当某个节点出问题时,会告知所有节点。如果是新的主节点被选举进去,还会告知已连贯过去的客户端程序对于主节点新的地址。
集群
Redis 的集群采纳了哈希槽的概念,总共会有 16384 个哈希槽。这些哈希槽会被调配到各个节点上,比方:
- 节点 1 调配了 0 至 5500 的哈希槽。
- 节点 2 调配了 5501 至 11000 的哈希槽。
- 节点 3 调配了 11001 至 16384 的哈希槽。
当有 key 过去时,Redis 会对其进行 CRC16(key) % 16384 的运算,看以后的 key 要扩散到哪个哈希槽上,再依据以后的哈希槽定位到对应的节点上。这样就实现了一次 key-value 的存储了。
读取也是按这规定来,不同的是,如果运算后果所对应的节点不在以后节点上,则会转发给对应的节点去解决。
当有节点进行新增或删除时,会从新划分这些哈希槽,当然,影响的只会是四周节点,不会造成整个集群不可用。
在这些节点背地还有属于它们的从节点,一旦主节点不可用,那么这些从节点就会被启用,以保证系统的失常运行。
9. 缓存雪崩和穿透该怎么解决?
当缓存生效,就会有大量的申请打到后端服务,压垮零碎,这就是缓存雪崩。
除了缓存雪崩,还有缓存穿透的可能。比方每次拜访不一样的数据,则申请还是会落到前方。
为了避免缓存雪崩,咱们能够对申请做管制,比方退出到音讯队列,缓缓消化它;又或者间接开启限流性能,将流量管制在正当的范畴内。
而针对缓存穿透,咱们能够建设黑白名单,将一些歹意申请拎进去,而后间接回绝掉。如果是失常的申请,那能够将筛选进去的后果也临时缓存起来,即便失去的值是 NULL 值。
10. 应用 Redis 在数据并发解决上有哪些须要思考?
因为 Redis 是以组件模式存在,所以实际上咱们的程序通信能够认为是分布式的了,也就是会有缓存和后端数据一致性的问题。
常见的做法是在有新数据到来时,将缓存 key 删除掉,期待下次的查问从新填补上缓存。
之所以在更新数据时不让 Redis 也做更新动作,是为了避免多个更新动作一起产生,可能因为网络起因,导致后更新的比后面更新的先一步达到 Redis,这样就会跟原来的流程不一样了。所以只采取了删除动作,不做其余。
不过,就算是删除 key 这种计划也有肯定概率跟下面的状况一样,真的要谨严的话,个别会设置定时过期工夫,让数据最多在这段时间不统一。
11. redis 如何实现提早队列?
利用有序汇合的 score 属性,将工夫戳设置到该属性上,而后定时的对其排序,查看最近要执行的记录,如果工夫到了,则取出来生产后删除,即可达到提早队列的目标。
12. redis 的事务和 db 的事务有什么不一样?
Redis 的事务保障了 ACID 中的一致性(C)和隔离性(I),但并不保障原子性(A)和持久性(D)。
对于原子性而言,要么都胜利,要么都不胜利,而 redis 的事务中途某个语句出错了, 比方 key 类型 出错了, 还会继续执行其余语句;
对于持久性而言,redis 即便开启了最严格的数据落地,因为保留是由后盾线程进行的,主线程不会阻塞直到保留胜利,所以从命令执行胜利到数据保留到硬盘之间,还是有一段十分小的距离,所以这种模式下的事务也是不长久的。
感兴趣的敌人能够搜一搜公众号「阅新技术」,关注更多的推送文章。
能够的话,就顺便点个赞、留个言、分享下,感激各位反对!
阅新技术,浏览更多的新常识。