Redis之一:数据结构和对象

来源:互联网 发布:福建广播电视网络 编辑:程序博客网 时间:2024/06/03 16:33

Redis是一个key-value存储系统。和Memcached类似。它支持存储的value类型相对更多,包括:string(字符串)、list(列表)、hash(哈希)、set(集合)、zset(有序集合)。为了保证效率,数据都是缓存在内存中,虽然数据的一些操作速度极大的提高,但是数据的大小受到了内存的限制。redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步。

在这里先简要引用别人对redis和Memcached两个主流的基于内存的存储系统的比较,总结如下:
(1)性能对比:由于Redis只使用单核,而Memcached可以使用多核,所以平均每一个核上Redis在存储小数据时比Memcached性能更高。而在100k以上的数据中,Memcached性能要高于Redis,虽然Redis最近也在存储大数据的性能上进行优化,但是比起Memcached,还是稍有逊色。
(2)内存使用效率对比:使用简单的key-value存储的话,Memcached的内存利用率更高,而如果Redis采用hash结构来做key-value存储,由于其组合式的压缩,其内存利用率会高于Memcached。
(3)Redis支持服务器端的数据操作:Redis相比Memcached来说,拥有更多的数据结构和并支持更丰富的数据操作,通常在Memcached里,你需要将数据拿到客户端来进行类似的修改再set回去。这大大增加了网络IO的次数和数据体积。在Redis中,这些复杂的操作通常和一般的GET/SET一样高效。所以,如果需要缓存能够支持更复杂的结构和操作,那么Redis会是不错的选择。

接下来就是介绍redis中的一些数据结构和对象了,因为现在也是学习,所以先从基础谈起,随着之后学习的深入,会进一步谈谈Redis和Memcached。。

数据结构

简单动态字符串

Redis本身是使用ANSI C语言编写的,但是Redis中并没有使用C语言传统的字符串表示。。它自己采用定义结构体的方式构建了一种名为简单动态字符串(SDC)的抽象类型,并将SDC用作Redis的默认字符串表示。
SDC的结构定义如下:

struct sdshdr {    //记录buf数组中已使用的字节数量,等于SDS所保存字符串的长度    int len;    //记录buf数组中未使用字节的数量,即空闲的空间    int free;    //字节数组,用于保存字符串    char buf[];}

SDS与C字符串的主要区别:

