Redis 设计与实现(第八章) -- 对象

概述

前面几张介绍了一些Redis的数据结构,比如SDS,集合,字典等,但是Redis并不会直接使用这些数据结构来实现键值对数据库,而是基于这些数据结构创建了一个对象系统,这些对象包括字符串对象,列表对象,哈希对象,集合对象和有序集合对象。每种对象都用到了一种或多种前面介绍的数据结构。

通过不同类型的对象,Redis在执行命令之前可以根据类型来判断一个对象是否可以执行给定的命令。

Redis对象还使用了基于引用计数的内存回收机制,当程序不再使用某个对象时,这个对象占用的内存就会释放。

Redis的对象带有访问时间记录信息,可以用于计算数据空键的空转时长,在服务器启用了maxmemory的情况下,空转时长比较大的键的可能会被服务器删除。

对象类型与编码

首先看一下对象的数据结构:

typedef struct redisObject {
    unsigned type:4;  //类型,主要包括:
    unsigned encoding:4;  //编码
    unsigned lru:REDIS_LRU_BITS; /* lru time (relative to server.lruclock) */
    int refcount;
    void *ptr;  //执行底层实现的数据结构的指针
} robj;

type有以下几种类型,在redis客户端可通过type key来查看对应key的类型

/* Object types */
#define REDIS_STRING 0
#define REDIS_LIST 1
#define REDIS_SET 2
#define REDIS_ZSET 3
#define REDIS_HASH 4

encoding类型有,在redis客户端可通过OBJECT ENCONDING key来查看对应key的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 */

接下来对五中类型的对象一一讲解下:

字符串对象

字符串对象的编码可以是int,raw或者embstr

如果一个字符串key保存的是整数值,而且这个整数可以通过long类型的表示,那么字符串对象就会将整数值保存在字符串对象结构的ptr里面(void *long),并将字符串的编码类型设置为int,如下:

redis 127.0.0.1:6379> set msg 10086
OK
redis 127.0.0.1:6379> object encoding msg
"int"

但是如果保存是字符串类型呢?

p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo }
span.s1 { }

127.0.0.1:6379[15]> set msg "hello"

OK

127.0.0.1:6379[15]> object encoding msg

"embstr"

127.0.0.1:6379[15]> set msg "helloasdfasdfsadfasdfasdfsadfsadfasdfasdfasdfsadf"

OK

127.0.0.1:6379[15]> object encoding msg

p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo }
span.s1 { }

"raw"

可以看到,分布使用了embstr和raw编码来保存字符串值,为什么会有这种区别呢?  还有什么情况下使用raw什么时候使用embstr呢?

当字符串长度超过32时,字符串对象采用raw来编码,如果小于32时,则使用embstr。那ptr怎么指向呢?  Redis里面的string通常都使用SDS来表示。所以数据结构如果:

embstr是专门用于保存短字符串的一种优化编码方式,和raw一样,都需要使用redisObject和sds结构来表示字符串对象,不同的是raw会调用两次内存分配来分别为两个数据结构分配空间大小,而embstr只需要调用一次来分配连续空间。

使用embstr的好处在哪呢?

1.刚才讲到的,内存分配次数从两次降到一次;

2.所以内存释放也只需要调用一次了;

3.连续内存能够更好的利用缓存优势。

embstr的结构如下:

最后浮点数也是以字符串的方式来保存的,可以看下:

p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo }
span.s1 { }

127.0.0.1:6379[15]> set msg 3.14159

OK

127.0.0.1:6379[15]> object encoding msg

"embstr"

编码是可以转换的,比如msg刚开始是int,后来追加了一条数据,编码就会改变

p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo }
span.s1 { }

127.0.0.1:6379[15]> set msg 3

OK

127.0.0.1:6379[15]> object encoding msg

"int"

127.0.0.1:6379[15]> append msg abc

(integer) 4

127.0.0.1:6379[15]> object encoding msg

"raw"

127.0.0.1:6379[15]> get msg

"3abc"

列表对象

列表对象的编码方式可以是ziplist或者linkedlist。

如果是ziplist,那么对象应该为下图,ptr执行ziplist结构

如果是linkedlist,对象结构如下,stringObject代表是SDS,这里是为了简化一下

什么时候使用ziplist,什么时候使用linkedlist?(版本仅针对3.0.x的,3.2.x版本上目前使用的都是quicklist)

当列表对象同时满足下面两个条件时,使用ziplist,否则使用linkedlist:

1.列表对象保存的所有字符串长度都小于64;

2.列表对象保存的元素个数小于512;

哈希对象

hash对象的编码方式由ziplist和hashtable

使用ziplist保存结构如下图

使用hashtable保存结构如下,其中stringObject也是为SDS结构

什么时候使用的ziplist,什么时候使用hashtable?

同时满足下面两个条件时,使用ziplist,否则使用hashtable:

1.hash的键值对的长度均小于64;

2.hash对象的保存的键值对数量小于512;

p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo }
span.s1 { }

127.0.0.1:6379[15]> object encoding book

"ziplist"

127.0.0.1:6379[15]> hset book content "C++asdfasdkfjlasdjfl;sajdfljsadlkfjsakldfjlksadjfklsdjflksajdflkasjdlfkjasdl;fjasl;dfjl;sadfjlsadkjflsadf"

(integer) 1

127.0.0.1:6379[15]> object encoding book

"hashtable"

集合对象

集合对象的编码方式由intset和hashtable

intset和hashtable表示的结构分布如下:

什么时候用hashtable,什么时候用intset?

同时满足下面两个条件时,使用intset,否则使用hashtable:

1.所有对象元素为整数值;

2.集合对象的元素数量不超过512个;

p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo }
span.s1 { }

127.0.0.1:6379[15]> sadd keyset 1 2 3

(integer) 3

127.0.0.1:6379[15]> object encoding keyset

"intset"

127.0.0.1:6379[15]> sadd keyset "aa"

(integer) 1

127.0.0.1:6379[15]> object encoding keyset

"hashtable"

有序集合对象

有序集合对象的编码可以是ziplist和skiplist

ziplist编码,压缩列表中的元素按照score从小到大排序,结构如下:

skiplist编码方式使用的zset结构作为底层实现,一个zset结构同时包含了一个skiplist和一个hashtable

typedef struct zset {
    dict *dict;
    zskiplist *zsl;
} zset;

zsl跳跃表按照分值从小到大保存了所有元素,每个跳跃点都保存了一个集合元素,跳跃节点的object属性保存了元素的成员,score属性保存了分值,通过跳跃表程序可以对有序集合进行范围操作,比如zrange等。

为什么还需要给dict呢?

dict为有序集合创建了一份从成员到分值的映射,

时间: 2024-10-01 22:11:17

Redis 设计与实现(第八章) -- 对象的相关文章

《Redis设计与实现》读书笔记

花了几天时间把<Redis设计与实现>读完了,把一些心得记下来给大家分享. 第2章 简单动态字符串 redis里面的字符串对象都采用SDS结构实现.SDS有别于C风格的字符数组和java的String(定长).这种结构更像C++的String或者java的ArrayList<Character>.长度动态可变. redis的所有键值及字符串字面量都采用这种结构. 这一章节花了十几页去讲这个SDS结构,感觉全是废话,有时间建议去看ArrayList的源码. 第3章 链表 redis的

Redis设计与实现(一~五整合版)【搬运】

Redis设计与实现(一~五整合版) by @飘过的小牛 一 前言 项目中用到了redis,但用到的都是最最基本的功能,比如简单的slave机制,数据结构只使用了字符串.但是一直听说redis是一个很牛的开源项目,很多公司都在用.于是我就比较奇怪,这玩意不就和 memcache 差不多吗?仅仅是因为memcache是内存级别的,没有持久化功能.而redis支持持久化?难道这就是它的必杀技? 带着这个疑问,我在网上搜了一圈.发现有个叫做huangz的程序员针对redis写了一本书叫做<redis设

《Redis设计与实现》

<Redis设计与实现> 基本信息 作者: 黄健宏 丛书名: 数据库技术丛书 出版社:机械工业出版社 ISBN:9787111464747 上架时间:2014-6-3 出版日期:2014 年6月 开本:16开 页码:1 版次:1-1 所属分类:计算机 > 数据库 > 数据库理论 > 综合 更多关于>>> <Redis设计与实现>   内容简介 书籍 计算机书籍 <redis设计与实现>全面而完整地讲解了redis的内部机制与实现方式,

