Redis 小调研

1、 概况:

Redis是一款开源的、网络化的、基于内存的、可进行数据持久化的Key-Value存储系统。它的数据模型创建在外层,相似于其它结构化存储系统,是经过Key映射Value的方式来创建字典以保存数据,有别于其它结构化存储系统的是,它支持多类型存储,包括String、List、Set、Sortset和Hash等,你能够在这些数据类型上作不少原子性操做。redis

在操做方面,Redis基于TCP协议的特性使得它能够经过管道的方式进行数据操做,Redis自己提供了一个可链接Server的客户端,经过客户端,可方便地进行数据存取操做。数据库

Redis具备如下优势:

a。单线程,利用redis队列技术并将访问变为串行访问,消除了传统数据库串行控制的开销数组

b。redis具备快速和持久化的特征,速度快,由于数据存在内存中。缓存

c。分布式读写分离模式安全

d。支持丰富数据类型服务器

e。支持事务,操做都是原子性,所谓原子性就是对数据的更改要么所有执行,要不所有不执行。网络

f。可用于缓存,消息,按key设置过时时间,过时后自动删除。数据结构

h。支持多种主流语言交互。app

Redis最适合全部数据in-momory的场景,虽然Redis也提供持久化功能,但实际更多的是一个disk-backed的功能,跟传统意义上的持久化有比较大的差异。分布式

 

2、 原理简介:

redis核心对象

        redisObject

Redis内部使用一个redisObject对象来表示全部的keyvalue
redisObject最主要的信息如上图所示:

type表明一个value对象具体是何种数据类型
encoding是不一样数据类型在redis内部的存储方式,好比:type=string表明value存储的是一个普通字符串,那么对应的encoding能够是raw或者是int,若是是int则表明实际redis内部是按数值型类存储和表示这个字符串的,固然前提是这个字符串自己能够用数值表示,好比:"123""456"这样的字符串。
vm字段,只有打开了Redis的虚拟内存功能,此字段才会真正的分配内存,该功能默认是关闭状态的。

五种数据类型的使用和内部实现方式:
1String
经常使用命令:set/get/decr/incr/mget等;
应用场景:String是最经常使用的一种数据类型,普通的key/value存储均可以归为此类;
实现方式:String在redis内部存储默认就是一个字符串,被redisObject所引用,当遇到incr、decr等操做时会转成数值型进行计算,此时redisObject的encoding字段为int。

2Hash
经常使用命令:hget/hset/hgetall等
应用场景:咱们要存储一个用户信息对象数据,其中包括用户ID、用户姓名、年龄和生日,经过用户ID咱们但愿获取该用户的姓名或者年龄或者生日;
实现方式:Redis的Hash实际是内部存储的Value为一个HashMap,并提供了直接存取这个Map成员的接口。如图2所示,Key是用户ID,value是一个Map。这个Map的key是成员的属性名,value是属性值。这样对数据的修改和存取均可以直接经过其内部Map的Key(Redis里称内部Map的key为field),也就是经过key(用户ID)+field(属性标签)就能够操做对应属性数据。当前HashMap的实现有两种方式:当HashMap的成员比较少时Redis为了节省内存会采用相似一维数组的方式来紧凑存储,而不会采用真正的HashMap结构,这时对应的value的redisObject的encoding为zipmap,当成员数量增大时会自动转成真正的HashMap,此时encoding为ht。

3List
经常使用命令:lpush/rpush/lpop/rpop/lrange等;
应用场景:Redislist的应用场景很是多,也是Redis最重要的数据结构之一,好比twitter的关注列表,粉丝列表等均可以用Redis的list结构来实现;
实现方式:Redislist的实现为一个双向链表,便可以支持反向查找和遍历,更方便操做,不过带来了部分额外的内存开销,Redis内部的不少实现,包括发送缓冲队列等也都是用的这个数据结构。

4Set
经常使用命令:sadd/spop/smembers/sunion等;
应用场景:Redisset对外提供的功能与list相似是一个列表的功能,特殊之处在于set是能够自动排重的,当你须要存储一个列表数据,又不但愿出现重复数据时,set是一个很好的选择,而且set提供了判断某个成员是否在一个set集合内的重要接口,这个也是list所不能提供的;
实现方式:set的内部实现是一个value永远为null的HashMap,实际就是经过计算hash的方式来快速排重的,这也是set能提供判断一个成员是否在集合内的缘由。

5SortedSet
经常使用命令:zadd/zrange/zrem/zcard等;
应用场景:Redissortedset的使用场景与set相似,区别是set不是自动有序的,而sortedset能够经过用户额外提供一个优先级(score)的参数来为成员排序,而且是插入有序的,即自动排序。当你须要一个有序的而且不重复的集合列表,那么能够选择sortedset数据结构,好比twitter的publictimeline能够以发表时间做为score来存储,这样获取时就是自动按时间排好序的。
实现方式:Redissortedset的内部使用HashMap和跳跃表(SkipList)来保证数据的存储和有序,HashMap里放的是成员到score的映射,而跳跃表里存放的是全部的成员,排序依据是HashMap里存的score,使用跳跃表的结构能够得到比较高的查找效率,而且在实现上比较简单。

Redis的持久化
Redis虽然是基于内存的存储系统,可是它自己是支持内存数据持久化的,并且提供两种主要的持久化策略:RDB快照和AOF日志。

RDB快照:Redis支持将当前数据的快照存成一个数据文件的持久化机制。
可是一个持续写入的数据库如何生成快照呢?Redis借助了fork命令的copyonwrite机制。在生成快照时,将当前进程fork出一个子进程,而后在子进程中循环全部的数据,将数据写成为RDB文件。

