Redis 不是一个普通的 key-value存储器,它实际上是一个支持不同类型的值数据结构服务。虽然在传统的 key-value 服务中,将字符串键与字符串值相关联,但在 Redis 中值不仅限于简单的字符串,还可以保存更复杂的数据结构。
Redis 内部整体的存储结构是一个大的HashMap,key冲突通过 链表去实现,每个dictEntry为一个key/value对象,value为RedisObject。
RedisServer --> RedisDB(dict) -- > dictht --> dicEntry --> redisObject --> Data Types
redisServer:默认包含16个 redisDb
redisDb: *dict 该数据库的键空间
dictht:通过两个Hash tables 存储键值对数据
ht[0]:存储正常情况下的所有数据
ht[1] :只有ReHash过程中使用
dicEntry:在dict.h文件中定义 dictEntry **table 数组,每个元素单个键值对指针,还包含下一个Entry指针,
redisObject -> prt :具体的数据结构指针
dictEntry:存储key->value的地方,dictEntry结构体
typedef struct dictType { uint64_t (*hashFunction)(const void *key); // hash the given key void *(*keyDup)(void *privdata, const void *key); // copy key void *(*valDup)(void *privdata, const void *obj); // copy value int (*keyCompare)(void *privdata, const void *key1, const void *key2); // compare keys void (*keyDestructor)(void *privdata, void *key); // destroy key void (*valDestructor)(void *privdata, void *obj); // destroy value int (*expandAllowed)(size_t moreMem, double usedRatio); // check if dict can be expanded} dictType;typedef struct dict { dictType *type; // hook functions void *privdata; // private data to be used in hooks dictht ht[2]; // two hash table instances for incremental rehashing long rehashidx; // rehashing not in progress if rehashidx == -1 unsigned long iterators; // number of iterators currently running} dict;// redis Hash表typedef struct dictht { //数组中每一个元素指向具体的dicEntry dictEntry **table; unsigned long size; unsigned long sizemask; unsigned long used;} dictht;/* * 字典 */typedef struct dictEntry { // 键 void *key; // 值 union { // 指向具体redisObject void *val; // uint64_t u64; int64_t s64; } v; // 指向下个哈希表节点,形成链表 struct dictEntry *next;} dictEntry;/* * Redis 对象 */typedef struct redisObject { // 类型 4bits unsigned type:4; // 编码方式 4bits unsigned encoding:4; // LRU 时间(相对于 server.lruclock) 24bits unsigned lru:22; // 引用计数 Redis里面的数据可以通过引用计数进行共享 32bits int refcount; // 指向对象的值 64-bit void *ptr;} robj;
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 (跳跃表和字典)
REDIS_ENCODING_QUICKLIST (快速列表)
REDIS_ENCODING_STREAM (流)
redisDb 对象包含 dict,dict 包含哈希表 dicttht,dictht 包含 dictEntry 链表的数组。
dict -> ht -> table[i] 称为桶。
Redis 使用单独的链接来解决哈希冲突。
Redis 总是以 2 的幂次方分配内存,默认初始哈希表大小为 4。
dict 包含两个哈希表实例:ht[0] & ht[1]。在正常情况下,ht[0] 包含所有数据 & ht[1] 为 NULL。但是当 redis 需要调整 ht[0] 的大小时,ht[1] 就会变为非 null。
如果 redis 容量不足,就会发生哈希表重组,它分为两个独立的过程:resize 和 rehash。
Redis Resize 触发条件,包含扩容(resize up)和收缩(resize down)
load_factor(负载车因子) = ht[0].used / ht[0].size
扩容触发条件(满足其一):
used / size ≥ 1 并且 dict_can_resize 全局标志为 enabled
在 used / size ≥ 5 时,强制调整大小
收缩触发条件:dic size > DICT_HT_INITIAL_SIZE , 且used / size < 0.1
两个 resize 操作中,redis 确保哈希表的最小大小为 DICT_HT_INITIAL_SIZE = 4。
resize down由后台 cron 作业完成。在满足必要条件的情况下,每个dic添加操作都可能发生resize up。
resize操作完成后,dict -> rehashidx 设置为 0,表示可以开始 rehash 操作。
rehashidx 是 dict -> ht[0] 上的索引。
rehashidx = -1 表示当前没有 rehash 正在进行。
rehash 发生在 resize 完成后,它逐渐将条目从 ht[0] 迁移到 ht[1]。
rehash 发生在 resize 完成后,逐渐将entries从 ht[0] 迁移到 ht[1]
rehash 过程是从 dict -> ht[0] -> table索引为[rehashidx]的元素 , 移动到 dict -> ht[1] -> table 某个索引 h 处
rehash 过程是一个多步操作,因为一次将大量数据从一个hash table移动到另一个,可能会导致redis 长时间阻塞
具体过程如下:
dict -> rehashidx 设置为 0,表示可以开始 rehash 操作
每次对dic执行Read&Write,同时将进行ht[0] -> ht[1]单步移动操作,并将rehashidx + 1
在向dic添加数据时,会将新entry添加到 dict -> ht[1] 。 并将新条目添加到桶位置的链表头部,以便将来找到该条目变得更快。
当ht[0]所有数据全部移动到ht[1]后,ht[1] 将重新分配给 ht[0] , ht[1] 重置为空 ,
rehashidx 被设置为 - 1 表示rehash过程结束
Redis key是二进制安全的,可以使用任何二进制序列作为key,从“foo”之类的字符串到 JPEG 文件的内容。
空字符串也是一个有效的key , 允许的最大key大小为 512 MB。
非常长的key,在数据集中查询时,可能导致多次消耗资源的key比较,如果确实存在一个大key,可以对key通过hash函数转换,例如使用SHA1。
key需要具有可读性,例如:user:1000:followers,如果缩写成“u1000flw”,虽然消耗更少内存,但不具备可读性。需要在长度和可读性中找到平稳点
key命名规则:“对象:id”,如“user:1000”,组合单词使用格式建议使用“-”分开,比如redisObject 命名为 redis-object。例:“comment:1234:reply-to”
字符串是 Redis 最基础最通用的构建块之一,它是一种二进制安全的数据结构。 它可以存储任何类型的数据——字符串、整数、浮点值、JPEG 图像、序列化的 Ruby 对象等其它任何数据。 还可以对整个字符串或部分进行操作,以及增加或减少整数和浮点数。Redis 字符串值的最大长度为 512 M。
Binary Safe (二进制安全) : 将输入的数据作为原始的无任何特殊格式意义的二进制。简单的说“数据在写入时是什么样的, 它被读取时就是什么样。”
Raw 是 redisObject + sds ,意思是 redisObject ptr指针 指向一个sds 对象
SDS Simple Dynamic Strings(简单动态字符串)
SDS 是 C 的字符串库,旨在通过添加堆分配的字符串来增强有限的 libc 字符串处理功能:使用更简单、二进制安全、计算效率更高、与普通的 C 字符串函数兼容。
3.2 之前版本len和free都使用unsigned int 占4个字节,取值范围:0到4,294,967,295,通常字符串都小于这个值,在 Redis 3.2 版本之后,将 SDS 划分为 5 种类型:sdshdr5(不实际使用,只取flags)、sdshdr8、sdshdr16、sdshdr32、sdshdr64,根据字符串长度初始化不同的sdshdr
struct sdshdr { unsigned int len; unsigned int free; char buf[];};//Redis 3.2之后版本struct __attribute__ ((__packed__)) sdshdr5 { unsigned char flags; /* 3低位type,5高位表示字符串长度 */ char buf[];};struct __attribute__ ((__packed__)) sdshdr8 { uint8_t len; /* used */ uint8_t alloc; /* excluding the header and null terminator */ unsigned char flags; /* 3 lsb of type, 5 unused bits */ char buf[];};sdshdr8 ... sdshdr64static inline char sdsReqType(size_t string_size) { if (string_size < 1<<5) return SDS_TYPE_5; if (string_size < 1<<8) return SDS_TYPE_8; if (string_size < 1<<16) return SDS_TYPE_16; if (string_size < 1ll<<32) return SDS_TYPE_32; return SDS_TYPE_64;}
Redis的embstr编码方式和raw编码方式分界线为39字节(3.2之前版本)或44字节,如果一个字符串值的长度小于等于44字节,则按照embstr进行编码,否则按raw进行编码
Embstr(Embedded String),是一种保存短字符串的特殊编码方式。与raw不同的是,raw会调用内存分配函数两次,创建redisobject结构和sdshdr结构,而embstr代码会调用一次内存分配函数,分配一块连续的空间,包括redisobject和sdshdr两种结构。
Embstr 具有以下优点:
embstr 编码将创建字符串对象所需的内存分配和释放次数从两次减少到一次
由于embstr 编码字符串对象的所有数据都存储在一个连续的内存中,这些编码字符串对象比原始编码对象字符串可以更好地利用缓存(CPU 缓存/缓存行)。
Embstr的缺点:
如果字符串长度增加,需要重新分配内存,SDS需要重新分配空间,所以embstr编码的字符串对象实际上是只读的,redis并没有为embstr编码的字符串对象写任何相应的修饰符。当我们对 embstr 编码的字符串对象执行任何修改命令(如 append)时,先将对象的编码从 embstr 转换为 raw,然后执行修改命令
redis> SET msg hello OK redis> OBJECT ENCODING msg embstr redis> DEBUG OBJECT msg Value at:0x7fd74ecac8a0 refcount:1 encoding:embstr serializedlength:6 lru:815344 lru_seconds_idle:14 redis> APPEND msg world 10redis> OBJECT ENCODING msg raw redis> DEBUG OBJECT msg Value at:0x7fd76445d0b0 refcount:1 encoding:raw serializedlength:11 lru:815482 lru_seconds_idle:26
redisObject = 16byte = type 4bit + encoding 4bit + lru 24bit + refcount 4byte + ptr 8byte。
sdshdr = len 1byte + alloc 1byte + Flag 1byte + ‘\0’ 1byte + buf 长度。 (3.2之前)
从 2.4 版本开始,redis 使用了 jemalloc内存分配器。可以简单理解为jemalloc并不是一个字节来申请和分配。它将分配 8、16、32、64 字节的内存(如果需要 12 字节,将分配 16 字节)。最小的 embstr 是 16 + 8 + 1 = 25,所以最小分配是 64 字节。当字符数小于 39 时,将分配 64 个字节。这是默认的 39。
jemalloc作为redis默认的内存分配器,在减少内存碎片方面比较擅长。在 64 位系统中,jemalloc 将内存空间分为三个范围:small、large 和 huge;每个范围被划分为许多小的内存块单元; redis在存储数据时,会选择大小最合适的内存块进行存储。
在 3.2 版本中,原来的 sdshdr已更改为 sdshdr8、sdshdr16、sdshdr32、sdshdr64。 sdshdr8中的unsigned int改成了uint8、uint16……(还增加了一个char标志),优化小SDS对象的内存使用
Embstr本身是针对短字符串的,自然会使用最小的sdshdr8,只比之前版本的sdshdr少5个字节(),因此它可以容纳的字符串长度增加了 5 个字节,达到 44 个。
sdsdr8 = uint8_t * 2 + char1 , 1 * 2 + 1 = 3
sdshdr = unsigned int * 2 , 4 * 2 = 8
如果一个字符串对象持有一个整数,并且整数值可以通过long类型来识别(不超过long的范围),那么字符串对象redisobject的指针会直接保存long值(将void *转换为long), 没有 sdshdr 对象。
Long 与指针的字节数完全相同(如64位服务器占用8个字节,“9223372036854775807”为8位字节所能表示的最大整数,其十六进制形式为:0x7fffffffffffffffL ,所以该值不能超过9223372036854775807)。
取数据时,将指针地址改为long 值:(long) O -> PTR。
不同Redis客户端,连接同一个Redis实例时,一定要使用同一编码格式
你可以在 Redis 中使用字符串做一些有趣的事情,例如:
使用INCR系列中的命令将字符串用作原子计数器:INCR、DECR、INCRBY。
> set counter 100OK> incr counter(integer) 101> incr counter(integer) 102> incrby counter 50(integer) 152> DECRBY counter 2(integer) 150
使用APPEND 命令附加到字符串。
使用GETRANGE 和SETRANGE类似数组下标,获取或更改字符串中某一段内容
使用 GETBIT 和 SETBIT 创建一个 Redis 支持的 Bloom Filter。
特殊命令SETEX,SETEX是SET、EXPIRE的原子操作命令
Session共享:比如Spring boot 可使用@EnableRedisHttpSession 注解,实现多实例Session共享
SETEX spring:session:sessions:7e8383a4-082c-4ffe-a4bc-c40fd3363c5e 1800 any
Redis List是一个有序集合,允许添加重复元素。可以将元素添加到 Redis 列表中,将新元素push到列表的头部(左侧)或尾部(右侧)。list类似于大多数编程语言中的数组,添加到列表中的每个值都会自动分配一个从 0 开始的index。
LPUSH 命令在头部插入一个新元素,而 RPUSH 在尾部插入一个新元素。当对不在的Key执行此push操作时,将创建一个新list。对list进行清空操作,则从键空间中删除key。
list操作和结果示例:
LPUSH mylist a # 列表是 "a" LPUSH mylist b # 列表是 "b","a" RPUSH mylist c # 列表是 "b","a","c"
列表的最大长度为 2^32 - 1 个元素(4294967295,每个列表超过 40 亿个元素)。
从时间复杂度的角度来看,Redis Lists 的主要特点是支持恒定时间插入和删除靠近头部和尾部的元素,即使插入了数百万个元素。访问列表的极端附近的元素非常快,但如果访问一个非常大的list的中间,则速度很慢,因为这是一个 O(N) 操作。
RPUSH :RPUSH key-name value [value …] 向list右端(尾部)添加一个或多个元素
LPUSH: LPUSH key-name value [value …] 向list左端(头部)添加一个或多个元素
RPOP: RPOP key-name 从列表中删除并返回最右边的元素
LPOP: LPOP key-name 从列表中删除并返回最左边的元素
LINDEX: LINDEX key-name offset 根据下标返回指定元素
LRANGE:LRANGE key-name start end 返回列表中从 start 到 end 范围的元素
LTRIM:LTRIM key-name start end 保留start 和 end 索引范围内的元素,删除其它
BLPOP BLPOP key-name [key-name ...] timeout 非空 list 中返回并删除最左边的元素,如为空则等元素且N秒后超时返回
BRPOP BRPOP key-name [key-name ...] timeout 非空list中返回并删除最右边的元素,如为空则等元素且N秒后超时返回
BRPOPLPUSH:BRPOPLPUSH source-key dest-key timeout — 从源获取最右边的元素,将返回的元素 LPUSH 到目标最左边,且将元素返回给用户,如果源为空,则等待超时
本节概述linked list 、ziplist、quick list的实现原理和各自的特性
Redis linked list 是一个简单的双向列表,在进行unshit和push指令操作时,linked list会自动维护列表头部和尾部的长度,因此在统计list长度时不需要遍历整个list,时间复杂度为恒定 O(1)
链表结构示例: [0] <-> [1] <-> [2] <-> … <-> [N]
[0] 是列表的头部,[N] 是列表的尾部,各元素通过previous和next指针相连
List底层结构说明:
linked list 每个节点都是一个listNode结构体,包含三个指针:前一个previous node、下一个next node、 值value。 3 个指针 = 8 个字节 * 3 = 共占用 24 个字节
listNode值是指向 struct robj(redisObject)的指针,其中包含:元数据(metadata)、引用计数(reference count)和指向内容的指针。2 个整数 + 1 个指针 = 2 * 4 个字节 + 1 * 8 个字节 = 共 16 个字节
redisObject 中的value指向一个带有两个整数字段和字符串内容的 Redis sds。 (2 个整数 + 内容 = 2 * 2 字节 + 内容 = 4 字节 + 内容)
每向linked list插入1个元素,Redis 会创建大约 40 字节的元数据以及内存分配导致的额外开销,假如存储 10 个字节的数据,将产生超过 40 个字节的元数据,最终需要的内存是实际内容的4倍
头/尾访问时间复杂度O(1)
append或prepend时间复杂度O(1)
从任意一端删除时间复杂度O(1)
任何内部删除只是将前一个/下一个指针连接在一起
任何内部插入只会在兄弟节点中创建新的上一个/下一个指针
访问元素慢,时间复杂度O(N)
需要额外的内存开销来存储小值
遍历列表时缓存效率不高
链表在理论上很好,但如果您需要存储许多小元素,Linked List就不太适合了。链式指针在遍历列表时不允许任何缓存局部性,并且所有指针都会产生我们可以避免的额外开销
为了解决Linked List的开销大于内容的问题,Redis 提供了一种替代列表 ziplist
ziplist 存储高效、无指针数据结构。使用连续的内存块存储所有元素。使得数据局部性很好,整个数据结构可以适应 CPU 的缓存。但是这种连续内存块会使插入到列表的中间或从列表的中间删除,变得不友好
因此Redis 将 ziplist 的使用限定为存储了小值和小元素计数的列表。Redis 会随着列表长度的增长或缩小自动在 ziplist 编码和Linked List编码之间切换。
ziplist结构示例:[size总大小][tail offset尾偏移][count缓存元素计数][entry 0]...[entry N][END]
空 ziplist 占用 11 个字节:[size=4 bytes][tail offset=4 bytes][count=2 bytes][END=1 byte]
ziplist entry 结构:[entry header][contents]
entry header 结构:[length of previous entry][length of this entry]
ziplists entry header 存储了前一个元素的长度,因此也可以从后到前遍历列表
ziplist 元素使用可变编码的方式,存储小数据时,偏移量也很小。存储数字类型数据时,entry使用integers替代String类型。
ziplist 也有一个有效的编码方案来存储Strings:
字符串的长度适合 6 位,则大小存储在元数据字节内
字符串的长度适合 14 位,则大小存储在元数据字节附加一个额外的字节内,总共使用了两个字节
字符串长度超过 16,383 个字符,则使用4 个字节来存储字符串的长度
顺序内存读写
不需要内部指针
高效的整数存储和高效的偏移记录
头/尾访问时间复杂度O(1)
插入列表需要将所有后续元素向下移动
从列表中删除需要将所有后续元素向上移动
插入如果需要重新分配内存(内存块必须扩容)这可能导致整个列表被复制到一个新的内存位置
quick list结构示例:[ziplist 0] <-> [ziplist 1] <-> ... <-> [ziplist N]
quick list为每个ziplist存储list-max-ziplist-entries个元素。当一个Ziplist增长超过这个数字时,一个新的链表节点就会被创建,并有创建一个新的Ziplist。每个持有Ziplist的链接列表节点都存储了一个Ziplist长度的缓存计数,因此在遍历操作时可以跳过整个Ziplist范围
在访问quick list的头部和尾部时,时间复杂度为O(1),非常高效。但将新元素插入到列表中间已经满了的ziplist节点中,需要对现有ziplist进行拆分,才可以保持单个ziplist节的元素数量维持在list-max-ziplist-entries个
存储任意长度的列表时都能有效地使用内存
高效的内存表示方式,便于高速缓存的遍历
对列表头部和尾部的访问时间复杂度O(1)
删除大范围的数据不需要遍历,可以直接删除整个内部ziplists节点
保留现有的RDB和AOF格式,因此旧的DB可以被加载到新的实现中
如果您将 ziplist 填充因子 (list-max-ziplist-entries) 设置每个 ziplist 一个entry,将达到与传统Linked List相同的性能,但内存利用率更高
在列表的中间插入元素可能需要拆分现有的Ziplist并创建新的内部quick list节点。
从列表的中间删除元素可能需要重新连接quick list
插入都会在 ziplist 本身上调用 realloc,可能会触发整个 ziplist 的内存副本到新内存。
头部插入需要将现有的Ziplist条目向下移动一个位置
用于显示最后发布N篇文章或内容,LPUSH将最后发布的文章插入到列表头部,并LTRIM删除最后一条
RPUSH mylist "one"RPUSH mylist "two"RPUSH mylist "three"LTRIM mylist 1 -1LRANGE mylist 0 -11) "two"2) "three"
用于社交媒体中的timeline,使用 LPUSH 以在用户timeline中添加新元素,并使用 LRANGE 以检索一些最近更新的内容。
Linked List 提供对列表头部和尾部的时间复杂度 O(1) ,允许从任意一端进行恒定时间插入和删除。但是如果要存储许多小值(时间戳、对其他数据的引用、用户名等),数据结构本身内存开销将大于数据内容。
ziplists 对列表头部和尾部的时间复杂度 O(1),但append到列表需要重新分配内存(可能会将整个列表复制到新内存),并且追加到列表需要重新分配以及显式移动当前内存到新的偏移量,整个列表可能会在插入时被复制两次,但内存中的拷贝非常快
删除 ziplist 的尾部元素很快:只需将 [END] 标记向上移动一个条目并更新有关 ziplist 大小和计数的缓存数据
删除 ziplist 的 head 元素比较慢:必须删除 head 元素,然后将剩余元素上移一位,因为 ziplist 是一个连续的内存空间
ziplist 可能会在插入/删除时复制和移动其全部内容,因此最好限制ziplist大小以加快操作。可以将小的 ziplist 链接在一起以允许快速的单 ziplist 操作,同时仍然不限制我们可以存储在高效、无指针数据结构中的元素数量。
Redis Set 是字符串的无序集合。在 Set 中添加、删除和测试成员的存在,无论集合中包含多少数量的元素,都是恒定的时间复杂性O(1) 。
还可以在非常高效的进行集合集合运算,获取并集、交集、差集。
Redis Set 不允许重复,集合中的最大成员数为 2^32 - 1(4294967295,每组超过 40 亿个成员)。
你可以使用 Redis Sets 做很多有趣的事情,例如你可以:
set是无序集合
因为set是无序的,无法通过下标获取特定的值
不允许重复元素,多次添加重复元素,只保留一份
添加:SADD key_name value-1 value-2 value-3
获取:SMEMBERS key_name
统计集合中成员数量:SCARD key_name
集合差集:SDIFF main_set compare_set-1 compare-set-2 ... 注:SDIFF 命令不存储比较结果
集合差集并保存结果:SDIFFSTORE new_result_set main_set compare_set-1 compare_set-2
交集:SINTER main_set compare_set-1 compare-set-2
交集并保存结果:SINTERSTORE new_result_set main_set compare_set-1 compare_set-2
固定范围内的抽奖活动,比如公司年会,将所有员工放入set,随机获取set中某一个或N个员工
# 添加员工唯一标识> sadd lottery 1 2 3 4 5(integer) 5# 随机抽取1...N名员工(不删除)> srandmember lottery 11) "4"# 随机抽取1...N名员工(并把抽中员工从集合删除)> spop lottery 11) "1"
统计访问用户数
#添加访问用户1> sadd app-name:dau uid1(integer) 1#添加访问用户2> sadd app-name:dau uid2(integer) 1#统计用户数> scard app-name:dau(integer) 2
共同好友功能,共同喜好,或者可以引申到二度好友等,需要通过集合运算取交集的业务场景
Redis Hash是字符串字段和值之间的映射,因此适合存储对象属性的数据类型(例如,具有姓名、年龄等多个字段的用户信息)
HMSET user:1000 username antirez password P1pp0 age 34
HGETALL user:1000
HSET user:1000 password 12345
HGETALL user:1000
只几个字段(100个字段以内)的hash表占用非常少内存空间,因此一个小型 Redis 实例可以存储数百万个对象
每个散列最多可以存储 2^32 - 1 个字段值对(超过 40 亿个)
HMGET: HMGET key-name key [key …] 获取 HASH 中字段的值
HMSET: HMSET key-name key value [key value …] 设置 HASH 中字段的值
HDEL: HDEL key-name key [key …] 删除 HASH 中的键值对,返回找到并删除的key-value对数量
HLEN: HLEN key-name — 返回 HASH中key-value对的数量
HEXISTS: HEXISTS key-name key 判断Key是否存在于 HASH 中
HKEYS: HKEYS key-name 获取 HASH 中的Key
HVALS: HVALS key-name 获取 HASH 中的Value
HGETALL: HGETALL key-name 从 HASH 中获取所有键值对
HINCRBY: HINCRBY key-name key increment key对应的值整数增量
HINCRBYFLOAT: HINCRBYFLOAT key-name key increment key对应的值浮点增量
用户信息:应用程序在其用户信息存储使用 Redis Hashs,可以对所有用户字段使用单个哈希,例如姓名、手机机号、Email、密码等。
用户文章或帖子:社交平台利用 Redis Hashs 将存储用户的照片、帖子等其它单个用户的信息。 散列机制使能够非常快速地查找
存储多租户指标:多租户应用系统可以利用 Redis Hashs来记录和存储产品和销售指标,以确保每个租户之间的可靠分离
Redis Sorted Sets 与 Redis Sets 类似,是非重复集合。 不同之处在于Sorted Set 的每个成员都与一个分数相关联,用于保持 Sorted Set 从最小到最大分数的顺序。 虽然成员是唯一的,但score可能会重复。
使用sorted sets,可以非常快速的方式添加、删除或更新元素(时间与元素数量的对数成正比)。 由于元素是按顺序存储的,而不是事后排序的,因此还可以通过分数或排名(位置)以非常快速的方式获取范围,访问 Sorted Set 的中间部分也非常快,因此可以将 Sorted Sets 用作非重复元素的智能列表,可以在其中快速访问所需的一切:元素按顺序、快速存在测试、快速访问中间元素 。
ZADD:ZADD key-name score member [score member ...] 指定分数的成员添加到 ZSET
ZREM: ZREM key-name member [member ...] — 从 ZSET 中删除,返回被删除的数量
ZCARD: ZCARD key-name — 返回 ZSET 中的成员数
ZINCRBY: ZINCRBY key-name increment member — 增加 ZSET 中的成员
ZCOUNT: ZCOUNT key-name min max — 统计指定分数范围的成员数
ZRANK: ZRANK key-name member — 返回给定成员在 ZSET 中的排名
ZSCORE: ZSCORE key-name member — 返回 ZSET 中成员的分数
ZRANGE: ZRANGE key-name start stop [WITHSCORES] — 返回指定区间(start 和 stop 之间)的成员,可选参数WITHSCORES同时返回成员分数
ZREVRANK: ZREVRANK key-name member — 返回成员在 ZSET 中的位置,倒序排列
ZREVRANGE: ZREVRANGE key-name start stop [WITHSCORES] — 按排名从 ZSET中获取指定区间成员,倒序排列
ZRANGEBYSCORE: ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count] — 获取 min 和 max 之间的成员,顺序排列
ZREVRANGEBYSCORE: ZREVRANGEBYSCORE key max min [WITHSCORES] [LIMIT offset count] — 在 min 和 max 之间成员,倒序排列
ZREMRANGEBYRANK: ZREMRANGEBYRANK key-name start stop — 从 ZSET中删除排名在 start 和 stop 之间的成员
ZREMRANGEBYSCORE: ZREMRANGEBYSCORE key-name min max — 从 ZSET 中删除分数在 min 和 max 之间的成员
ZINTERSTORE: ZINTERSTORE dest-key key-count key [key …] [WEIGHTS weight [weight …]] [AGGREGATE SUM|MIN|MAX] — 交集
ZUNIONSTORE: ZUNIONSTORE dest-key key-count key [key …] [WEIGHTS weight [weight …]] [AGGREGATE SUM|MIN|MAX] — 并集
问答平台:Stack Overflow 和 Quora 等许多问答平台使用 Redis Sorted Sets 对每个提议的问题的最高投票答案进行排序,以确保最优质的内容列在页面顶部。
各种排行榜:利用 Redis Sorted Sets 来维护排行榜。使用 ZADD 对进行数据更新,使用 ZRANGE 轻松检索排名靠前的用户,还可以使用 ZRANK 返回其在列表中的排名。
任务调度服务:Redis Sorted Sets 是一个很好的任务调度服务工具,可以为每一个任务设置一个分数来决定队列中任务的优先级。
Geo Hashing:Redis GEO相关API 使用了 Geo Hash 技术的 Sorted Set,根据纬度和经度对位置进行索引,将多维数据转换为线性数据