Redis 高性能内存存储

来源:互联网 发布:华为帐号无法获取数据 编辑:程序博客网 时间:2024/06/11 15:40

Redis是一个开源,先进的key-value存储,并用于构建高性能,可扩展的Web应用程序的完美解决方案。

Redis从它的许多竞争继承来的三个主要特点:

  • Redis数据库完全在内存中,使用磁盘仅用于持久性。

  • 相比许多键值数据存储,Redis拥有一套较为丰富的数据类型。

  • Redis可以将数据复制到任意数量的从服务器。


Redis 优势

  • 异常快速:Redis的速度非常快,每秒能执行约11万集合,每秒约81000+条记录。

  • 支持丰富的数据类型:Redis支持最大多数开发人员已经知道像列表,集合,有序集合,散列数据类型。这使得它非常容易解决各种各样的问题,因为我们知道哪些问题是可以处理通过它的数据类型更好。

  • 操作都是原子性:所有Redis操作是原子的,这保证了如果两个客户端同时访问的Redis服务器将获得更新后的值。

  • 多功能实用工具:Redis是一个多实用的工具,可以在多个用例如缓存,消息,队列使用(Redis原生支持发布/订阅),任何短暂的数据,应用程序,如Web应用程序会话,网页命中计数等。

Redis - 数据类型

Redis支持5种类型的数据类型,它描述如下的:

1、字符串:Redis字符串是字节序列。Redis字符串是二进制安全的,这意味着他们有一个已知的长度没有任何特殊字符终止,

     可以存储任何东西,512兆为上限。

2、哈希:Redis的哈希是键值对的集合。 Redis的哈希值是字符串字段和字符串值之间的映射,因此它们被用来表示对象。

3、列表:Redis的列表是简单的字符串列表,排序插入顺序。您可以添加元素到Redis的列表的头部或尾部。

      列表的最大长度为 232 - 1 元素(4294967295,每个列表中可容纳超过4十亿的元素)。

4、集合:Redis的集合是字符串的无序集合。在Redis您可以添加,删除和测试文件是否存在,在成员O(1)的时间复杂度。

      集合中的元素最大数量为 232 - 1 (4294967295,可容纳超过4十亿元素)。

5、有序集合:Redis的有序集合类似于Redis的集合,字符串不重复的集合。不同的是,一个有序集合的每个成员用分数,以便采取

     有序set命令,从最小的到最大的成员分数有关。虽然成员具有唯一性,但分数可能会重复。


----------------------------------------- 以上的内容都是网上整理的。

下面的简要的分析一下Redis的源码而得来:

1、网络IO模型
Redis使用单线程的IO复用模型,自己封装了一个简单的AeEvent事件处理框架,主要实现了epoll、kqueue和select,对于单纯只有IO操作来说,单线程可以将速度优势发挥到最大,但是Redis也提供了一些简单的计算功能,比如排序、聚合等,对于这些操作,单线程模型实际会严重影响整体吞吐量,CPU计算过程中,整个IO调度都是被阻塞住的。

2、内存管理方面

允许按配置使用tcmalloc,jemalloc等快速、内存使用率高的库,并支持统计内存使用率。

利用zmalloc对基本的c函数:malloc、calloc、free进行一定的封装。其实最重要的就是加了一个长度前缀:

#define PREFIX_SIZE (sizeof(long long))
void *zmalloc(size_t size) {
    void *ptr = malloc(size+PREFIX_SIZE);
    *((size_t*)ptr) = size;
    return (char*)ptr+PREFIX_SIZE;
}
zmalloc是典型的空间换时间思想。对统计总的内存使用量具有极大的帮助(尤其是调用free之后,实时统计内存使用量),避免了对所有的内存handler进行遍历。

3、分布式架构
当前Redis Cluster支持的最大节点数就是4096。Redis Cluster使用的分布式算法也很简单:crc16( key ) % HASH_SLOTS_NUMBER


