redis源代码分析24–VM(中)

VM根据value换进换出的策略又有两种使用方式:阻塞方式和多线程方式(server.vm_max_threads == 0为阻塞方式)。

这一节主要介绍阻塞方式。

redis 启动重建db(aof方式或者快照方式)时,可能会因为内存限制将某些value换出到磁盘,此时只使用阻塞方式换出 value(vmSwapOneObjectBlocking函数)。除此之外,redis只在serverCron函数(该函数事件处理章节分析过)中换出value。我们来看看serverCron中的处理代码,阻塞方式使用函数vmSwapOneObjectBlocking换出value,多线程方式使用函数vmSwapOneObjectThreaded换出value。 继续阅读

发表在 redis | 留下评论

redis源代码分析23–VM(上)

VM是Redis2.0新增的一个功能。在没有VM之前,redis会把db中的所有数据放在内存中。随着redis的不断运行,所使用的内存会越来越大。但同时,client对某些数据的访问频度明显会比其他数据高。redis引入VM功能来试图解决这个问题。简言之,VM使得redis会把很少访问的value保存到磁盘中。但同时,所有value的key都放在内存中,这是为了让被换出的value的查找在启用VM前后性能差不多。

VM在redis中算是redis中最复杂的模块之一,我们分三节来介绍。这一节介绍redis的主要数据结构,下一节介绍非阻塞方式,最后一节介绍多线程方式。

我们先来看看redis中的通用对象结构redisObject : 继续阅读

发表在 redis | 一条评论

redis源代码分析22–协议

redis默认使用tcp协议的6379端口,其协议是文本行格式的而不是二进制格式的,每一行都以”\r\n”结尾,非常容易理解。

参考ProtocolSpecification.html就知道,发布到redis的命令有如下几种返回格式(对于不存在的值,会返回-1,此时client library应返回合适的nil对象(比如C语言的NULL),而不是空字符串):

1)第一个字节是字符“-”,后面跟着一行出错信息(error reply) 继续阅读

发表在 redis | 留下评论

redis源代码分析21– 事务

redis的事务较简单,并不具备事务的acid的全部特征。主要原因之一是redis事务中的命令并不是立即执行的,会一直排队到发布exec命令才执行所有的命令;另一个主要原因是它不支持回滚,事务中的命令可以部分成功,部分失败,命令失败时跟不在事务上下文执行时返回的信息类似。不知道在未来会不会提供更好的支持。

我们且来看看现在redis事务的实现。

redis中跟事务相关的主要结构如下所示。每个redisClient的multiState保存了事务上下文要执行的命令。 继续阅读

发表在 redis | 一条评论

redis源代码分析20–发布/订阅

redis的发布/订阅(publish/subscribe)功能类似于传统的消息路由功能,发布者发布消息,订阅者接收消息,沟通发布者和订阅者之间的桥梁是订阅的channel或者pattern。发布者向指定的publish或者pattern发布消息,订阅者阻塞在订阅的channel或者pattern。可以看到,发布者不会指定哪个订阅者才能接收消息,订阅者也无法只接收特定发布者的消息。这种订阅者和发布者之间的关系是松耦合的,订阅者不知道是谁发布的消息,发布者也不知道谁会接收消息。

redis的发布/订阅功能主要通过SUBSCRIBE、UNSUBSCRIBE、PSUBSCRIBE、PUNSUBSCRIBE 、PUBLISH五个命令来表现。其中SUBSCRIBE、UNSUBSCRIBE用于订阅或者取消订阅channel,而PSUBSCRIBE、PUNSUBSCRIBE用于订阅或者取消订阅pattern,发布消息则通过publish命令。

对于发布/订阅功能的实现,我们先来看看几个与此相关的结构。 继续阅读

发表在 redis | 一条评论

redis源代码分析19–主从复制

先说下Redis主从复制的特点。

官方文档ReplicationHowto中提到以下特点:
1. 一个master支持多个slave
2. slave可以接受其他slave的连接,作为其他slave的master,从而形成一个master-slave的多级结构
3. 复制在master端是非阻塞的,也就是master在向client复制时可处理其他client的命令,而slave在第一次同步时是阻塞的
4. 复制被利用来提供可扩展性,比如可以将slave端用作数据冗余,也可以将耗时的命令(比如sort)发往某些slave从而避免master的阻塞,另外也可以用slave做持久化,这只需要将master的配置文件中的save指令注释掉。

client可以在一开始时作为slave连接master,也可以在运行后发布sync命令,从而跟master建立主从关系。

接下来我们分别从slave和master的视角概述下redis的主从复制的运行机制。 继续阅读

发表在 redis | 一条评论

redis源代码分析18–持久化之aof

Redis的aof功能的目的是在性能和持久化粒度上对持久化机制提供更好的支持。

快照方式持久化的粒度有时间(秒)和改变的key数两种,如果持久化的粒度较小,对性能会有较大的影响,因为每次都是dump整个db;如果持久化的粒度较大,则在指定时间内指定数目的数据的持久化无法保证。而aof持久化的粒度是每次会修改db数据的命令,因此粒度是最小的了,跟日志方式有点类似,由于仅记录一条命令,性能也最好。另外,跟日志类似,aof文件会越来越大,则可以通过执行BGREWRITEAOF命令在后台重建该文件。

我们先来看看redis如何记录命令的。

call函数是命令执行的函数(前面命令处理章节已详细介绍过该函数)。如果命令执行前后数据有修改,则server.dirty的取值会有变化。在启用了aof机制的情况下,call函数会调用feedAppendOnlyFile保存命令及其相关参数。 继续阅读

发表在 redis | 一条评论

redis源代码分析17–持久化之快照

redis的持久化支持快照方式。快照方式会将整个db dump到磁盘上。

client 可以发布save/bgsave命令让server将db dump到磁盘上。其中bgsave会执行后台dump(新建子进程执行dump),而save是阻塞式的dump db,会影响其他client的命令执行。除了发布命令执行快照保存外,redis的serverCron也会按照配置的参数执行后台dump,另外 slave建立连接时,master也会执行一个后台dump,然后才发送数据给slave(这在主从复制一节中介绍)。 继续阅读

发表在 redis | 一条评论

redis源代码分析16–阻塞式命令

redis现在只支持对list的阻塞式操作,相关的两个命令是brpop和blpop。

这两个命令在list中有元素时,跟普通的pop没有区别,弹出list的一个元素,然后返回。但在list没有元素时,会为redisClient设置REDIS_BLOCKED标志,然后client阻塞(设置REDIS_BLOCKED标志的redisClient会一直阻塞,参考命令处理章节),一直到新元素加入时(push操作的处理函数pushGenericCommand),才会返回。

这两个命令设置的处理函数brpopCommand和blpopCommand都会调用blockingPopGenericCommand。该函数在检查list中有元素后,会调用非阻塞的popGenericCommand来弹出一个元素,否则调用blockForKeys来处理阻塞的情况。 继续阅读

发表在 redis | 一条评论

redis源代码分析15–val加载机制

这一节主要介绍下val的加载。

对于某些命令,比如get somekey,当运行到processCommand时可能key对应的val不在内存中。在运行命令绑定的处理函数之前,redis会提前加载其val。

在 processCommand中,在vm开启并启用多线程时,会调用 blockClientOnSwappedKeys来加载可能已swap的val,如果blockClientOnSwappedKeys返回0,说明有 swap的val没被加载,则返回不调用call了(此时client会设置 REDIS_IO_WAIT标志,并已放到等待列表中)。代码如下: 继续阅读

发表在 redis | 一条评论