咱们能够经过Redis的save指令来配置RDB快照生成的时机,好比你能够配置当10分钟之内有100次写入就生成快照,也能够配置当1小时内有1000次写入就生成快照,也能够多个规则一块儿实施。这些规则的定义就在Redis的配置文件中,你也能够经过Redis的CONFIGSET命令在Redis运行时设置规则,不须要重启Redis。

Redis的RDB文件不会坏掉,由于其写操做是在一个新进程中进行的,当生成一个新的RDB文件时,Redis生成的子进程会先将数据写到一个临时文件中,而后经过原子性rename系统调用将临时文件重命名为RDB文件,这样在任什么时候候出现故障,Redis的RDB文件都老是可用的。同时,Redis的RDB文件也是Redis主从同步内部实现中的一环。

可是,咱们能够很明显的看到,RDB有他的不足,就是一旦数据库出现问题,那么咱们的RDB文件中保存的数据并非全新的,从上次RDB文件生成到Redis停机这段时间的数据所有丢掉了。在某些业务下,这是能够忍受的,咱们也推荐这些业务使用RDB的方式进行持久化,由于开启RDB的代价并不高。可是对于另一些对数据安全性要求极高的应用,没法容忍数据丢失的应用,RDB就无能为力了,因此Redis引入了另外一个重要的持久化机制:AOF日志。

AOF日志:AOF日志的全称是appendonlyfile,从名字上咱们就能看出来,它是一个追加写入的日志文件。
通常数据库的binlog不一样的是,AOF文件是可识别的纯文本,它的内容就是一个个的Redis标准命令。固然,并非发送发Redis的全部命令都要记录到AOF日志里面,只有那些会致使数据发生修改的命令才会追加到AOF文件
那么每一条修改数据的命令都生成一条日志,那么AOF文件是否是会很大?答案是确定的,AOF文件会愈来愈大,因此Redis又提供了一个功能,叫作AOFrewrite。其功能就是从新生成一份AOF文件,新的AOF文件中一条记录的操做只会有一次,而不像一份老文件那样,可能记录了对同一个值的屡次操做。其生成过程和RDB相似,也是fork一个进程,直接遍历数据,写入新的AOF临时文件。在写入新文件的过程当中,全部的写操做日志仍是会写到原来老的AOF文件中,同时还会记录在内存缓冲区中。当重完操做完成后,会将全部缓冲区中的日志一次性写入到临时文件中。而后调用原子性的rename命令用新的AOF文件取代老的AOF文件。
AOF是一个写文件操做,其目的是将操做日志写到磁盘上,因此它也一样会遇到咱们上面说的写操做的5个流程。那么写AOF的操做安全性又有多高呢。实际上这是能够设置的,在Redis中对AOF调用write(2)写入后,什么时候再调用fsync将其写到磁盘上,经过appendfsync选项来控制,下面appendfsync的三个设置项,安全强度逐渐变强。
1)appendfsyncno
当设置appendfsync为no的时候,Redis不会主动调用fsync去将AOF日志内容同步到磁盘,因此这一切就彻底依赖于操做系统的调试了。对大多数Linux操做系统,是每30秒进行一次fsync,将缓冲区中的数据写到磁盘上。
2)appendfsynceverysec
当设置appendfsync为everysec的时候,Redis会默认每隔一秒进行一次fsync调用,将缓冲区中的数据写到磁盘。可是当这一次的fsync调用时长超过1秒时。Redis会采起延迟fsync的策略,再等一秒钟。也就是在两秒后再进行fsync,这一次的fsync就无论会执行多长时间都会进行。这时候因为在fsync时文件描述符会被阻塞,因此当前的写操做就会阻塞。因此结论就是,在绝大多数状况下,Redis会每隔一秒进行一次fsync。在最坏的状况下,两秒钟会进行一次fsync操做。这一操做在大多数数据库系统中被称为groupcommit,就是组合屡次写操做的数据,一次性将日志写到磁盘。
3)appednfsyncalways
当设置appendfsync为always时,每一次写操做都会调用一次fsync,这时数据是最安全的,固然,因为每次都会执行fsync,因此其性能也会受到影响。

Redis的内存管理机制
Redis的内存管理机制主要经过源码中的zmalloc.h和zmalloc.c两个文件来实现的。Redis为了方便内存的管理,在分配一块内存以后,会将这块内存的大小存入内存的头部。

如图所示,real_ptr是redis调用malloc后返回的指针。redis将内存的大小size存入头部,size所占据的内存大小是已知的,为size_t类型的长度,而后返回ret_ptr。当须要释放内存的时候,ret_ptr被传给内存管理程序。经过ret_ptr,程序能够很容易的计算出real_ptr的值,而后将real_ptr传给free释放内存。

Redis经过定义一个数组来记录全部的内存分配状况,这个数组的长度为ZMALLOC_MAX_ALLOC_STAT.数组的每个元素表明当前程序所分配的内存块的个数,且内存块的个数,且内存块的大小为该元素的下标。
在源码中,这个数组为zmalloc_allocations。zmalloc_allocations[16]表明已经分配的长度为16bytes的内存块的个数。zmalloc.c中有一个静态变量used_memory用来记录当前分配的内存总大小。因此,总的来看,Redis采用的是包装的mallc/free,相较于Memcached的内存管理方法来讲,要简单不少。

 

总而言之,Redis键值数据库中五种主要的数据存储结构以及它们支持的各类应用场景,使得Redis具备了强大的性能和普遍的功能,很大程度上补偿了memcached等键值数据库的不足,在不少场合对关系型数据库也起到了很好的补充做用。它支持主从结构且彻底实现了发布/订阅机制,在主从服务器搭建、处理冗余数据和读写操做的可拓展性方面都具备至关的优点。

(参考http://chentian114.iteye.com/blog/2270254)

相关文章
相关标签/搜索