开发者社区官方技术圈

阿里云开发者社区官方技术圈,用户产品功能发布、用户反馈收集等。

阿里云开发者社区官方技术圈,用户产品功能发布、用户反馈收集等。

1

回答

1.内存开销

在AOFRW期间,主进程会将fork之后的数据变化写进aof_rewrite_buf中,aof_rewrite_buf和aof_buf中的内容绝大部分都是重复的,因此这将带来额外的内存冗余开销。在Redis INFO中的aof_rewrite_buffer_length字段可以看到当前时刻aof_rewrite_buf占用的内存大小。如下面显示的,在高写入流量下aof_rewrite_buffer_length几乎和aof_buffer_length占用了同样大的内存空间,几乎浪费了一倍的内存。当aof_rewrite_buf占用的内存大小超过一定阈值时,我们将在Redis日志中看到如下信息。可以看到,aof_rewrite_buf占用了100MB的内存空间且主进程和子进程之间传输了2135MB的数据(子进程在通过pipe读取这些数据时也会有内部读buffer的内存开销)。对于内存型数据库Redis而言,这是一笔不小的开销。AOFRW带来的内存开销有可能导致Redis内存突然达到maxmemory限制,从而影响正常命令的写入,甚至会触发操作系统限制被OOM Killer杀死,导致Redis不可服务。2.CPU开销

CPU的开销主要有三个地方,分别解释如下:

1)在AOFRW期间,主进程需要花费CPU时间向aof_rewrite_buf写数据,并使用eventloop事件循环向子进程发送aof_rewrite_buf中的数据; 2)在子进程执行重写操作的后期,会循环读取pipe中主进程发送来的增量数据,然后追加写入到临时AOF文件;3)在子进程完成重写操作后,主进程会在backgroundRewriteDoneHandler 中进行收尾工作。其中一个任务就是将在重写期间aof_rewrite_buf中没有消费完成的数据写入临时AOF文件。如果aof_rewrite_buf中遗留的数据很多,这里也将消耗CPU时间。AOFRW带来的CPU开销可能会造成Redis在执行命令时出现RT上的抖动,甚至造成客户端超时的问题。

3.磁盘IO开销

如前文所述,在AOFRW期间,主进程除了会将执行过的写命令写到aof_buf之外,还会写一份到aof_rewrite_buf中。aof_buf中的数据最终会被写入到当前使用的旧AOF文件中,产生磁盘IO。同时,aof_rewrite_buf中的数据也会被写入重写生成的新AOF文件中,产生磁盘IO。因此,同一份数据会产生两次磁盘IO。

4.代码复杂度

Redis使用下面所示的六个pipe进行主进程和子进程之间的数据传输和控制交互,这使得整个AOFRW逻辑变得更为复杂和难以理解。以上内容摘自《阿里开发者手册-Redis专题》电子书,点击https://developer.aliyun.com/ebook/download/7770可下载完整版

游客c3gxxcx6cqeyo 评论 0

1

回答

当AOFRW被触发执行时,Redis首先会fork一个子进程进行后台重写操作,该操作会将执行fork那一刻Redis的数据快照全部重写到一个名为temp-rewriteaof-bg-pid.aof的临时AOF文件中。

由于重写操作为子进程后台执行,主进程在AOF重写期间依然可以正常响应用户命令。因此,为了让子进程最终也能获取重写期间主进程产生的增量变化,主进程除了会将执行的写命令写入aof_buf,还会写一份到aof_rewrite_buf中进行缓存。在子进程重写的后期阶段,主进程会将aof_rewrite_buf中累积的数据使用pipe发送给子进程,子进程会将这些数据追加到临时AOF文件中(详细原理可参考[1])。

当主进程承接了较大的写入流量时,aof_rewrite_buf中可能会堆积非常多的数据,导致在重写期间子进程无法将aof_rewrite_buf中的数据全部消费完。此时,aof_rewrite_buf剩余的数据将在重写结束时由主进程进行处理。

当子进程完成重写操作并退出后,主进程会在backgroundRewriteDoneHandler 中处理后续的事情。首先,将重写期间aof_rewrite_buf中未消费完的数据追加到临时AOF文件中。其次,当一切准备就绪时,Redis会使用rename 操作将临时AOF文件原子的重命名为server.aof_filename,此时原来的AOF文件会被覆盖。至此,整个AOFRW流程结束。以上内容摘自《阿里开发者手册-Redis专题》电子书,点击https://developer.aliyun.com/ebook/download/7770 可下载完整版

游客c3gxxcx6cqeyo 评论 0

1

回答

Redis支持内存规格配置,maxmemory和maxmemory-policy大家已经都很熟悉了,但是在这里我还是想再解释一下:maxmemory控制的是Redis的整体运行内存而非数据内存,例如client buffer、lua cache、fucntion cache、db metadata等这些都会算在运行内存中,如果运行内存超过了maxmemory就会触发evict删除数据,这也是用户在使用Redis时的一大痛点。

