reids数据库是一种内存数据库,也提供了两种持久化的方式。作为内存数据库, 访问数据的速度肯定是杠杠的。但是随着数据的不断增加,消耗的内存也就越来越多直到内存消耗完。这种问题要么增加内存,要么就是将内存中的很少用到的数据替换到硬盘中。redis采用的就是第二种方法,也就是redis中的虚拟内存实现的功能。redis采用了阻塞式更替和非阻塞式更替,这个等会再讲。先来看下虚拟内存的初始化:
void vmInit(void) {
......
if (server.vm_max_threads != 0)
zmalloc_enable_thread_safeness(); /* we need thread safe zmalloc() */
if ((server.vm_fp = fopen(server.vm_swap_file,"r+b")) == NULL) {
server.vm_fp = fopen(server.vm_swap_file,"w+b");
}
......
server.vm_next_page = 0;
server.vm_near_pages = 0;
server.vm_stats_used_pages = 0;
server.vm_stats_swapped_objects = 0;
server.vm_stats_swapouts = 0;
server.vm_stats_swapins = 0;
totsize = server.vm_pages*server.vm_page_size;
......
server.io_newjobs = listCreate();
server.io_processing = listCreate();
server.io_processed = listCreate();
server.io_ready_clients = listCreate();
......
if (aeCreateFileEvent(server.el, server.io_ready_pipe_read, AE_READABLE,
vmThreadedIOCompletedJob, NULL) == AE_ERR)
oom("creating file event");
}
既然是初始化,肯定是给虚拟内存的属性值进行赋值。
vm_max_threads就是虚拟化最大的线程数,这个也是在非阻塞更替时用到的。既然是多线程,肯定就需要线程间的同步。
io_newjobs:redis在把数据置换出内存还是从硬盘中load到内存都是新建一个job,然后插入到io_newjobs中。而下面的io_processing,io_processed也是相同的概念。
io_ready_clients:客户端因为操作某个key,导致客户端阻塞,当这个key被改变就会把阻塞的客户端插入到io_ready_clients队列中。
最后创建一个可读事件,这个事件很重要,后面会讲到。
redis会有两种方式将内存数据置换到虚拟内存或者把虚拟内存中的数据load到内存中。第一种:采用定时器方式实现,前面也讲过redis的定时器都是在serverCron中调用。第二种就是rdb文件load和aof文件load的时候会调用。主要不同是rdb,aof文件load采用的都是阻塞式更替。而serverCron就需要看配置文件中的配置来决定。下面来看着这三个地方的代码:
aofLoad:
force_swapout = 0;
if ((zmalloc_used_memory() - server.vm_max_memory) > 1024*1024*32)
force_swapout = 1;
if (server.vm_enabled && force_swapout) {
while (zmalloc_used_memory() > server.vm_max_mem