引言

Redis中每个键值对都是由对象组成:

  • 键总是一个字符串对象(string)
  • 值可以是字符串对象(string)、列表对象(list)、哈希对象(hash)、集合对象(set)、有序集合对象(zset)。

介绍

redis官方网站中对其数据类型的简单介绍:

An introduction to Redis data types and abstractions

摘抄一段关于redis key的介绍:

Redis keys

Redis keys are binary safe, this means that you can use any binary sequence as a key, from a string like "foo" to the content of a JPEG file. The empty string is also a valid key.

A few other rules about keys:

  • Very long keys are not a good idea. For instance a key of 1024 bytes is a bad idea not only memory-wise, but also because the lookup of the key in the dataset may require several costly key-comparisons. Even when the task at hand is to match the existence of a large value, hashing it (for example with SHA1) is a better idea, especially from the perspective of memory and bandwidth.
  • Very short keys are often not a good idea. There is little point in writing "u1000flw" as a key if you can instead write "user:1000:followers". The latter is more readable and the added space is minor compared to the space used by the key object itself and the value object. While short keys will obviously consume a bit less memory, your job is to find the right balance.
  • Try to stick with a schema. For instance "object-type:id" is a good idea, as in "user:1000". Dots or dashes are often used for multi-word fields, as in "commentreply.to" or "commentreply-to".
  • The maximum allowed key size is 512 MB.

源码中的对象数据结构

在redis源码中,对象的数据结构定义在redis.h文件中

#define REDIS_LRU_BITS 24
typedef struct redisObject {
// 类型
unsigned type:4;
// 编码
unsigned encoding:4;
// 对象最后一次被访问的时间
unsigned lru:REDIS_LRU_BITS; /* lru time (relative to server.lruclock) */
// 引用计数
int refcount;
// 指向实际值的指针
void *ptr;
}

下面分别介绍以下对象中定义的属性定义:

  • type

    type是指对象的5中基本类型:string、hash、list、set、zset。
// 对象类型
#define REDIS_STRING 0
#define REDIS_LIST 1
#define REDIS_SET 2
#define REDIS_ZSET 3
#define REDIS_HASH 4
  • encoding

    表示对象的底层编码实现,有简单动态字符串、链表、字典、跳跃表、整数集合、压缩列表。
// 对象编码
#define REDIS_ENCODING_RAW 0 /* Raw representation */
#define REDIS_ENCODING_INT 1 /* Encoded as integer */
#define REDIS_ENCODING_HT 2 /* Encoded as hash table */
#define REDIS_ENCODING_ZIPMAP 3 /* Encoded as zipmap */
#define REDIS_ENCODING_LINKEDLIST 4 /* Encoded as regular linked list */
#define REDIS_ENCODING_ZIPLIST 5 /* Encoded as ziplist */
#define REDIS_ENCODING_INTSET 6 /* Encoded as intset */
#define REDIS_ENCODING_SKIPLIST 7 /* Encoded as skiplist */
#define REDIS_ENCODING_EMBSTR 8 /* Embedded sds string encoding */
  • lru

    最后一次访问对象的时间,用来处理键的过期策略。
  • refcount

    对象引用的指针,不同key的相同对象引用同一个对象,减少内存分配即对象共享。也用于内存释放。
  • *ptr

    指向底层数据结构的指针。

基本类型type与底层数据结构的对应关系

盗取一张图(懒的画图):



各种对象的创建可以参考redis源码中的object.c文件,如:

根据传入的整数型,创建一个字符串:

/*
* 根据传入的整数值,创建一个字符串对象
*
* 这个字符串的对象保存的可以是 INT 编码的 long 值,
* 也可以是 RAW 编码的、被转换成字符串的 long long 值。
*/
robj *createStringObjectFromLongLong(long long value) { robj *o; // value 的大小符合 REDIS 共享整数的范围
// 那么返回一个共享对象
if (value >= 0 && value < REDIS_SHARED_INTEGERS) {
incrRefCount(shared.integers[value]);
o = shared.integers[value]; // 不符合共享范围,创建一个新的整数对象
} else {
// 值可以用 long 类型保存,
// 创建一个 REDIS_ENCODING_INT 编码的字符串对象
if (value >= LONG_MIN && value <= LONG_MAX) {
o = createObject(REDIS_STRING, NULL);
o->encoding = REDIS_ENCODING_INT;
o->ptr = (void*)((long)value); // 值不能用 long 类型保存(long long 类型),将值转换为字符串,
// 并创建一个 REDIS_ENCODING_RAW 的字符串对象来保存值
} else {
o = createObject(REDIS_STRING,sdsfromlonglong(value));
}
} return o;
}

线面简单看看基本类型type对应的底层数据结构:

string

sting在redis底层对应三种编码方式,两种数据结构。

如果一个字符串内容可以转成long,那么编码方式为int,底层数据结构为int.

