Redis的快照为什么不会阻塞其他哀求?
The child process and the parent process run in separate memory spaces. At the time of fork() both memory spaces have the same content. Memory writes, file mappings (mmap(2)), and unmappings (munmap(2)) performed by one of the processes do not affect other. 除此之外,子历程险些是父历程的完备副本(Exact duplicate),然而这两个历程在以下的一些方面会有较小的区别: 子历程用于独立且独一的历程 ID; 子历程的父历程 ID 与父历程 ID 完全沟通; 子历程不会担任父历程的内存锁; 子历程会从头配置历程资源操作率和 CPU 计时器; ... 最要害的点在于父子历程的内存在 fork 时是完全沟通的,在 fork 之后举办写入和修改也不会彼此影响,这着实就美满的办理了快照这个场景的题目 —— 只必要某个时刻点下内存中的数据,而父历程可以继承对本身的内存举办修改,这既不会被阻塞,也不会影响天生的快照。 写时拷贝 既然父历程和子历程拥有完全沟通的内存空间而且两者对内存的写入都不会彼此影响,那么是否意味着子历程在 fork 时必要对父历程的内存举办全量的拷贝呢?假设子历程必要对父历程的内存举办拷贝,这对付 Redis 处事来说根基都是劫难性的,尤其是在以下的两个场景中: 内存中存储大量的数据,fork 时拷贝内存空间会耗损大量的时刻和资源,会导致措施一段时刻的不行用; Redis 占用了 10G 的内存,而物理机可能假造机的资源上限只有 16G,在这时我们就无法对 Redis 中的数据举办耐久化,也就是说 Redis 对呆板上内存资源的最大操作率不能高出 50%; 假如无法办理上面的两个题目,行使 fork 来天生内存镜像的方法也无法真正落地,不是一个工程中真正可以行使的要领。 就算离开了 Redis 的场景,fork 时全量拷贝内存也是难以接管的,假设我们必要在呼吁行中执行一个呼吁,我们必要先通过 fork 建设一个新的历程再通过 exec 来执行措施,fork 拷贝的大量内存空间对付子历程来说也许完全没有任何浸染的,可是却引入了庞大的特殊开销。 写时拷贝(Copy-on-Write)的呈现就是为了办理这一题目,就像我们在这一节开头先容的,写时拷贝的首要浸染就是将拷贝推迟到写操纵真正产生时,这也就停止了大量有时义的拷贝操纵。在一些早期的 *nix 体系上,体系挪用 fork 确实会立即对父历程的内存空间举办复制,可是在本日的大都体系中,fork 并不会立即触发这一进程: 在 fork 函数挪用时,父历程和子历程会被 Kernel 分派到差异的假造内存空间中,以是在两个历程看来它们会见的是差异的内存: 在真正会见假造内存空间时,Kernel 会将假造内存映射到物理内存上,以是父子历程共享了物理上的内存空间; 当父历程可能子历程对共享的内存举办修改时,共享的内存才会以页为单元举办拷贝,父历程会保存原有的物理空间,而子历程会行使拷贝后的新物理空间; 在 Redis 处事中,子历程只会读取共享内存中的数据,它并不会执行任何写操纵,只有父历程会在写入时才会触发这一机制,而对付大大都的 Redis 处事可能数据库,写哀求每每都是远小于读哀求的,以是行使 fork 加上写时拷贝这一机制可以或许带来很是好的机能,也让 BGSAVE 这一操纵的实现变得很是简朴。 总结 Redis 实现靠山快照的方法很是奇妙,通过操纵体系提供的 fork 和写时拷贝的特征垂手可得的就实现了这个成果,从这里我们就能看出作者对付操纵体系常识的把握还长短常踏实的,大多人在面临相同的场景时,想到的要领也许就是手动实现相同『写时拷贝』的特征,然而这不只增进了事变量,还增进了措施呈现题目的也许性。 到这里,我们简朴总结一下 Redis 为什么在行使 RDB 举办快照时会通过子历程的方法举办实现: 通过 fork 建设的子历程可以或许得到和父历程完全沟通的内存空间,父历程对内存的修改对付子历程是不行见的,两者不会彼此影响; 通过 fork 建设子历程时不会立即触发大量内存的拷贝,内存在被修改时会以页为单元举办拷贝,这也就停止了大量拷贝内存而带来的机能题目; (编辑:湖南网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |