- 浏览: 725948 次
- 性别:
- 来自: 上海
文章分类
- 全部博客 (168)
- CSS (1)
- Eclipse (4)
- English (1)
- ExtJS (1)
- Git (3)
- Grails (3)
- Groovy (2)
- Hadoop (7)
- HTML5 (2)
- JavaScript (3)
- Maven (2)
- MQ (5)
- MyBatis (3)
- NodeJS (6)
- NoSQL (4)
- Oracle (6)
- PDF (3)
- Python (9)
- Redis (17)
- Tomcat (2)
- Unix (8)
- Web Service (6)
- 安全 (1)
- 电子书 (6)
- 工具 (1)
- 其他 (21)
- 人工智能 (2)
- 视觉 (2)
- 算法 (6)
- 图表 (1)
- 网络 (13)
- 性能 (5)
- 游戏 (9)
- 字节码 (3)
- 机器学习 (1)
最新评论
-
lijunwyf:
cevin15 写道可以看下这个开源软件,https://gi ...
用markdown2html把md转换成html -
cevin15:
可以看下这个开源软件,https://github.com/c ...
用markdown2html把md转换成html -
Raina:
运行不了呢……提示错误无法加载主类Baiduwallpaper ...
用Java更换Windows桌面壁纸 -
苏城细雨沐秋风:
我把解码的jar添加到类路径后,mp3可以播放,但是flac和 ...
java播放mp3/ogg/ape/flac音乐 -
peishuai1987:
请问楼主现在怎么样了,读了很多源码吗,比如mybatis、sp ...
mybatis源码阅读心得
本篇我们研究redis里的对象。
1. 概述
redis有5种对象类型
它们的底层数据结构都是由前面6篇文章提到的所实现的。
请先参考如下文章,并结合源码阅读体会。
http://www.redisbook.com/en/latest/toc.html里的8.对象
http://origin.redisbook.com/en/latest/里的第三部分:Redis 数据类型
2. 一种类型,多种实现
redis使得一种对象类型的实现可以由不同的数据结构来切换,从而同时兼顾性能(CPU)和内存。而且当某个底层数据结构有性能问题时,可以替换掉。当然这种替换的实现还是没有java这么优雅的,redis里面只能靠if else来切换不同的数据结构。
3. zipmap
以前小的哈希表使用zipmap实现的,而redis从2.6版本开始,改用ziplist来实现,因为zipmap被爆有性能问题。
所以现在zipmap已经没用了,如果读者感兴趣的话,代码就在zipmap.c里面。我就不关注了。
4. redisObject的定义
redis.h中定义了redisObject
这里解释一下里面的一个奇怪的语法:(冒号)
该语法的定义叫位字段(bit field)。
4.1 位字段(bit field)
type:4的意思是定义了一个属性叫type,占4个比特,而前面的unsigned等价于unsigned int。
下面又定义了一个属性叫encoding,占4个比特。以及lru,占24个比特。
这样这3个属性合在一起,占32个比特,也就是4个字节。
剩下的refcount和ptr自不必说,各占4个字节。
所以整个结构体robj占了12个字节。可以看到巧妙运用了bit field以后,可以节省内存,同时相对直接操纵位来编程也更容易。
4.2 写个程序测试一下
bitfieldtest.c(可在附件下载)
在我的32位的win7下测试结果和预期一致。
我们随后对结构体robj的属性赋了值,用eclipse调试观察内存如下图所示。
图已经很明白了,文字就不再赘述了,只解释一点,type和encoding为何顺序是倒过来的,这个和大端小端有关系。
5. 对象共享
用flyweight模式,将一些常用的整数对象缓存起来。这个思路和java里面的Integer.valueOf如出一辙。
redis.h中定义
redis.c中初始化
可以看到将0~9999的整数缓存了起来。而java里面的Integer则是将-128到127缓存了起来。
6. 引用计数以及对象的销毁
引用计数是垃圾回收的一种算法,但是此算法无法解决循环引用问题,所以java里面没有采用这种算法。
在这里我不禁要感叹c程序员的功力了,事无巨细都亲力亲为。而java程序员就幸福多了,语言,类库还有框架的封装使得程序员生产力大增,且不用动用脑细胞做这类琐事。
7. 具体对象类
下面稍微记录一下redis中5种对象类型所对应的源码位置,方便读者自学。
7.1 字符串对象
redis.h中定义了原型
实现在object.c里
会分3情况创建不同的实现。
7.2 列表对象
见t_list.c
举例:
rpush命令键入后将执行的函数是rpushCommand-->pushGenericCommand-->listTypePush
listTypePush函数里可以看到,如果是ziplist,则调用ziplistPush,如果是adlist,则调用listAddNodeHead或listAddNodeTail
具体的每个命令所调用的函数,可参考
http://www.redisbook.com/en/latest/preview/object/list.html#id3
阻塞。比较难懂,看不懂可以放到以后再看。
http://origin.redisbook.com/en/latest/datatype/list.html#id4
7.3 哈希对象
见t_hash.c
举例:
hset命令键入后将执行的函数是hsetCommand-->hashTypeSet
hashTypeSet函数里可以看到,如果是ziplist,则调用ziplistPush两次,将key和value都加入列表尾部,如果是hashtable(dict),则调用dictReplace
具体的每个命令所调用的函数,可参考
http://www.redisbook.com/en/latest/preview/object/hash.html#id3
7.4 集合对象
见t_set.c
举例:
sadd命令键入后将执行的函数是saddCommand-->setTypeAdd
setTypeAdd函数里可以看到,如果是hashtable(dict),则调用dictAdd,如果是intset,则调用intsetAdd
具体的每个命令所调用的函数,可参考
http://www.redisbook.com/en/latest/preview/object/set.html#id3
7.5 有序集合对象
见t_zset.c
为了让有序集合的查找和范围型操作都尽可能快地执行, Redis 选择了同时使用字典和跳跃表两种数据结构来实现有序集合。
举例:
zadd命令键入后将执行的函数是zaddCommand-->zaddGenericCommand
zaddGenericCommand函数里可以看到,如果是ziplist,则调用zzlInsert,进而调用ziplistInsert两次,将value和score都加入列表,如果是skiplist,则调用zslInsert插入跳跃表,然后调用dictAdd插入到字典。
具体的每个命令所调用的函数,可参考
http://www.redisbook.com/en/latest/preview/object/sorted_set.html#id3
1. 概述
redis有5种对象类型
- 字符串
- 列表
- 哈希
- 集合
- 有序集合
它们的底层数据结构都是由前面6篇文章提到的所实现的。
请先参考如下文章,并结合源码阅读体会。
http://www.redisbook.com/en/latest/toc.html里的8.对象
http://origin.redisbook.com/en/latest/里的第三部分:Redis 数据类型
2. 一种类型,多种实现
redis使得一种对象类型的实现可以由不同的数据结构来切换,从而同时兼顾性能(CPU)和内存。而且当某个底层数据结构有性能问题时,可以替换掉。当然这种替换的实现还是没有java这么优雅的,redis里面只能靠if else来切换不同的数据结构。
对象类型 | 实现方式一 | 实现方式二 | 实现方式三 |
字符串 | REDIS_ENCODING_INT | REDIS_ENCODING_EMBSTR | REDIS_ENCODING_RAW |
列表 | REDIS_ENCODING_ZIPLIST | REDIS_ENCODING_LINKEDLIST | |
哈希 | REDIS_ENCODING_ZIPLIST | REDIS_ENCODING_HT | |
集合 | REDIS_ENCODING_INTSET | REDIS_ENCODING_HT | |
有序集合 | REDIS_ENCODING_ZIPLIST | REDIS_ENCODING_SKIPLIST |
3. zipmap
以前小的哈希表使用zipmap实现的,而redis从2.6版本开始,改用ziplist来实现,因为zipmap被爆有性能问题。
所以现在zipmap已经没用了,如果读者感兴趣的话,代码就在zipmap.c里面。我就不关注了。
4. redisObject的定义
redis.h中定义了redisObject
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;
这里解释一下里面的一个奇怪的语法:(冒号)
该语法的定义叫位字段(bit field)。
4.1 位字段(bit field)
type:4的意思是定义了一个属性叫type,占4个比特,而前面的unsigned等价于unsigned int。
下面又定义了一个属性叫encoding,占4个比特。以及lru,占24个比特。
这样这3个属性合在一起,占32个比特,也就是4个字节。
剩下的refcount和ptr自不必说,各占4个字节。
所以整个结构体robj占了12个字节。可以看到巧妙运用了bit field以后,可以节省内存,同时相对直接操纵位来编程也更容易。
4.2 写个程序测试一下
bitfieldtest.c(可在附件下载)
int main(void) { printf("sizeof(robj)=%d\n", sizeof(robj)); robj obj1; void *address = &obj1; printf("address=%#X\n", address); obj1.type = 1; obj1.encoding = 5; obj1.lru = 255; obj1.refcount = 2; obj1.ptr = NULL; return 0; }
在我的32位的win7下测试结果和预期一致。
sizeof(robj)=12 address=0X22ABE0
我们随后对结构体robj的属性赋了值,用eclipse调试观察内存如下图所示。
图已经很明白了,文字就不再赘述了,只解释一点,type和encoding为何顺序是倒过来的,这个和大端小端有关系。
5. 对象共享
用flyweight模式,将一些常用的整数对象缓存起来。这个思路和java里面的Integer.valueOf如出一辙。
redis.h中定义
struct sharedObjectsStruct { //......此处省略一部分代码...... *integers[REDIS_SHARED_INTEGERS]; };
redis.c中初始化
#define REDIS_SHARED_INTEGERS 10000 struct sharedObjectsStruct shared; void createSharedObjects(void) { int j; //......此处省略一部分代码...... // 常用整数 for (j = 0; j < REDIS_SHARED_INTEGERS; j++) { shared.integers[j] = createObject(REDIS_STRING,(void*)(long)j); shared.integers[j]->encoding = REDIS_ENCODING_INT; } }
可以看到将0~9999的整数缓存了起来。而java里面的Integer则是将-128到127缓存了起来。
6. 引用计数以及对象的销毁
引用计数是垃圾回收的一种算法,但是此算法无法解决循环引用问题,所以java里面没有采用这种算法。
/* * 为对象的引用计数减一 * * 当对象的引用计数降为 0 时,释放对象。 */ void decrRefCount(robj *o) { if (o->refcount <= 0) redisPanic("decrRefCount against refcount <= 0"); // 释放对象 if (o->refcount == 1) { switch(o->type) { case REDIS_STRING: freeStringObject(o); break; case REDIS_LIST: freeListObject(o); break; case REDIS_SET: freeSetObject(o); break; case REDIS_ZSET: freeZsetObject(o); break; case REDIS_HASH: freeHashObject(o); break; default: redisPanic("Unknown object type"); break; } zfree(o); // 减少计数 } else { o->refcount--; } } /* * 释放字符串对象 */ void freeStringObject(robj *o) { if (o->encoding == REDIS_ENCODING_RAW) { sdsfree(o->ptr); } }
在这里我不禁要感叹c程序员的功力了,事无巨细都亲力亲为。而java程序员就幸福多了,语言,类库还有框架的封装使得程序员生产力大增,且不用动用脑细胞做这类琐事。
7. 具体对象类
下面稍微记录一下redis中5种对象类型所对应的源码位置,方便读者自学。
7.1 字符串对象
redis.h中定义了原型
/* Redis object implementation */ robj *createObject(int type, void *ptr); robj *createStringObject(char *ptr, size_t len); robj *createRawStringObject(char *ptr, size_t len); robj *createEmbeddedStringObject(char *ptr, size_t len); robj *createStringObjectFromLongLong(long long value); robj *createStringObjectFromLongDouble(long double value);
实现在object.c里
会分3情况创建不同的实现。
7.2 列表对象
见t_list.c
举例:
rpush命令键入后将执行的函数是rpushCommand-->pushGenericCommand-->listTypePush
listTypePush函数里可以看到,如果是ziplist,则调用ziplistPush,如果是adlist,则调用listAddNodeHead或listAddNodeTail
具体的每个命令所调用的函数,可参考
http://www.redisbook.com/en/latest/preview/object/list.html#id3
阻塞。比较难懂,看不懂可以放到以后再看。
http://origin.redisbook.com/en/latest/datatype/list.html#id4
7.3 哈希对象
见t_hash.c
举例:
hset命令键入后将执行的函数是hsetCommand-->hashTypeSet
hashTypeSet函数里可以看到,如果是ziplist,则调用ziplistPush两次,将key和value都加入列表尾部,如果是hashtable(dict),则调用dictReplace
具体的每个命令所调用的函数,可参考
http://www.redisbook.com/en/latest/preview/object/hash.html#id3
7.4 集合对象
见t_set.c
举例:
sadd命令键入后将执行的函数是saddCommand-->setTypeAdd
setTypeAdd函数里可以看到,如果是hashtable(dict),则调用dictAdd,如果是intset,则调用intsetAdd
具体的每个命令所调用的函数,可参考
http://www.redisbook.com/en/latest/preview/object/set.html#id3
7.5 有序集合对象
见t_zset.c
为了让有序集合的查找和范围型操作都尽可能快地执行, Redis 选择了同时使用字典和跳跃表两种数据结构来实现有序集合。
typedef struct zset { // 字典,使得ZSCORE只需要O(1) dict *dict; // 跳跃表,使得ZRANK只需要O(1) zskiplist *zsl; } zset;
举例:
zadd命令键入后将执行的函数是zaddCommand-->zaddGenericCommand
zaddGenericCommand函数里可以看到,如果是ziplist,则调用zzlInsert,进而调用ziplistInsert两次,将value和score都加入列表,如果是skiplist,则调用zslInsert插入跳跃表,然后调用dictAdd插入到字典。
具体的每个命令所调用的函数,可参考
http://www.redisbook.com/en/latest/preview/object/sorted_set.html#id3
发表评论
-
redis官方文档中文版_Partitioning : 怎么样将你的数据分布在多个redis instance上?
2014-07-30 17:26 2311本文转载自http://skynetdoc.com/?p=11 ... -
redis源码阅读笔记(13)——事务
2014-07-22 15:40 11821. 高层视角解读 redis的 ... -
redis源码阅读笔记(12)——发布与订阅
2014-07-21 16:33 16361. 发布/订阅 发布/订阅(Publish/subscrib ... -
redis源码阅读笔记(11)——服务器与客户端
2014-07-21 16:14 21641.高层视角 可首先阅读《Redis设计与实现》中的服务器与 ... -
redis源码阅读笔记(10)——事件
2014-07-20 14:40 15511. Reactor模式 Reactor模式(反应器模式)是一 ... -
redis源码阅读笔记(9)——RDB,AOF持久化
2014-07-18 16:12 25051. 持久化 Redis提供了两种持久化数据到硬盘的方式。 R ... -
redis源码阅读笔记(8)——数据库
2014-07-18 11:27 12961. 高层视角解读 Redis设计与实现中的数据库章节 Re ... -
redis源码阅读笔记(6)——ziplist
2014-07-12 01:13 12111.高层视角解读 压缩列表(ziplist)是为了尽可能地节 ... -
redis源码阅读笔记(5)——intset
2014-07-08 16:23 8811.高层视角解读 整数集合(intset)是集合键的底层实现 ... -
redis源码阅读笔记(4)——skiplist
2014-07-08 15:08 9880.跳表基础知识 跳表(s ... -
redis源码阅读笔记(3)——dict
2014-07-08 13:26 14281.高层视角解读 字典, ... -
redis源码阅读笔记(2)——adlist
2014-07-07 15:54 1825adlist(A doubly linked list)是Re ... -
redis源码阅读笔记(1)——sds
2014-07-06 01:03 1440最近突然一时兴起,开 ... -
用spring-data-redis实现类似twitter的网站
2014-06-20 14:09 73821. spring-data-redis简介 封装了一下red ... -
jedis/nosql-unit初步
2014-06-17 16:36 12011. redis的客户端概述 redis的客户端实在太多了,比 ... -
redis初步
2014-06-16 23:19 16480. 介绍 redis是一个高性 ...
相关推荐
Redis是一款高性能的键值对数据库,其内部使用了许多优化的数据结构来存储数据,其中ziplist是Redis为了节省内存而设计...在阅读Redis源码时,深入分析ziplist的实现细节将有助于我们更好地理解和调试Redis的内存管理。
在Redis源码阅读笔记(10)——事件中,我们将探讨Redis如何利用事件模型来实现非阻塞I/O,以及相关的编程模型如Reactor模式和NIO。 Redis使用了一个基于epoll的事件处理器,epoll是Linux系统提供的一种高效I/O多路...
本篇笔记将聚焦于Redis源码中的“sds”(Simple Dynamic Strings,简单动态字符串)部分,这是Redis中处理字符串的基础数据结构。 首先,我们要明白sds是什么。在C语言中,字符串是以字符数组的形式存在的,而sds是...
### Redis基础配置笔记 #### 一、Redis简介 Redis 是一款开源且采用 BSD 许可证的高级键值存储系统。由于它支持多种数据结构,如字符串(Strings)、哈希(Hashes)、列表(Lists)、集合(Sets)以及有序集合...
我在之前阅读源码的同时也发现了很多变化的地方,在此把已经发现的列举出来,不过肯定是不全的,因为我还没读完(逃跑~)。等再发现新的值得一聊的,并且能凑够一篇文章的内容的话,我会再继续写一篇的。
概念:Redis (REmote DIctionary Server) 是用 C 语言开发的一个开源的高性能键值对(key-value)数据库。 特征: 1、数据间没有必然的关联关系 2、内部采用单线程机制进行工作 3、高性能。 4、多数据类型支持: ...
SSM实战项目——Java高并发秒杀API是一个深入学习Java后端开发的重要实践,它涵盖了Spring、SpringMVC和MyBatis三大框架的整合应用,以及如何处理高并发下的秒杀场景。在这个项目中,我们将深入理解如何设计并实现一...
**Redis实战教程——Java工程师视角** 本教程名为“javacourse-redis-in-action”,是针对Java工程师设计的一套深入浅出的Redis实战课程。旨在帮助Java开发者更好地理解和应用Redis这一高性能、键值存储系统,通过...
《个人知识管理系统的设计与实现——基于Java、Idea、Vue、Redis及Mybatis的技术实践》 个人知识管理系统是现代信息社会中,为了帮助用户高效管理、组织和分享知识而开发的一种软件工具。在这个系统中,用户可以...
│ │ 鲁班学院-上课笔记mybaits源码分析9-05.docx │ │ │ └─mybaits源码分析 │ mybaits源码分析.mp4 │ ├─面试必问-springcloud架构微服务项目 │ springcloud架构微服务项目.mp4 │ ├─面试必问-...
### 偌依框架的使用学习笔记 #### 一、若依框架的介绍与说明 若依框架是一款基于Spring Boot、Spring Security、JWT、Vue和Element的全开源快速开发平台,旨在提供高效的前后端分离解决方案。它具备以下特点: 1....
Docker —— 从入门到实践 - v1.0.pdf Dubbo部署文档.doc Dubbo面试专题.docx java后端面试题答案.pdf Java并发Fork-Join框架原理解析.docx JAVA核心知识整理.pdf JAVA核心知识点整理.pdf Java面试笔记.docx JAVA...
java8集合源码真棒明星 我自己的真棒清单! 生成者 [Jupyter 笔记本](#jupyter 笔记本) 动作脚本 - 长期废弃的 SWFUpload 项目的一个分支,由 WordPress 和其他人维护,以确保存在安全版本的 SWFUpload。 向 报告...
本项目——“Java基于SpringBoot的教学资料管理系统”旨在提供一个高效、便捷的平台,帮助管理员和用户对各类教学资源进行有效管理。下面将详细阐述系统的核心功能以及实现原理。 一、系统架构与技术选型 本系统...
《SpringBootNote——让开发如虎添翼》 在IT行业中,SpringBoot已经成为了Java开发者构建微服务架构的首选框架,其简洁的配置、快速的启动以及强大的集成能力深受好评。"SpringBootNote",正如其名,是针对...