Redis 原理及混合持久化

网友投稿 752 2023-06-05

Redis 原理及混合持久化

Redis 原理及混合持久化

上一篇文章我们对redis的基本类型及相应的应用场景做了了解,那你知道redis 为什么这么快吗?redis 宕机了怎么办?redis 的持久化方式有哪些?如果你是云服务厂商,你怎么优化你的云版的redis呢?

本文将对redis 的相关原理进行分析。

线程 IO 模型

redis 是单线程程序的,除了 Redis 之外,Node.js 也是单线程,Nginx 也是单线程,但是它们都是服务器高性能的典范。

redis 单线程也这么快,也要归功于非阻塞IO和 多路复用器。

非阻塞IO

能读多少读多少,取决于内核为套接字分配的读缓冲区内部的数据字节数。

能写多少写多少,取决于内核为套接字分配的写缓存区空闲空间的字节数。

事件轮询(多路复用)

非阻塞 IO 有个问题,那就是线程要读数据,结果读了一部分就返回了,线程如何知道

何时才应该继续读。也就是当数据到来时,线程如何得到通知。写也是一样,如果缓冲区满

了,写不完,剩下的数据何时才应该继续写,线程也应该得到通知。

最简单的事件轮询API,如下图所示。select函数,它是操作系统提供的。在描述符特别多的情况下,性能特别差。现代的操作系统多路复用改为:epoll(linux) kqueue(freeBsd 和 macosx)。

目前支持I/O多路复用的系统调用有 select,pselect,poll,epoll,I/O多路复用就是通过一种机制,一个进程可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。但select,pselect,poll,epoll本质上都是同步I/O,因为他们都需要在读写事件就绪后自己负责进行读写,也就是说这个读写过程是阻塞的,而异步I/O则无需自己负责进行读写,异步I/O的实现会负责把数据从内核拷贝到用户空间。

select 和epoll的区别

select

首先是垮平台的,select本质上是通过设置或者检查存放fd标志位的数据结构来进行下一步处理,

select最大的缺陷就是单个进程所打开的FD是有一定限制的,它由FD_SETSIZE设置,默认值是1024

对socket进行扫描时是线性扫描,即采用轮询的方法,效率较低。

epoll

是2.6 内核提出的,是 select 和poll 的增强版,epoll使用一个文件描述符管理多个描述符,将用户关系的文件描述符的事件存放到内核的一个事件表中,这样在用户空间和内核空间的copy只需一次。

它的原理其实就是epoll支持水平触发和边缘触发,最大的特点在于边缘触发,它只告诉进程哪些fd刚刚变为就绪态,并且只会通知一次。

还有一个特点是,epoll使用“事件”的就绪通知方式,通过epoll_ctl注册fd,

一旦该fd就绪,内核就会采用类似callback的回调机制来激活该fd,epoll_wait便可以收到通知。

它的有点优点:

1、没有最大并发连接的限制,能打开的FD的上限远大于1024(1G的内存上能监听约10万个端口)。

2、效率提升,不是轮询的方式,不会随着FD数目的增加效率下降。

内存拷贝,利用mmap()文件映射内存加速与内核空间的消息传递;即epoll使用mmap减少复制开销。

单线程如何处理相应请求?

通过 指令队列,相应队列,和定时任务的设计来相应请求。

指令队列

客户端指令通过队列来排队进行顺序处理,先到先服务

响应队列

等队列里面有数据了再把客户端fd 放进去,避免浪费cpu

定时任务

服务器处理要响应 IO 事件外,还要处理其它事情。比如定时任务就是非常重要的一件事。如果线程阻塞在 select 系统调用上,定时任务将无法得到准时调度。

那 Redis 是如何解决这个问题的呢?

Redis 的定时任务会记录在一个称为最小堆的数据结构中。这个堆中,最快要执行的任务排在堆的最上方。在每个循环周期,Redis 都会将最小堆里面已经到点的任务立即进行处理。处理完毕后,将最快要执行的任务还需要的时间记录下来,这个时间就是 select 系统调用的 timeout 参数。因为 Redis 知道未来 timeout 时间内,没有其它定时任务需要处理,所以可以安心睡眠 timeout 的时间。

Nginx 和 Node 的事件处理原理和 Redis 也是类似的。

通信协议

Redis 的请求也相当于一次rpc 的调用,rpc中,通信协议的设计是非常重要的。,Redis 的作者认为数据库系统的瓶颈一般不在于网络流量,而是数据库自身内部逻辑处理上。所以即使 Redis 使用了浪费流量的文本协议,依然可以取得极高的访问性能。Redis

将所有数据都放在内存,用一个单线程对外提供服务,单个节点在跑满一个 CPU 核心的情况下可以达到了 10w/s 的超高 QPS。

RESP(Redis Serialization Protocol)

RESP 是 Redis 序列化协议的简写。它是一种直观的文本协议,优势在于实现异常简

