定义:
Redis 是 C 语言开发的一个开源的(遵从 BSD 协议)高性能键值对(key-value)的内存数据库,可以用作数据库、缓存、消息中间件等。它是一种 NoSQL(not-only sql,泛指非关系型数据库)的数据库。
特点:
性能优秀,数据在内存中,读写速度非常快,支持并发 10W QPS。单进程单线程,是线程安全的,采用 IO 多路复用机制。丰富的数据类型,支持字符串(strings)、散列(hashes)、列表(lists)、集合(sets)、有序集合(sorted sets)等。支持数据持久化。可以将内存中数据保存在磁盘中,重启时加载。主从复制,哨兵,高可用。可以用作分布式锁。可以作为消息中间件使用,支持发布订阅
Redis内部内存对象管理 !
Redis 内部使用一个 redisObject 对象来表示所有的 key 和 value,结构如下
typedef struct redisObject{
//对象的数据类型(String/Hash/List/Set/Zset)
unsigned type:4;
//表示redisObject对象的底层编码实现,主要有简单动态字符串,链表,字典,跳跃表,整数集合以及压缩列表,
unsigned encoding:4;
//指向底层数据结构的指针
void *ptr;
//引用计数器,初始值为1,被引用一次+1,反之则-1,为0则释放空间,可以用OBJECT REFCOUNT查看引用情况
int refCount;
//最后一次访问该对象的时间,可以通过Object idletime查看当前时间距离该键的lru的时间
unsigned lru:
}
在redisObject内部,type 表示一个 value 对象的具体数据类型(例如,type=string,则表示存储的是string类型的对象),encoding 是不同数据类型在 Redis 内部的存储方式
- String
String是Reids的基本数据类型,value可以存储任何类型的数据,比如 jpg 图片或者序列化的对象。String 类型的单键的value最大能存储 512M. 常用命令,set/get
字符串对象的值底层都是由简单动态字符串实现的
一个 简单动态字符串(SDS) 示例如下:
数据结构如下:
struct sdshdr{
//记录buf数组中已使用字节的长度
int len;
//记录buf数组中剩余空间的长度
int free;
//字节数组,用于存储字符串
char buf[];
};
在SDS拼接发生以后,如果此时的len小于1MB则它会多分配和len大小相同的未使用空间,用free表示,如果大于1MB,则会分配1MB的为使用空间
应用场景:
1.缓存: 经典使用场景,把常用信息,字符串,图片或者视频等信息放到redis中,redis作为缓存层,mysql做持久化层,降低mysql的读写压力。
2.计数器:redis是单线程模型,一个命令执行完才会执行下一个,同时数据可以一步落地到其他的数据源。
3.session:常见方案spring session + redis实现session共享,
- Hash
Hash是一个键值(key-value)的集合
当哈希对象保存的键值对数量小于 512,并且所有键值对的长度都小于 64 字节时,使用压缩列表存储;否则使用 hashtable 存储
使用压缩列表的时候,图示如下:
压缩列表(ziplist)的结构体
struct ziplist<T> {
int32 zlbytes; // 整个压缩列表占用字节数
int32 zltail_offset; // 最后一个元素距离压缩列表起始位置的偏移量,用于快速定位到最后一个节点
int16 zllength; // 元素个数
T[] entries; // 元素内容列表,挨个挨个紧凑存储
int8 zlend; // 标志压缩列表的结束,值恒为 0xFF
}
图示如下:
节点entry结构体:
struct entry {
int<var> prevlen; // 前一个 entry 的字节长度
int<var> encoding; // 元素类型编码
optional byte[] content; // 元素内容
}
图示如下:
Hashtable 对应的示意图如下:
应用场景:
缓存: 能直观,相比string更节省空间,的维护缓存信息,如用户信息,视频信息等。
常用命令:hget,hset,hgetall 等。
- List
列表是简单的字符串列表,按照插入顺序排序。可以添加一个元素到列表的头部(左边)或者尾部(右边),当列表的长度小于 512,并且所有元素的长度都小于 64 字节时,使用压缩列表(Hash中有描述)存储;否则使用 linkedlist 存储。
应用场景:List 应用场景非常多,也是 Redis 最重要的数据结构之一,比如 关注列表,粉丝列表等/可以用来当消息队列用
链表对应的示意图如下:
常用命令:lpush、rpush、lpop、rpop、lrange(获取列表片段)等。
- Set
Set是String 类型的无序集合。集合是通过 hashtable 实现的。Set 中的元素是没有顺序的,而且是没有重复的
intset(整数集合)主要是为节省内存而设计的内存结构,它的优点就是节省内存,但缺点就是比其他结构要消耗更多的时间,所以 Redis 在数据量小的时候使用整数集合存储。
当集合的长度小于 512,并且所有元素都是整数时,使用整数集合存储;否则使用 hashtable 存储。
intset结构如下
typedef struct intset{
//编码方式
uint32_t encoding;
//元素数量
uint32_t length;
//存储元素的数组
int8_t contents[];
}
intset 编码 使用 Intset(数组) 作为底层实现。(以此来保证集合内唯一的元素) 图示如下:
hashtable结构如下:
typedef struct dictht{
//哈希表数组
dictEntry **table;
//哈希表大小
unsigned long size;
//哈希表掩码,总是等于size-1,存储时计算索引值
unsigned long sizemask;
//已有元素数量
unsigned long used;
}
使用字典实现,字典的每个键都是一个字符串对象(包含了一个集合元素)(以此来保证集合内唯一的元素) 图示如下:
应用场景:
1.标签(tag),给用户添加标签,或者用户给消息添加标签,这样有同一标签或者类似标签的可以给推荐关注的事或者关注的人。
2.点赞,或点踩,收藏等,可以放到set中实现
常用命令:sdd、spop、smembers、sunion 等。
- Zset ①
是 String 类型元素的集合 有序 有压缩列表ziplist和跳跃链表skiplist
有序集合和集合有着必然的联系,保留了集合不能有重复成员的特性,区别是,有序集合中的元素是可以排序的,它给每个元素设置一个分数,作为排序的依据。
(有序集合中的元素不可以重复,但是score 分数 可以重复,就和一个班里的同学学号不能重复,但考试成绩可以相同)
跳跃表的结构如下:
typedef struct zskiplist{
//跳跃表的头结点
zskiplistNode header;
//尾节点
zskiplistNode tail;
//跳跃表中层数最大的节点的层数(不包括头结点)
unsigned long level;
//跳跃表长度(不包括头节点)
unsigned int length;
节点结构:
typedef struct zskiplistNode{
//后退指针
struct zskiplistNode *backward;
//分值
double score;
//成员对象
robj *obj;
//层
struct zskiplistLevel{
//前进指针
struct zskiplistNode *forward;
//跨度
unsigned int span;
}level[];
};
图示如下:
应用场景:
排行榜:有序集合经典使用场景。例如小说视频等网站需要对用户上传的小说视频做排行榜,榜单可以按照用户关注数,更新时间,字数等打分,做排行。
常用命令:zadd、zrange、zrem、zcard 等。
注: ①详解见链接:https://zsr.github.io/2017/07/03/redis-zset%E5%86%85%E9%83%A8%E5%AE%9E%E7%8E%B0/
本文参考: 1,Redis 学习笔记(篇五):对象(RedisObject)https://www.cnblogs.com/wind-snow/p/11172832.html
2,搞懂这些Redis知识点,吊打面试官!https://baijiahao.baidu.com/s?id=1660009541007805174&wfr=spider&for=pc
来源:oschina
链接:https://my.oschina.net/u/4517769/blog/4335139