redis - 底层数据类型

一:String

struct string_name {  
    // buf 中已占用空间的长度  
    int len;  
    // buf 中剩余可用空间的长度  
    int free;  
    // 数据空间  
    char buf[];  
};

  • string是redis最基本的类型,你可以理解成与Memcached一模一样的类型,一个key对应一个value。
  • string类型是二进制安全的。意思是redis的string可以包含任何数据。比如jpg图片或者序列化的对象 。
  • string类型是Redis最基本的数据类型,一个redis中字符串value最多可以是512

常用命令:

- set/get/del/append/strlen 
- Incr/decr/incrby/decrby,一定要是数字才能进行加减
- getrange/setrange
- setex(set with expire)键秒值 / setnx(set if not exist)
- mset/mget/msetnx
- getset(先get再set)

String应用场景:普通的key/ value 存储都可以归为此类.即可以完全实现目前 Memcached 的功能,并且效率更高。还可以享受Redis的定时持久化,操作日志及 Replication ,常规计数:微博数,粉丝数等等功能。

二:list

typedef struct listnode {
  struct listNode *prev;
  struct listNode *next;
  void *value;
} listNode;

typedef struct list {
  listNode *head;
  listNode *tail;
  unsigned long len;
  // ....省略其他定义
} list;

压缩列表
列表中保存的单个数据(有可能是字符串类型的)小于 64 字节,列表中数据个数少于 512 个。
压缩列表这种存储结构,一方面比较节省内存,另一方面可以支持不同类型数据的存储。而且,因为数据存储在一片连续的内存空间,通过键来获取值为列表类型的数据,读取的效率也非常高。

更多 ziplist 参见:https://blog.csdn.net/xiejing...

双向链表

 

  • 双端链表:带有指向前置节点和后置节点的指针,获取这两个节点的复杂度为O(1)
  • 无环:表头节点的prev和表尾节点的next都指向NULL,对链表的访问以NULL结束
  • 链表长度计数器:带有len属性,获取链表长度的复杂度为O(1)
  • 多态:链表节点使用 void*指针保存节点值,可以保存不同类型的值

常用命令:

#push:插入 l(left):栈顶,插入与读取顺序相反 r(right):栈尾,顺序相同 	lrange:查
lpush/rpush/lrange
LPUSH list1 1 2 3 4 5 
LRANGE list1 0 -1	#0和-1是索引值,0~-1表示查全部 结果为 5 4 3 2 1
LPUSH list2 1 2 3 4 5 
LRANGE list2 0 -1 #1 2 3 4 5

#弹出,l:栈顶,后进先出 r:栈尾,先进先出
lpop/rpop
lpop list1 #4 3 2 1
rpop list1 #4 3 2

#按照索引下标获得元素(从上到下)
lindex
lindex list2 2 #3

#获取list长度
llen
llen list2 #5

# 删N个value
lrem key N value
rpush list3 1 1 2 2 2 2
lrem list3 3 2 #删除3个2 结果:1 1 2

#截取指定范围的值后再赋值给key
ltrim key start end
rpush list4 1 2 3 4 5
ltrim list4 0 2
lrange list4 0 -1 #1 2 3

#移除源列表的最后一个元素添加到目的列表
rpoplpush 源列表 目的列表

#通过索引改变列表值
lset key index value

#在目标值前/后插入value
linsert key before/after 目标值 value

list应用场景:1.最新消息排行。2.消息队列,3.聊天室读取消息与发送消息。可以利用Lists的PUSH操作,将任务存在Lists中,然后工作线程再用POP操作将任务取出进行执行。

三:Hash

压缩列表
字典中保存的键和值的大小都要小于 64 字节;字典中键值对的个数要小于 512 个。

散列表
Redis 使用MurmurHash2这种运行速度快、随机性好的哈希算法作为哈希函数。对于哈希冲突问题,Redis 使用链表法来解决。除此之外,Redis 还支持散列表的动态扩容、缩容。

当数据动态增加之后,散列表的装载因子会不停地变大。为了避免散列表性能的下降,当装载因子大于 1 的时候,Redis 会触发扩容,将散列表扩大为原来大小的 2 倍左右,redis使用渐进式扩容缩容策略,将数据的搬移分批进行,避免了大量数据一次性搬移导致的服务停顿。

当数据动态减少之后,为了节省内存,当装载因子小于 0.1 的时候,Redis 就会触发缩容,缩小为字典中数据个数的大约 2 倍大小

哈希冲突
key是我们的键;v是键值,可以是一个指针,也可以是整数或浮点数;next属性是指向下一个哈希表节点的指针,可以让多个哈希值相同的键值对形成链表,解决键冲突问题

常用命令:

#添加一个/查询一个/添加多个/查询多个/删除一个或多个
hset/hget/hmset/hmget/hgetall/hdel key field value
hset user id 1
hmset user name birdy age 18
hmget user id	#1
hmget user #1 birdy 18
hgetall user #id 1 name birdy age 18
#获取哈希表key的字段数量
hlen key
hlen user #3
#哈希表key是否存在field属性
hexists key field
#获取哈希表key的所有属性/值
hkeys/hvals key
#为哈希表key执行字段的整数值/浮点值增加increment
hincrby/hincrbyfloat key filed increment
#添加并判断是否已存在,若存在则添加失败
hsetnx key field value