单,解析性能极好。Redis 协议里有大量冗余的回车换行符,但是这不影响它成为互联网技术领域非常受欢

迎的一个文本协议。有很多开源项目使用 RESP 作为它的通讯协议。在技术领域性能并不总

是一切,还有简单性、易理解性和易实现性,这些都需要进行适当权衡。

关于持久化

一款优秀的中间件都会考虑到持久化,Redis 的数据全部在内存里,如果突然宕机,数据就会全部丢失,因此必须有一种机制来保证 Redis 的数据不会因为故障而丢失,这种机制就是 Redis 的持久化机制

Redis 的持久化机制有两种,第一种是RDB(快照)(Redis DataBase),第二种是 AOF(Append Only File)日志。

RDB原理:

redis 使用操作系统的多进程(cow copy on write【写时复制】)机制来实现快照的持久化;手动命令save,bgsave;配置文件设置.

fork 多进程

调用glibc 的函数 fork产生一个子进程,快照持久化交个子进程处理.子进程刚刚产生时和父进程共享代码段和数据段,随着父进程修改操作持续进行,more 共享页面被分离出来,创建进程变快;内存增长,不会超过原来的2倍.另外一个 Redis 实例里冷数据占的比例往往是比较高的,所以很少会出现所有的页面都会被分离,被分离的往往只有其中一部分页面。每个页面的大小只有 4K,一个 Redis 实例里面一般都会有成千上万的页面子进程的数据没有变化,可以安心的遍历数据,进行序列化写磁盘。

AOF原理

AOF 日志存储的是 Redis 服务器的顺序指令序列,AOF 日志只记录对内存进行修改的指令记录。随着操作的进行,指令文本会越来越大。需要对AOF 重写,进行日志瘦身,redis 提供bgrewriteaof 指令对AOF 进行瘦身,原理是开辟一个子进程对内存进行遍历,序列化一个新的aof+这段时间的增量。Linux 的glibc 提供了 fsync(int fd) 强制从内核缓存刷到磁盘,fsync 很慢,一般生产环境每隔一秒执行一次,可配置。

redis 是先执行指令再写日志,leveldb\hbase 相反。

关于redis 运维的一些经验,不要在主节点进行持久化,要在从节点进行持久化,因为持久化是一个很耗时的IO操作。

关于混合持久化

为什么要进行混合持久化?首先fork 动作需要copy 页表,大内存场景下阻塞server,百ms 或秒级 latency spike(延迟尖峰),比较适合每天定时的 全备场景。部分云厂商的方案比如:redis+rocksDB。从以下几个方面来分析。

数据写入

写入Redis 内存,并记录增量,后台线程异步应用增量到RocksDB

数据可靠性

RocksDB 包含全量数据,彻底避免Fork 调用,启动时,从RocksDB 加载全量索引信息到内存

数据读取

数据在redis 内存,直接读取,数据不在Redis 内存,整体换入Redis 再读取,针对简单命令,可以直接从Rocks DB 读取。

关于RcoksDB,RcoksDB可嵌入的,持久型的key-value 存储,

RocksDB项目起源于Facebook的一个实验项目,该项目旨在开发一个与快速存储器(尤其是闪存)存储数据性能相当的数据库软件,以应对高负载服务。这是一个c++库,可用于存储键和值,可以是任意大小的字节流。它支持原子读和写。RocksDB具有高度灵活的配置功能,可以通过配置使其运行在各种各样的生产环境,包括纯内存,Flash,硬盘或HDFS。它支持各种压缩算法,并提供了便捷的生产环境维护和调试工具。

数据结构编码

每个key对应一个RocksDB metakey,存储key 的元数据。所有的metakey 相邻存储,启动加载效率高,复杂key的每个元素在RocksDB 里面对应一个Datakey,元素在RocksDB 相邻存储,访问效率高。简化String 类型,metakey 与Datakey 合并优化IO。

数据交换模型(SWAP MODE)

内存数据换出:后台定期检查是否超哥内存使用阀值,根据访问评率、大小等选择key 预备淘汰。

磁盘数据换入:某个key 被频繁读取或有o(n) 的读取操作,整个key 换入的RocksDB,加速访问;key 写入时,数据不再内存,整体换入内存;默认后台多线程异步换入。

主备同步&迁移

全量同步:使用RocksDB checkoutpoint 代替RDB,避免fork 系统调用,备接收到checkoutpoint 数据只需加载meta key

增量同步:从 checkoutpoint 对应aof binlog 位点继续增量同步,增量同步断开,直接从断开位置继续同步,无需触发全量同步。

一些其他的知识

管道

技术的本质:客户端提供,pipline 包含多条指令;客户端改变管道中的指令列表的读写顺序,可以大幅节省IO 时间。自带压力测试工具 redis-beanchmark。普通 set 5w qps

小对象压缩

如果redis 使用内存不超过4G,使用32bit 编译。

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:Redis锁被别人释放怎么办
下一篇:加班到2点,一不小心我把MySQL删了
相关文章