Redis
一、概述
-
Redis 是速度非常快的非关系型(NoSQL)内存键值数据库,可以存储键和五种不同类型的值之间的映射。
-
键的类型只能为字符串,值支持五种数据类型:字符串、列表、集合、散列表、有序集合。
-
Redis 支持很多特性,例如将内存中的数据持久化到硬盘中,使用复制来扩展读性能,使用分片来扩展写性能。
-
只要作为缓存,存储经常访问的数据或者经常更改的数据。
二、数据类型
数据类型 | 存储值 | 操作 | 应用 |
---|---|---|---|
STRING | 字符串、整数 或者浮点 数 | 字符串或字符串一部分执行操、整数或者浮点数执行自增或者自减操作(set,get,decr,incr,mget) | 常规计数、微博数、粉丝数等 |
LIST | 列表 | 两端压入或者弹出元素、对单个或者多个元素进行修剪、保留以一个范围内的元素(lpush,rpush,lpop,rpop,lrange) | 双向链表、关注列表、粉丝列表、消息列;lrange可以实现分页查询(微博下拉分页) |
SET | 无序集合 | 添加、获取、移除单个元素、检查一个元素是否存在于集、计算交集、并集、差集、从集合里面随机获取元素。去重(sadd,spop,smembers,sunion) | 共同关注、共同粉丝、共同喜好等功能、list排重功能 |
HASH | 键值对的无序散列表 | 添加、获取、移除单个键值对、获取所有键值对检查某个键是否存在(hget,hset,hgetall) | 适合存储对象、用户信息、商品信息(购物车);后期可以仅仅修改对象中某个字段的值 |
ZSET | 有序集合 | 添加、获取、删除元素、根据分值范围或者成员来获取、计算一个键的排名(zadd,zrange,zrem,zcard)增加了一个权重参数score,可以依据其进行有序排列 | 实时排行、礼物排行榜、在线用户列表 |
zset 的数据结构
zset底层的存储结构包括ziplist或skiplist,在同时满足以下两个条件的时候使用ziplist,其他时候使用skiplist,两个条件如下:
- 有序集合保存的元素数量小于128个
- 有序集合保存的所有元素的长度小于64字节
当ziplist作为zset的底层存储结构时候,每个集合元素使用两个紧挨在一起的压缩列表节点来保存,第一个节点保存元素的成员,第二个元素保存元素的分值。
当skiplist作为zset的底层存储结构的时候,使用skiplist按序保存元素及分值,使用dict来保存元素和分值的映射关系。
ziplist 结构
ziplist作为zset的存储结构时,格式如下图,紧挨着的是元素memeber和分值socore,整体数据是有序格式。
skiplist数据结构
skiplist作为zset的存储结构,整体存储结构如下图,核心点主要是包括一个dict对象和一个skiplist对象。dict保存key/value,key为元素,value为分值;skiplist保存的有序的元素列表,每个元素包括元素和分值。两种数据结构下的元素指向相同的位置。
三、Redis存储结构
3.1 字典
- redis的存储结构从外层往内层依次是redisDb、dict、dictht、dictEntry。
- redis的Db默认情况下有15个,每个redisDb内部包含一个dict的数据结构。
- redis的dict内部包含dictht的数组,数组个数为2,主要用于hash扩容使用。
- dictht内部包含dictEntry的数组,可以理解就是hash的桶,然后如果冲突通过挂链法解决,冲突的时候将欣新节点添加到表头位置。
3.1.1 字典的数据结构
typedef struct dict {
// 特定类型的函数,针对不同类型的键值对创建多态字典而设置的
// 指向dictType结构的指针,每一个dictType结构保存了一组用于操作特定类型键值对的函数
dictType *type;
// 私有数据,保存了需要传给那些类型特定函数的可选参数
void *privdata;
// 哈希表,有两个哈希表,进行扩容的时候使用
dictht ht[2];
// rehash 索引,当rehash不在进行时,值为 -1
long rehashidx; /* rehashing not in progress if rehashidx == -1 */
unsigned long iterators; /* number of iterators currently running */
} dict;
/* This is our hash table structure. Every dictionary has two of this as we
* implement incremental rehashing, for the old to the new table.
* 使用渐进的 rehash 操作,将旧的键值对 rehash 到 另一个 dictht 上面
*/
typedef struct dictht {
dictEntry **table; // 哈希表数组,数组中没用元素有指向一个 dictEntry结构的指针