为了保证单点故障下的数据可用性,Redis Cluster引入了Master节点和Slave节点。如图4所示,在Redis Cluster中,每个Master节点都会有对应的两个用于冗余的Slave节点。

4、主要文件列表
adlist.c     //双向链表
ae.c     //
事件驱动
    ae_epoll.c //epoll
接口, linux
    ae_kqueue.c //kqueue
接口, freebsd
    ae_select.c //select
接口, windows
anet.c     //
网络处理
aof.c     //
处理AOF文件
config.c     //
配置文件解析
db.c     //DB
处理
dict.c     //hash

intset.c     //
转换为数字类型数据
multi.c     //
事务,多条命令一起打包处理
networking.c //
读取、解析和处理客户端命令
object.c     //
各种对像的创建与销毁,stringlistsetzsethash
rdb.c     //redis
数据文件处理
redis.c    //
程序主要文件
replication.c     //
数据同步master-slave
sds.c     //
字符串处理
sort.c     //
用于listsetzset排序
t_hash.c     //hash
类型处理
t_list.c     //list
类型处理
t_set.c     //set
类型处理
t_string.c     //string
类型处理
t_zset.c     //zset
类型处理

ziplist.c     //
节省内存方式的list处理
zipmap.c     //
节省内存方式的hash处理
zmalloc.c     //
内存管理

最复杂的代码在 db.c 及 dict.c 中、这块需要慢慢深入学习。

5、整个服务的处理流程

redis是单线程模式,所有的请求、处理都是在同一个线程里面进行,也就是一个无限循环,在这个无限循环的内部有两件事要做,第一 件就是调用通过aeSetBeforeSleepProc函数设置的回调函数,第二件就是开始接受客户端的请求和处理。

void aeMain(aeEventLoop *eventLoop) {
    eventLoop->stop = 0;
    while (!eventLoop->stop) {
        if (eventLoop->beforesleep != NULL)
            eventLoop->beforesleep(eventLoop);
        aeProcessEvents(eventLoop, AE_ALL_EVENTS);
    }
}


/* This function gets called every time Redis is entering the
 * main loop of the event driven library, that is, before to sleep
 * for ready file descriptors. */
void beforeSleep(struct aeEventLoop *eventLoop) ; 
1、释放客户端的阻塞操作
2、将客户端命令写入文件缓冲区(合适时候写到硬盘上)

/* Process every pending time event, then every pending file event
 * (that may be registered by time event callbacks just processed).
 * Without special flags the function sleeps until some file event
 * fires, or when the next time event occurs (if any).
 *
 * If flags is 0, the function does nothing and returns.
 * if flags has AE_ALL_EVENTS set, all the kind of events are processed.
 * if flags has AE_FILE_EVENTS set, file events are processed.
 * if flags has AE_TIME_EVENTS set, time events are processed.
 * if flags has AE_DONT_WAIT set the function returns ASAP until all
 * the events that's possible to process without to wait are processed.
 *
 * The function returns the number of events processed. */
int aeProcessEvents(aeEventLoop *eventLoop, int flags)
1、整个服务端的socket里面要分监听socket和客户端socket、接发新的客户端连接且当客户端有消息时触发客户端事件回调处理函数。
2、处理定时器:关闭太久没通讯的链接、key值的swap操作、主从同步等。

数据的处理流程:
processInputBuffer()->processInlineBuffer()->processCommand()->lookupCommand()->call(c->cmd->proc(c))

最后以一个逻辑图示:



最后说明一下数据的请求格式:

测试命令:
"*3\r\n$3\r\nSET\r\n$00000001\r\n1\r\n$00000001\r\n1\r\n"
命令的行数+1\r\n  $命令的长度\r\n命令\r\n 【$第一个参数长度\r\n第一个参数$第二个参数度长\r\n第二个参数 ... n】

可参考函数redisFormatCommand() 组织命令:
redisFormatCommand(&cmd,"SET %s %s","foo","bar");
strncmp(cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$3\r\nbar\r\n",len) == 0;
len == 4+4+(3+2)+4+(3+2)+4+(3+2);

1 0