当前位置: 首页 > 科技观察

Redis原理与HybridPersistence

时间:2023-03-18 22:11:04 科技观察

在上一篇文章中,我们了解了redis的基本类型以及对应的应用场景。你知道redis为什么那么快吗?redis挂了怎么办?redis的持久化方式有以下几种:有哪几种?如果你是云服务商,你如何优化你的云版redis?本文将分析redis的相关原理。线程IO模型redis是一个单线程程序。除了Redis,Node.js也是单线程的,Nginx也是单线程的,但都是服务器高性能的典范。Redis单线程也这么快,也是得益于非阻塞IO和多路复用器。非阻塞IO能读多少取决于内核为socket分配的读缓冲区中的数据字节数。能写多少取决于内核为套接字分配的写缓冲区中空闲空间的字节数。事件轮询(多路复用)非阻塞IO有个问题,就是线程要读数据,读了一部分就返回结果。线程如何知道何时继续阅读。即数据到达时如何通知线程。写作也是如此。如果缓冲区已满,无法写入,则还应通知线程何时应继续写入剩余数据。最简单的事件轮询API,如下图所示。select函数,由操作系统提供。在描述符特别多的情况下,性能特别差。取而代之的是现代操作系统多路复用:epoll(linux)kqueue(freeBsd和macosx)。目前支持I/O多路复用的系统调用有select、pselect、poll、epoll。I/O多路复用是一种机制,通过它进程可以监视多个描述符。一旦一个描述符就绪(一般是readready或writeready),就可以通知程序执行相应的读写操作。但是select、pselect、poll、epoll本质上都是同步I/O,因为它们都需要在读写事件就绪后,才负责读写,也就是说读写过程是阻塞的,而asynchronousI/O是异步I/O的实现不自己负责读写,而是负责将数据从内核拷贝到用户空间。select和epollSelect的区别首先在于平台的突破。Select实质上是通过设置或检查存储fd标志的数据结构来进行下一步。select最大的缺陷是单个进程打开的fd有一定的局限性。由FD_SETSIZE设置,默认值为1024。扫描socket时,是线性扫描,即采用轮询的方式,效率较低。epoll是2.6内核提出的,是select和poll的增强版。epoll使用一个文件描述符来管理多个描述符,将与用户相关的文件描述符的事件存储在内核的一个事件表中,这样在用户空间和内核空间的拷贝只需要做一次。它的原理其实就是epoll支持水平触发和边沿触发。最大的特点是边沿触发。它只告诉进程哪些fds刚刚准备就绪,它只会被通知一次。另一个特点是epoll使用“事件”就绪通知方式,通过epoll_ctl注册fd。一旦fd就绪,内核会使用类似callback的回调机制来激活fd,epoll_wait可以收到通知。它有一些优点:1.最大并发连接没有限制,可以打开的FD上限远大于1024(1G内存可以监听10万个端口左右)。2.效率提升不是轮询的方式,效率不会随着FD数量的增加而降低。内存拷贝,使用mmap()文件映射内存加速与内核空间的消息传递;也就是说,epoll使用mmap来减少复制开销。单个线程如何处理相应的请求?通过指令队列、对应队列、定时任务的设计来响应请求。InstructionQueueClient指令通过队列排队进行顺序处理,先来先服务等。响应队列等待队列中有数据后再将clientfd放入,避免浪费cpu定时任务。事情。比如定时任务就是一个很重要的东西。如果线程阻塞在select系统调用上,则计划任务将无法按时调度。那么Redis是如何解决这个问题的呢?Redis的定时任务会记录在一个叫做最小堆的数据结构中。在这个堆中,最快执行的任务在堆的顶部。在每个循环中,Redis会立即处理到达最小堆中的点的任务。处理完成后,记录最快执行任务所需的时间。这个时间就是select系统调用的超时参数。因为Redis知道以后超时时间内没有其他定时任务需要处理,所以可以安心的休眠超时时间。Nginx和Node的事件处理原理与Redis类似。通信协议Redis的请求也相当于一个rpc调用。在rpc中,通信协议的设计非常重要。Redis的作者认为,数据库系统的瓶颈一般不在于网络流量,而在于数据库本身的内部逻辑处理。所以即使Redis使用了浪费流量的文本协议,仍然可以达到极高的访问性能。Redis将所有数据放在内存中,并使用单线程对外提供服务。单节点运行在CPU核心上可以达到10w/s的超高QPS。RESP(RedisSerializationProtocol)RESP是RedisSerializationProtocol的缩写。它是一种直观的文本协议,具有实现极其简单的优点,并且具有出色的解析性能。Redis协议中存在大量冗余的回车和换行,但这并不影响它成为互联网技术领域非常流行的文本协议。有许多开源项目使用RESP作为它们的通信协议。在技??术世界中,性能并不总是一切,还存在简单性、理解性和易于实施性,所有这些都需要适当的平衡。关于持久化,一个优秀的中间件都会考虑持久化。所有Redis数据都在内存中。如果突然停机,所有数据都将丢失。因此,必须有一种机制来保证Redis的数据不会因为故障而丢失,这个机制就是Redis的持久化机制。Redis有两种持久化机制。第一个是RDB(快照)(RedisDataBase),第二个是AOF(AppendOnlyFile)日志。RDB原理:redis利用操作系统的多进程(cowcopyonwrite)机制实现快照持久化;手动命令保存,bgsave;配置文件设置。fork多进程调用glibc函数fork生成子进程,快照持久化交给子进程。子进程刚生成时,与父进程共享代码段和数据段。随着父进程不断修改操作,分出更多的共享页面,创建过程变得更快;内存增长,不会是原来的2倍多。在另外一个Redis实例中,冷数据的比例往往比较高,所以很少会全部分页,只会分出一部分。每页的大小只有4K。在一个Redis实例中,一般有数以万计的页面子进程,其数据没有发生变化。您可以安全地遍历数据并将其序列化到磁盘。AOF原理AOF日志存储的是Redis服务器的顺序指令序列,AOF日志只记录修改内存的指令记录。随着操作的进行,指令文字会越来越大。需要重写AOF来瘦身日志。Redis提供了bgrewriteaof命令来瘦身AOF。原理是创建一个子进程遍历内存,在这期间序列化一个新的aof+increment。Linux的glibc提供了fsync(intfd)来强制从内核缓存刷新到磁盘。fsync很慢,生产环境一般每秒执行一次,可配置。Redis先执行命令再写入日志,leveldb\hbase则相反。关于redis运维的一些经验,不要在主节点上持久化,而是在从节点上持久化,因为持久化是一个耗时的IO操作。关于混合持久化为什么要做混合持久化?首先,fork动作需要复制页表,在大内存场景阻塞服务器,有100ms或秒级别的延迟尖峰,更适合每天定时的全量备份场景。一些云厂商的方案,比如:redis+rocksDB。从以下几个方面来分析。数据写入Redis内存,记录增量,后台线程异步将增量应用到RocksDB数据可靠性RocksDB包含全量数据,完全避免Fork调用。启动时,从RocksDB中加载全量的索引信息到内存数据读取Redis内存中的数据,直接读取,数据不在Redis内存中,整体换入Redis重新读取,对于简单的命令,可以直接从RocksDB读取。关于RcoksDB,RcoksDB的可嵌入、持久键值存储,RocksDB项目起源于Facebook的一个实验项目,旨在开发一个性能媲美快速内存(尤其是闪存)的数据库软件,以应对高负载服务。这是一个c++库,可用于存储键和值,可以是任意大小的字节流。它支持原子读写。RocksDB具有高度灵活的配置功能,可以配置运行在多种生产环境中,包括纯内存、Flash、硬盘或者HDFS。支持多种压缩算法,提供方便的生产环境维护和调试工具。数据结构编码每个key对应一个RocksDBmetakey,metakey存储key的元数据。所有元键相邻存储,加载效率高。复杂键的每个元素对应RocksDB中的一个Datakey,元素相邻存储在RocksDB中,访问效率高。简化String类型,结合metakey和Datakey优化IO。数据交换模型(SWAPMODE)内存数据换出:后台定时检查是否超过内存使用阈值,根据访问评估率和大小选择key,准备淘汰。磁盘数据换入:一个key被频繁读取或有o(n)次读操作,将整个key换入RocksDB,加快访问速度;写入key后,数据不再存入内存,全部换入内存;默认后台多线程异步换入。主从同步&Migration全同步:使用RocksDBcheckoutpoint代替RDB,避免fork系统调用。Standby只需要在收到checkoutpoint数据后加载metakey。增量同步:从aofbinlog位置对应的checkoutpoint继续增量同步,增量同步断开,直接从断开的位置恢复同步,不会触发全同步。其他一些知识流水线技术的本质:由客户端提供,流水线包含多条指令;客户端改变指令列表在流水线中的读写顺序,可以大大节省IO时间。自带压力测试工具redis-beanchmark。普通设置5wqps小对象压缩如果redis使用内存不超过4G,使用32bit编译。