redis学习笔记(9)---对象robject

robject 之前对redis基本的数据结构分别进行了简单的介绍,包括字符串.链表.哈希表.整数集合.压缩列表.压缩字典等,但是redis并不是直接使用这些数据结构来实现key-value对数据库的,而是基于这些数据结构为每一个对象创建一个对象robject.robject对象再根据数据类型,来选择合适的底层数据结构来存储数据. robject的定义如下: typedef struct redisObject { unsigned type:4; unsigned encoding:4; un

Redis设计与实现 pdf扫描版【65M】高清下载

<Redis设计与实现>全面而完整地讲解了Redis的内部机制与实现方式,对Redis的大多数单机功能以及所有多机功能的实现原理进行了介绍,展示了这些功能的核心数据结构以及关键的算法思想,图示丰富,描述清晰,并给出大量参考信息.通过阅读本书,读者可以快速.有效地了解Redis的内部构造以及运作机制,更好.更高效地使用Redis. <Redis设计与实现>主要分为四大部分.第一部分“数据结构与对象”介绍了Redis中的各种对象及其数据结构,并说明这些数据结构如何影响对象的功能和性能.

设计一个不强引用对象的单例字典

大家都知道,使用NSDictionary存储对象的时候会强引用对象,导致被存储对象的引用计数+1,有时候,我们想用单例来存储对象,但又不希望强引用存储的对象,这该怎么实现呢? 在这里,我们可以使用NSMapTable来实现这个功能. 我直接给出源码: WeakDictionary.h   +   WeakDictionary.m // // WeakDictionary.h // 弱引用字典 // // http://www.cnblogs.com/YouXianMing/ // Copyrig

Java-集合=第五题 (Map)设计Account 对象如下: private long id; private double balance; private String password; 要求完善设计,使得该Account 对象能够自动分配id。 给定一个List 如下: List list = new ArrayList(); list.add(new A

第五题 (Map)设计Account 对象如下: private long id; private double balance; private String password; 要求完善设计,使得该Account 对象能够自动分配id. 给定一个List 如下: List list = new ArrayList(); list.add(new Account(10.00, “1234”)); list.add(new Account(15.00, “5678”)); list.add(ne

《Redis设计与实现剖析- 前言》

现如今Redis已经不折不扣的成为缓存技术中的主流中间件,基本上大型的系统都会选择Redis缓存来提升系统性能. 由于在目前开发项目中也有使用Redis,在使用以及了解Redis的过程中被Redis优秀的设计与实现所吸引,Redis本身是基于C语言实现的高级应用,Redis内部也大量使用了经典数据结构(数组,链表,Hash表,队列,堆,跳跃表),刚好最近在巩固加深数据结构与算法这方面的基本功,所以就萌出了通过剖析Redis内部实现的方式,来复习巩固C语言的知识及其高级应用和经典数据结构的原理及其

如何使用redis设计关系数据库

目录 redis设计关系数据库 前言 设计用户信息表结构 hash存储记录 set存储id 图示 索引/查询: 1.select 查询所有记录 : 类似sql的select from table_name 2.根据主键查询记录 3.其他列索引 c++ 实现 小结 redis设计关系数据库 前言 最近需要一张用户信息表,因为数据量并不大,想先放在内存中,等需求变更了,再移到磁盘上,或者往mysql塞,那么问题来了,怎么用redis的数据类型设计一个关系数据库呢. redis只有key-value这

Redis设计原理

1.简介 Redis中的每个Key-Value在内存中都会被划分成DictEntry.RedisObject以及具体对象,其中DictEntry又分别包含指向Key和Value的指针(以RedisObject的形式)以及指向下一个DictEntry的指针. Key固定是字符串,因此使用字符串对象来进行表示,Value可以是字符串.列表.哈希.集合.有序集合对象中的任意一种. Redis提供了五种对象,每种对象都需要使用RedisObject进行表示. Redis使用redisObject结构来表