在这些非数据内存使用当中,又以client buffer消耗最大,在大流量场景下client需要缓存很多用户读写数据(想象一下keys的结果需要先缓存在client output buffer中再发送给用户),由于网络流量的内存消耗导致触发eviction删除数据的情况非常之多。虽然Redis很早就支持对client-output-buffer-limit配置项,但其限制的也只是单个连接维度的output buffer,没有全局统计client使用内存和限制。为了解决这个问题,7.0新增了maxmemory-clients配置项,来对所有client使用的内存做限制,如果超过了这个限制就会挑选内存消耗最大的client释放,用来缓解内存使用的消耗。

Client-eviction并不是终点,还有很多元数据的内存使用会对用户造成困扰,Redis是基于内存的数据库,我们需要对各个模块的内存进行更精确的统计和控制,让用户能够对数据存储有更为清晰的理解和规划。以上内容摘自《阿里开发者手册-Redis专题》电子书,点击https://developer.aliyun.com/ebook/download/7770 可下载完整版

游客c3gxxcx6cqeyo 评论 0

1

回答

Function是Redis脚本方案的全新实现,在Redis7.0之前用户只能使用EVAL命令族来执行Lua脚本,但是Redis对Lua脚本的持久化和主从复制一直是undefined状态,在各个大版本甚至release版本中也都有不同的表现。因此社区也直接要求用户在使用Lua脚本时必须在本地保存一份(这也是最为安全的方式),以防止实例重启、主从切换时可能造成的Lua脚本丢失,维护Redis中的Lua脚本一直是广大用户的痛点。

Function的出现很好的对Lua脚本进行了补充,它允许用户向Redis加载自定义的函数库,一方面相对于EVALSHA的调用方式用户自定义的函数名可以有更为清晰的语义,另一方面Function加载的函数库明确会进行主从复制和持久化存储,彻底解决了过去Lua脚本在持久化上含糊不清的问题。

那么自7.0开始,Function命令族和EVAL命令族有了各自明确的定义:FUNCTION LOAD会把函数库自动进行主从复制和持久化存储;而SCRIPT LOAD则不会进行持久化和主从复制,脚本仅保存在当前执行节点。并且社区也在计划后续版本中让Function支持更多语言,例如JavaScript、Python等,敬请期待。

总的来说,Function在7.0中被设计为数据的一部分,因此能够被保存在RDB、AOF文件中,也能通过主从复制将Function由主库复制到所有从库,可以有效解决之前Lua脚本丢失的问题,我们也非常建议大家逐步将Redis中的Lua脚本替换为Function。以上内容摘自《阿里开发者手册-Redis专题》电子书,点击https://developer.aliyun.com/ebook/download/7770 可下载完整版

游客c3gxxcx6cqeyo 评论 0

1)第一阶段是主从库间建立连接、协商同步的过程

主要是为全量复制做准备。从库和主库建立起连接,并告诉主库即将进行同步,主库确认回复后,主从库间就可以开始同步了。

具体来说,从库给主库发送psync命令,表示要进行数据同步,主库根据这个命令的参数来启动复制。psync命令包含了主库的runID和复制进度offset两个参数。runID,是每个Redis实例启动时都会自动生成的一个随机ID,用来唯一标记这个实例。

当从库和主库第一次复制时,因为不知道主库的runID,所以将runID设为“?”。offset,此时设为-1,表示第一次复制。主库收到psync命令后,会用FULLRESYNC响应命令带上两个参数:主库runID和主库目前的复制进度offset,返回给从库。

从库收到响应后,会记录下这两个参数。这里有个地方需要注意,FULLRESYNC响应表示第一次复制采用的全量复制,也就是说,主库会把当前所有的数据都复制给从库。

2)第二阶段,主库将所有数据同步给从库

从库收到数据后,在本地完成数据加载。这个过程依赖于内存快照生成的RDB文件。

具体来说,主库执行bgsave命令,生成RDB文件,接着将文件发给从库。从库接收到RDB文件后,会先清空当前数据库,然后加载RDB文件。这是因为从库在通过replicaof命令开始和主库同步前,可能保存了其他数据。

为了避免之前数据的影响,从库需要先把当前数据库清空。在主库将数据同步给从库的过程中,主库不会被阻塞,仍然可以正常接收请求。否则,Redis的服务就被中断了。但是,这些请求中的写操作并没有记录到刚刚生成的RDB文件中。为了保证主从库的数据一致性,主库会在内存中用专门的replication buffer,记录RDB文件生成后收到的所有写操作。

3)第三个阶段,主库会把第二阶段执行过程中新收到的写命令,再发送给从库

具体的操作是,当主库完成RDB文件发送后,就会把此时replication buffer中的修改操作发给从库,从库再重新执行这些操作。这样一来,主从库就实现同步了。以上内容摘自《阿里开发者手册-Redis专题》电子书,点击https://developer.aliyun.com/ebook/download/7770 可下载完整版

游客c3gxxcx6cqeyo 评论 0

1

回答

游客lmkkns5ck6auu 2022-10-11 202浏览量 回答数 1

公告

阿里云开发者社区官方技术圈,用户产品功能发布、用户反馈收集等。

展开