应用场景

  1. 由于hash数据类型的key-value的特性,用来存储关系型数据库中表记录,是redis中哈希类型最常用的场景。一条记录作为一个key-value,把每列属性值对应成field-value存储在哈希表当中,然后通过key值来区分表当中的主键。
  2. 经常被用来存储用户相关信息。优化用户信息的获取,不需要重复从数据库当中读取,提高系统性能。
  3. hash 类型十分适合存储对象类数据,相对于在 string 中介绍的把对象转化为 json 字符串存储,hash 的结构可以任意添加或删除‘字段名’,更加高效灵活。

四:Set

集合这种数据类型用来存储一组不重复的数据,有两种实现方法。

一种是有序数组
存储的数据都是整数,存储的数据元素个数不超过 512 个,就会使用有序数组。

另一种是散列表
如果不是有序数组的限制,Redis 就使用散列表来存储集合中的数据。

集合中最大的成员数为 2(32次方)- 1 (4294967295, 每个集合可存储40多亿个成员)。如果 key 已经持有其他值,就覆写旧值,无视类型。

Java程序员都知道HashSet的内部实现使用的是HashMap,只不过所有的value都指向同一个对象。Redis的set结构也是一样,它的内部也使用hash结构,所有的value都指向同一个内部值。

 常用命令:

#添加/查询/是否存在
sadd/smembers/sismember key member

#获取集合里面的元素个数
scard key

#删除集合中元素
srem key value 

#随机返回集合中1个或多个随机数
srandmember key [count] 

#随机出栈
spop key 

#将key1里的某个值赋给key2
smove key1 key2 在key1里某个值
#数学集合类
差集:sdiff 交集:sinter 并集:sunion
sadd set1 1 2 3 4 5
sadd set2 a b c 1 2
sdiff set1 set2 #3 4 5
sinter set1 set2 #1 2
sunion set1 set2 #1 2 3 4 5 a b c 真实情况下这些值是没有顺序的

应用场景
交集,并集,差集
当你需要存储一个列表数据,又不希望出现重复数据时,set是一个很好的选择,并且set提供了判断某个成员是否在一个set集合内的重要接口,这个也是list所不能提供的。

在微博中,可以将一个用户所有的关注人存在一个集合中,将其所有粉丝存在一个集合。Redis还为集合提供了求交集、并集、差集等操作,可以非常方便的实现如共同关注、共同喜好、二度好友等功能,对上面的所有集合操作,你还可以使用不同的命令选择将结果返回给客户端还是存集到一个新的集合中。

五:Zset

在set基础上,加一个score值。 之前set是k1 v1 v2 v3, 现在zset是k1 score1 v1 score2 v2

压缩列表

当数据量比较小的时候,Redis 会用压缩列表来实现有序集合。具体点说就是,使用压缩列表来实现有序集合的前提,有这样两个:所有数据的大小都要小于 64 字节;元素个数要小于 128 个。

跳表
Redis 中的有序集合是通过跳表来实现的,严格点讲,其实还用到了散列表

其中,插入、删除、查找以及迭代输出有序序列这几个操作,红黑树也可以完成,时间复杂度跟跳表是一样的。但是,按照区间来查找数据这个操作,红黑树的效率没有跳表高。对于按照区间查找数据这个操作,跳表可以做到 O(logn) 的时间复杂度定位区间的起点,然后在原始链表中顺序往后遍历就可以了。这样做非常高效。当然,Redis 之所以用跳表来实现有序集合,还有其他原因,比如,跳表更容易代码实现。虽然跳表的实现也不简单,但比起红黑树来说还是好懂、好写多了,而简单就意味着可读性好,不容易出错。还有,跳表更加灵活,它可以通过改变索引构建策略,有效平衡执行效率和内存消耗。不过,跳表也不能完全替代红黑树。因为红黑树比跳表的出现要早一些,很多编程语言中的 Map 类型都是通过红黑树来实现的。

 常用命令:

#向有序集合中添加或更新成员/查询成员
zadd key score1 member/zrange key 索引start 索引end [withscores]
ZADD score 60 d 70 c 80 b 90 a
ZCARD score 0 -1 #d c b a 只有member
ZCARD score 0 -1 withscores #60 d 70 c 80 b 90 a 

#返回满足分数区间的值
zrangebyscore key min max
ZRANGEBYSCORE score 60 80 #d c b
选项:
	withscores 是否带分数返回
	( 不包含
	limit 开始索引 结束索引
#删除有序结合成员
zrem key 某分数对应下member
ZREM score d
ZRANGE score 0 -1  #c b a
#计算有序区间的成员数
zcard/zcount key min max
#获得索引值
zrank key member
zrank score a #2,a的索引为2
#获得成员对应分数
zscore key member
zscore score a #90
#逆序获得下标值
zrevrank key member
zrevrank score a #0

#逆序查询成员
zrevrange key start end 
#查询区间成员并逆序显示
zrevrangebyscore key min max

应用场景:Redis sorted set的使用场景与set类似,区别是set不是自动有序的,而sorted set可以通过用户额外提供一个优先级(score)的参数来为成员排序,并且是插入有序的,即自动排序。当你需要一个有序的并且不重复的集合列表,那么可以选择sorted set数据结构,比如twitter 的public timeline可以以发表时间作为score来存储,这样获取时就是自动按时间排好序的。 另外还可以用Sorted Sets来做带权重的队列,比如普通消息的score为1,重要消息的score为2,然后工作线程可以选择按score的倒序来获取工作任务。让重要的任务优先执行。(1.去重排序 2.优先级队列 3.“亲密度”排序显示好友)

六:其他命令


版权声明:本文为ApatheCrazyFan原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。