根据官方数据.官方的基准程序测试,Redis 的 QPS 可以达到约 100000(每秒请求数)
基于内存实现 .Redis 是基于内存的数据库,不论读写操作都是在内存上完成的,跟磁盘数据库相比,完全吊打磁盘的速度.
String:存储数字的话,采用int类型的编码,如果是非数字的话,采用 raw 编码;
①.、简单动态字符串
这个名词可能你不熟悉,换成 SDS肯定就知道了. 这是用来处理字符串的.了解 C 语言的都知道,它是有处理字符串方法的.而 Redis 就是 C 语言实现的,那为什么还要重复造轮子?我们从以下几点来看:
(1)字符串长度处理
Redis 中怎么操作呢?用一个 len 字段记录当前字符串的长度.想要获取长度只需要获取 len 字段即可.你看,差距不言自明.前者遍历的时间复杂度为 O(n),Redis 中 O(1) 就能拿到,速度明显提升.
C 语言中涉及到修改字符串的时候会重新分配内存.修改地越频繁,内存分配也就越频繁.而内存分配是会消耗性能的,那么性能下降在所难免.
而 Redis 中会涉及到字符串频繁的修改操作,这种内存分配方式显然就不适合了.于是 SDS 实现了两种优化策略:
空间预分配
对 SDS 修改及空间扩充时,除了分配所必须的空间外,还会额外分配未使用的空间.
具体分配规则是这样的:SDS 修改后,len 长度小于 1M,那么将会额外分配与 len 相同长度的未使用空间.如果修改后长度大于 1M,那么将分配1M的使用空间.
惰性空间释放
当然,有空间分配对应的就有空间释放.
SDS 缩短时,并不会回收多余的内存空间,而是使用 free 字段将多出来的空间记录下来.如果后续有变更操作,直接使用 free 中记录的空间,减少了内存的分配.
看,二进制安全的问题就解决了.
List:字符串长度及元素个数小于一定范围使用 ziplist 编码,任意条件不满足,则转化为 linkedlist 编码;
列表 List 更多是被当作队列或栈来使用的.队列和栈的特性一个先进先出,一个先进后出.双端链表很好的支持了这些特性.
(1)前后节点
链表里每个节点都带有两个指针,prev 指向前节点,next 指向后节点.这样在 时间复杂度为 O(1)内就能获取到前后节点.
你可能注意到了,头节点里有 head 和 tail 两个参数,分别指向头节点和尾节点.这样的设计能够对双端节点的处理时间复杂度降至 O(1),对于队列和栈来说再适合不过.同时链表迭代时从两端都可以进行.
头节点里同时还有一个参数 len,和上边提到的 SDS 里类似,这里是用来记录链表长度的.所以呢获取链表长度时不用再遍历整个链表,直接拿到 len 值就可以了,这个时间复杂度是 O(1).
你看,这些特性都降低了 List 使用时的时间开销.
Hash:hash 对象保存的键值对内的键和值字符串长度小于一定值及键值对;
Set:保存元素为整数及元素个数小于一定范围使用 intset 编码,任意条件不满足,则使用 hashtable 编码;
Zset:zset 对象中保存的元素个数小于及成员长度小于一定值使用 ziplist 编码,任意条件不满足,则使用 skiplist 编码.
作为 Redis 中特有的数据结构-跳跃表,其在链表的基础上增加了多级索引来提升查找效率.
这是跳跃表的简单原理图,每一层都有一条有序的链表,最底层的链表包含了所有的元素.这样跳跃表就可以支持在 O(logN) 的时间复杂度里查找到对应的节点.
下面这张是跳表真实的存储结构,和其它数据结构一样,都在头节点里记录了相应的信息,减少了一些不必要的系统开销.
单线程模型. 单线程指的是 Redis 键值对读写指令的执行是单线程.
不会因为线程创建导致的性能消耗;
避免上下文切换引起的 CPU 消耗,没有多线程切换的开销;
避免了线程之间的竞争问题,比如添加锁、释放锁、死锁等,不需要考虑各种锁问题.
代码更清晰,处理逻辑简单.
I/O多路服用模型
Redis 采用 I/O 多路复用技术,并发处理连接.采用了 epoll + 自己实现的简单的事件框架.
epoll 中的读、写、关闭、连接都转化成了事件,然后利用 epoll 的多路复用特性,绝不在 IO 上浪费一点时间
Redis 通过链式哈希解决冲突:也就是同一个 桶里面的元素使用链表保存.但是当链表过长就会导致查找性能变差可能,所以 Redis 为了追求快,使用了两个全局哈希表.用于 rehash 操作,增加现有的哈希桶数量,减少哈希冲突.
释放 「hash 表 1」 的空间.
Redis 的数据持久化使用了「RDB 数据快照」的方式来实现宕机快速恢复.但是 过于频繁的执行全量数据快照,有两个严重性能开销:
频繁生成 RDB 文件写入磁盘,磁盘压力过大.会出现上一个 RDB 还未执行完,下一个又开始生成,陷入死循环.
fork 出 bgsave 子进程会阻塞主线程,主线程的内存越大,阻塞时间越长.
所以 Redis 还设计了 AOF 写后日志记录对内存进行修改的指令记录.
RDB就是把某一刻的数据以文件的形式拍下来,写到磁盘上.这个快照文件叫做 RDB 文件,RDB 就是 Redis DataBase 的缩写.
在做数据恢复时,直接将 RDB 文件读入内存完成恢复.
可以的,Redis 使用操作系统的多进程写时复制技术 COW(Copy On Write) 来实现快照持久化,保证数据一致性.
当主线程执行写指令修改数据的时候,这个数据就会复制一份副本, bgsave 子进程读取这个副本数据写到 RDB 文件.
这既保证了快照的完整性,也允许主线程同时对数据进行修改,避免了对正常业务的影响.
AOF 日志记录了自 Redis 实例创建以来所有的修改性指令序列,那么就可以通过对一个空的 Redis 实例顺序执行所有的指令,也就是「重放」,来恢复 Redis 当前实例的内存数据结构的状态.
Redis 提供的 AOF 配置项appendfsync写回策略直接决定 AOF 持久化功能的效率和安全性.
always:同步写回,写指令执行完毕立马将 aof_buf缓冲区中的内容刷写到 AOF 文件.
everysec:每秒写回,写指令执行完,日志只会写到 AOF 文件缓冲区,每隔一秒就把缓冲区内容同步到磁盘.
no: 操作系统控制,写执行执行完毕,把日志写到 AOF 文件内存缓冲区,由操作系统决定何时刷写到磁盘.
AOF 写前日志,记录的是每个「写」指令操作.不会像 RDB 全量快照导致性能损耗,但是执行速度没有 RDB 快,同时日志文件过大也会造成性能问题.
所以,Redis 设计了一个杀手锏「AOF 重写机制」,Redis 提供了 bgrewriteaof指令用于对 AOF 日志进行瘦身.
其原理就是开辟一个子进程对内存进行遍历转换成一系列 Redis 的操作指令,序列化到一个新的 AOF 日志文件中.序列化完毕后再将操作期间发生的增量 AOF 日志追加到这个新的 AOF 日志文件中,追加完毕后就立即替代旧的 AOF 日志文件了,瘦身工作就完成了.
重启 Redis 时,我们很少使用 rdb 来恢复内存状态,因为会丢失大量数据.我们通常使用 AOF 日志重放,但是重放 AOF 日志性能相对 rdb 来说要慢很多,这样在 Redis 实例很大的情况下,启动需要花费很长的时间.
于是在 Redis 重启的时候,可以先加载 rdb 的内容,然后再重放增量 AOF 日志就可以完全替代之前的 AOF 全量文件重放,重启效率所以呢大幅得到提升.
读操作:主、从库都可以执行;
写操作:主库先执行,之后将写操作同步到从库;
故障恢复:当主节点宕机,其他节点依然可以提供服务;
负载均衡:Master 节点提供写服务,Slave 节点提供读服务,分担压力;
高可用基石:是哨兵和 cluster 实施的基础,是高可用的基石.
第一次主从库全量复制;
主从正常运行期间的同步;
主从库间网络断开重连同步.
建立连接:从库会和主库建立连接,从库执行 replicaof 并发送 psync 命令并告诉主库即将进行同步,主库确认回复后,主从库间就开始同步了.
主库同步数据给从库:master 执行 bgsave命令生成 RDB 文件,并将文件发送给从库,同时主库为每一个 slave 开辟一块 replication buffer 缓冲区记录从生成 RDB 文件开始收到的所有写命令.从库保存 RDB 并清空数据库再加载 RDB 数据到内存中.
发送 RDB 之后接收到的新写命令到从库:在生成 RDB 文件之后的写操作并没有记录到刚刚的 RDB 文件中,为了保证主从库数据的一致性,所以主库会在内存中使用一个叫 replication buffer 记录 RDB 文件生成后的所有写操作.并将里面的数据发送到 slave.
增量复制:用于网络中断等情况后的复制,只将中断期间主节点执行的写命令发送给从节点,与全量复制相比更加高效.
断开重连增量复制的实现奥秘就是 repl_backlog_buffer 缓冲区,不管在什么时候 master 都会将写指令操作记录在 repl_backlog_buffer 中,因为内存有限, repl_backlog_buffer 是一个定长的环形数组,如果数组内容满了,就会从头开始覆盖前面的内容.
master 使用 master_repl_offset记录自己写到的位置偏移量,slave 则使用 slave_repl_offset记录已经读取到的偏移量.
当主从断开重连后,slave 会先发送 psync 命令给 master,同时将自己的 runID,slave_repl_offset发送给 master.
master 只需要把 master_repl_offset与 slave_repl_offset之间的命令同步给从库即可.
增量复制执行流程如下图:
当主从库完成了全量复制,它们之间就会一直维护一个网络连接,主库会通过这个连接将后续陆续收到的命令操作再同步给从库,这个过程也称为基于长连接的命令传播,使用长连接的目的就是避免频繁建立连接导致的开销.
哨兵是 Redis 的一种运行模式,它专注于对 Redis 实例(主节点、从节点)运行状态的监控,并能够在主节点发生故障时通过一系列的机制实现选主及主从切换,实现故障转移,确保整个 Redis 系统的可用性.
哨兵与 master 建立通信,利用 master 提供发布/订阅机制发布自己的信息,比如身高体重、是否单身、IP、端口......
master 有一个 __sentinel__:hello 的专用通道,用于哨兵之间发布和订阅消息.这就好比是 __sentinel__:hello 微信群,哨兵利用 master 建立的微信群发布自己的消息,同时关注其他哨兵发布的消息.
关键还是利用 master 来实现,哨兵向 master 发送 INFO 命令, master 掌门自然是知道自己门下所有的 salve 小弟的.所以 master 接收到命令后,便将 slave 列表告诉哨兵.
哨兵根据 master 响应的 slave 名单信息与每一个 salve 建立连接,并且根据这个连接持续监控哨兵.
Redis 集群是一种分布式数据库方案,集群通过分片(sharding)来进行数据管理(「分治思想」的一种实践),并提供复制和故障转移功能.
它是去中心化的,每个节点负责整个集群的一部分数据,每个节点负责的数据多少可能不一样.
节点相互连接组成一个对等的集群,它们之间通过 Gossip协议相互交互集群信息,最后每个节点都保存着其他节点的 slots 分配情况
根据该槽信息定位到对应的实例.
Redis 集群节点采用 Gossip 协议来广播自己的状态以及自己对整个集群认知的改变.比如一个节点发现某个节点失联了 (PFail),它会将这条信息向整个集群广播,其它节点也就可以收到这点失联信息.
如果一个节点收到了某个节点失联的数量 (PFail Count) 已经达到了集群的大多数,就可以标记该节点为确定下线状态 (Fail),然后向整个集群广播,强迫其它节点也接收该节点已经下线的事实,并立即对该失联节点进行主从切换.
Redis 实例会将自己的哈希槽信息通过 Gossip 协议发送给集群中其他的实例,实现了哈希槽分配信息的扩散.
这样,集群中的每个实例都有所有哈希槽与实例之间的映射关系信息.
如果某个 slot 的数据比较多,部分迁移到新实例,还有一部分没有迁移.
如果请求的 key 在当前节点找到就直接执行命令,否则时候就需要 ASK 错误响应了.
redis过期策略?
①只对过期的key进行LRU算法
以上就是土嘎嘎小编为大家整理的Redis知识网络相关主题介绍,如果您觉得小编更新的文章只要能对粉丝们有用,就是我们最大的鼓励和动力,不要忘记讲本站分享给您身边的朋友哦!!