  • 获取C字符串的长度时间复杂度是O(N),获取SDS的长度的时间复杂度为O(1)。
  • C字符串容易导致缓冲区溢出,而SDS不存在缓冲区溢出的问题。
  • 当修改字符串时,SDS的内存重分配次数更少。
  • C字符串只能行末尾包含空字符串,而SDS可以保存任意的二进制数据。

跳跃表

跳跃表(skiplist)是一种有序数据结构,它通过在单个节点维持多个指向其他节点的指针,从而达到快速访问的目的。在Redis中只有两个地方用到了跳跃表,一个是实现有序集合键,另一个是在集群节点中用作内部数据结构。
大家可以查看下百度的跳跃表详细解释。

整数集合

整数集合(intset)是集合键的底层实现之一,当一个集合只包含整数元素,并且这个集合的元素个数不多时,Redis将使用整数集合作为集合键的底层实现。
整数集合的结构定义如下:

typedef struct intset {    //编码方式    uint32_t encoding;    //集合包含的元素的数量    unit32_t length;    //保存元素的数组    int8_t contents[];}intset;

压缩列表

压缩列表是Redis为了节约内存而开发的,是由一系列特殊编码的连续内存块组成的顺序型存储结构。一个压缩列表可以包含任意多个节点,每个节点可以保存一个字节数组或者一个整数值。

压缩列表被用作列表键和哈希键的底层实现之一。

对象

Redis基于上面介绍的数据结构,构建了一个对象系统,这个系统包括字符串对象、列表对象、哈希对象、集合对象、有序集合对象这五种类型对象。Redis的对象系统还提供了基于引用计数技术的内存回收机制,同时实现了对象共享机制,以节约内存。

Redis中的每个对象都由一个redisObject结构表示,其结构如下:

typedef struct redisObject {    //类型    unsigned type:4;    //编码    unsigned encoding:4;    //指向底层实现数据结构的指针    void *ptr;    //...}robj;

对象的类型:

类型常量 对象的名称 REDIS_STRING 字符串对象 REDIS_LIST 列表对象 REDIS_HASH 哈希对象 REDIS_SET 集合对象 REDIS_ZSET 有序集合对象


对象的编码:

编码常量 编码对应的底层数据结构 REDIS_ENCODING_INT long类型的整数 REDIS_ENCODING_EMBSTR embstr编码的简单动态字符串 REDIS_ENCODING_RAW 简单动态字符串 REDIS_ENCODING_HT 字典 REDIS_ENCODING_LINKEDLIST 双端链表 REDIS_ENCODING_ZIPLIST 压缩列表 REDIS_ENCODING_INTSET 整数集合 REDIS_ENCODING_SKIPLIST 跳跃表和字典

字符串对象

字符串对象的编码可以是int、raw或者embstr。
常用命令:SET/GET/APPEND/STRLEN/DECRBY/INCRBYFLOAT等。
应用场景:String是最常用的一种数据类型,普通的key/value存储都可以归为此类;

如果一个字符串对象保存的是整数值,并且这个整数值可以用long类型表示,则采用的是int编码方式;如果字符串对象保存的是一个字符串值,并且这个字符串值的长度大于32字节,则采用raw编码方式;如果字符串对象保存的是一个字符串值,并且这个字符串值的长度小于等于32字节,采用embstr编码方式。

列表对象

列表对象的编码方式可以是ziplist或者linkedlist。
常用命令:LPUSH/RPUSH/LPOP/RPOP/LINDEX等。
应用场景:Redis list的应用场景非常多,也是Redis最重要的数据结构之一,比如twitter的关注列表,粉丝列表等都可以用Redis的list结构来实现。

当列表对象可以同时满足以下两个条件时,列表对象使用ziplist编码:
(1)列表对象保存的所有字符串元素长度都小于64字节;
(2)列表对象保存的元素数量小于512个;

哈希对象

哈希对象的编码可以是ziplist或者hashtable。
常用命令:HSET/HGET/HEXISTS/HDEL/HLEN/HGETALL等。
应用场景:我们要存储一个用户信息对象数据,其中包括用户ID、用户姓名、年龄和生日,通过用户ID我们希望获取该用户的姓名或者年龄或者生日;

当哈希对象可以同时满足下列两个条件时,哈希对象使用ziplist编码:
(1)哈希对象保存的所有键值对的键和值的字符串长度都小于64字节;
(2)哈希对象保存的键值对的数量小于512个;

集合对象

集合对象的编码可以是intset或者hashtable。
常用命令:SADD/SCARD/SISMEMBER/SPOP等
应用场景:Redis set对外提供的功能与list类似是一个列表的功能,特殊之处在于set是可以自动排重的,当你需要存储一个列表数据,又不希望出现重复数据时,set是一个很好的选择,并且set提供了判断某个成员是否在一个set集合内的重要接口,这个也是list所不能提供的;

当集合对象满足下列两个条件时,使用intset编码:
(1)集合对象保存的所有元素都是整数值;
(2)集合对象保存的元素的数量不超过512个;

有序集合对象

有序集合的编码可以是ziplist或者skiplist。
常用命令:ZADD/ZCARD/ZCOUNT/ZRANGE等。
应用场景:Redis sorted set的使用场景与set类似,区别是set不是自动有序的,而sorted set可以通过用户额外提供一个优先级(score)的参数来为成员排序,并且是插入有序的,即自动排序。当你需要一个有序的并且不重复的集合列表,那么可以选择sorted set数据结构,比如twitter 的public timeline可以以发表时间作为score来存储,这样获取时就是自动按时间排好序的。

当有序集合对象满足下列两个条件时,使用zip编码方式:
(1)所有元素的长度小于64个字节;
(2)元素的个数小于128个;

总结:上面简单介绍了一些redis中的数据结构和对象的内容,这些知识可以说是redis的基础。中间省略了数据结构的详细解释,大家可以查阅Redis官方文档的详细解释。。

原创粉丝点击