如果普通字符串对象的长度小于39字节,就用embstr对象。否则用的raw对象,底层数据结构为简单动态字符串。

/* Create a string object with EMBSTR encoding if it is smaller than
* REIDS_ENCODING_EMBSTR_SIZE_LIMIT, otherwise the RAW encoding is
* used.
*
* The current limit of 39 is chosen so that the biggest string object
* we allocate as EMBSTR will still fit into the 64 byte arena of jemalloc. */
#define REDIS_ENCODING_EMBSTR_SIZE_LIMIT 39
robj *createStringObject(char *ptr, size_t len) {
if (len <= REDIS_ENCODING_EMBSTR_SIZE_LIMIT)
return createEmbeddedStringObject(ptr,len);
else
return createRawStringObject(ptr,len);
}
SDS

简单动态字符串(simple dynamic string)的数据结构为:

/*
* 保存字符串对象的结构
*/
struct sdshdr {
// buf 中已占用空间的长度
int len;
// buf 中剩余可用空间的长度
int free;
// 数据空间
char buf[];
};

list

有两种编码实现,链表linkedlist和压缩列表ziplist,当当list元素少且元素内容长度不大时,使用ziplist,否则使用linkedlist.

/* Use a real list when there are too many entries
*
* 根据节点数,创建对象的编码
*/
if (len > server.list_max_ziplist_entries) {
o = createListObject();
} else {
o = createZiplistObject();
}
/*
* 创建一个 LINKEDLIST 编码的列表对象
*/
robj *createListObject(void) { list *l = listCreate(); robj *o = createObject(REDIS_LIST,l); listSetFreeMethod(l,decrRefCountVoid); o->encoding = REDIS_ENCODING_LINKEDLIST; return o;
} /*
* 创建一个 ZIPLIST 编码的列表对象
*/
robj *createZiplistObject(void) { unsigned char *zl = ziplistNew(); robj *o = createObject(REDIS_LIST,zl); o->encoding = REDIS_ENCODING_ZIPLIST; return o;
}
链表linkedlist

其底层数据结构list位于adlist.h中:

/*
* 双端链表节点
*/
typedef struct listNode {
// 前置节点
struct listNode *prev;
// 后置节点
struct listNode *next;
// 节点的值
void *value;
} listNode; /*
* 双端链表结构
*/
typedef struct list {
// 表头节点
listNode *head;
// 表尾节点
listNode *tail;
// 节点值复制函数
void *(*dup)(void *ptr);
// 节点值释放函数
void (*free)(void *ptr);
// 节点值对比函数
int (*match)(void *ptr, void *key);
// 链表所包含的节点数量
unsigned long len;
} list;
压缩列表ziplist

类似数组,但是每个节点存储的数据大小不同,节点上有length属性。

hash

有两种数据结构实现,压缩列表ziplist和字典dict.

字典dict

相当于java中的HashMap。解决hash冲突使用的是链表法,好像没有上升到红黑树。

set

如果是整数类型,直接使用整数集合intset,如果不是,就用字典,和java的set一样。

zset

有序的set,元素个数少且不大,就用压缩列表ziplist,否则就用跳跃表skiplist.

跳跃表skiplist

跳跃表(skiplist)是一种有序数据结构,它通过在每个节点中维持多个指向其他节点的指针,从而达到快速访问节点的目的。

跳跃表是一种随机化的数据,跳跃表以有序的方式在层次化的链表中保存元素。

定义位于redis.h中。


/* ZSETs use a specialized version of Skiplists */
/*
* 跳跃表节点
*/
typedef struct zskiplistNode { // 成员对象
robj *obj; // 分值
double score; // 后退指针
struct zskiplistNode *backward; // 层
struct zskiplistLevel { // 前进指针
struct zskiplistNode *forward; // 跨度
unsigned int span; } level[]; } zskiplistNode; /*
* 跳跃表
*/
typedef struct zskiplist { // 表头节点和表尾节点
struct zskiplistNode *header, *tail; // 表中节点的数量
unsigned long length; // 表中层数最大的节点的层数
int level; } zskiplist;

参考文档

深入浅出Redis-redis底层数据结构(上)

深入浅出Redis-redis底层数据结构(下)

Redis基本类型及其数据结构

Redis的五种对象类型及其底层实现

Redis-基本数据类型与内部存储结构

redis的五种基本数据类型及其内部实现

《Redis设计与实现》黄健宏

深入了解Redis【二】对象及数据结构综述的更多相关文章

  1. Redis 的几种数据结构&五种数据类型对象

    先看几种数据结构 通过分析底层的数据结构,学习如何根据场景选型和设计 1,简单动态字符串 redis使用的字符串SDS有别于C语言中的字符串 a, 结构 free字段为已分配但未使用的空间 len为已 ...

  2. 一文读懂Redis常见对象类型的底层数据结构

    Redis是一个基于内存中的数据结构存储系统,可以用作数据库.缓存和消息中间件.Redis支持五种常见对象类型:字符串(String).哈希(Hash).列表(List).集合(Set)以及有序集合( ...

  3. 【Redis破障之路】二:Redis安装和基本数据结构

    1.安装Redis Redis6.0在2020年已经发布,所以我们安装Redis3.0. 1.1.在Linux上安装Redis 我们在CentOS上安装Redis.常见的的有三种安装方式: yum/a ...

  4. Redis系列(二):Redis的5种数据结构及其常用命令

    上一篇博客,我们讲解了什么是Redis以及在Windows和Linux环境下安装Redis的方法, 没看过的同学可以点击以下链接查看: Redis系列(一):Redis简介及环境安装. 本篇博客我们来 ...

  5. 详细介绍Redis的几种数据结构以及使用注意事项(转)

    原文:详细介绍Redis的几种数据结构以及使用注意事项 1. Overview 1.1 资料 <The Little Redis Book>,最好的入门小册子,可以先于一切文档之前看,免费 ...

  6. 深入理解MVC C#+HtmlAgilityPack+Dapper走一波爬虫 StackExchange.Redis 二次封装 C# WPF 用MediaElement控件实现视频循环播放 net 异步与同步

    深入理解MVC   MVC无人不知,可很多程序员对MVC的概念的理解似乎有误,换言之他们一直在错用MVC,尽管即使如此软件也能被写出来,然而软件内部代码的组织方式却是不科学的,这会影响到软件的可维护性 ...

  7. Redis第二讲【Redis基本命令和五大数据结构】

    [二.Redis基本命令和五大数据结构] redis的基础知识和命令 redis 是一个单进程(包装epoll函数来对读写事件进行相应) 默认有16个数据库,初始使用的数据库为0号库 默认端口为637 ...

  8. Redis中5种数据结构的使用场景介绍

    转载于:http://www.itxuexiwang.com/a/shujukujishu/redis/2016/0216/108.html?1455861435 一.redis 数据结构使用场景 原 ...

  9. redis中5种数据结构的使用

    一.redis 数据结构使用场景 原来看过 redisbook 这本书,对 redis 的基本功能都已经熟悉了,从上周开始看 redis 的源码.目前目标是吃透 redis 的数据结构.我们都知道,在 ...

随机推荐

  1. 嵌入式Linux串口编程简介

    文章目录 简介 用到的API函数 代码 简介 嵌入式Linux下串口编程与Linux系统下的编程没有什么区别,系统API都是一样的.嵌入式设备中串口编程是很常用的,比如会对接一些传感器模块,这些模块大 ...

  2. Python人脸识别 + 手机推送,老板来了你就会收到短信提示

  3. SpringBoot整合Shiro自定义Redis存储

    Shiro Shiro 主要分为 安全认证 和 接口授权 两个部分,其中的核心组件为 Subject. SecurityManager. Realms,公共部分 Shiro 都已经为我们封装好了,我们 ...

  4. Nginx一个server主机上80、433,http、https共存

    如果一站点既要80 http访问,又要443https访问. 要让https和http并存,不能在配置文件中使用ssl on,配置listen 443 ssl; 实例 server { listen ...

  5. Spring Boot 项目几种启动方式

    Spring Boot 项目几种启动方式 1. 使用 main 启动 jar xxxx.jar 2. 使用 mvn 启动 mvn spring-boot:run 3. 使用 Spring Boot c ...

  6. 申请支付宝app支付签约综合评分不足,拒绝不通过快速强开通支付宝App支付强开,强开支付宝App支付产品权限!

    一.如何开通支付宝App支付 正常来说,按照官方的指引要求填写相关资料,即可开通支付宝手机网站支付.但是,更多的时候我们的申请都会碰到一些阻力,常见的阻力就是“系统综合评估签约条件不满足,谢谢您的支持 ...

  7. java web应用启动报错:Caused by: java.lang.ClassNotFoundException: ServletContext

    ServletContext是个接口,不同的WEB容器(tomcat, jboss等)都有各自的实现. 一般是缺少servlet-api.jar包 在Java Build Path的Libraries ...

  8. Vue CLI3 移动端适配 【px2rem 或 postcss-plugin-px2rem】

    Vue CLI3 移动端适配 [px2rem 或 postcss-plugin-px2rem] 今天,我们使用Vue CLI3 做一个移动端适配 . 前言 首先确定你的项目是Vue CLI3版本以上的 ...

  9. eclipse中 sec/test/resource 文件夹消失怎么设置?

    右键改包--->build path --->Configure bulid path 按 add Folder 搞定.....

  10. linux驱动之内核空间几种长延时的实现策略的优劣评估

    本文转载自http://blog.chinaunix.net/uid-23769728-id-3084737.html 这里所谓的长延时,是指其实现时间延时的粒度可以在HZ这一水准上.<深入Li ...