Redis源码解析之ziplist
Ziplist是用字符串来实现的双向链表,对于容量较小的键值对,为其创建一个结构复杂的哈希表太浪费内存,所以redis 创建了ziplist来存放这些键值对,这可以减少存放节点指针的空间,因此它被用来作为哈希表初始化时的底层实现。下图即ziplist 的内部结构。
Zlbytes是整个ziplist 所占用的空间,必要时需要重新分配。
Zltail便于快速的访问到表尾节点,不需要遍历整个ziplist。
Zllen表示包含的节点数。
Entries表示用户增加上去的节点。
Zlend是一个255的值,表示ziplist末尾
Ziplist比dict更节省内存,所以在创建hash的时候默认ziplist作为其底层实现,当有需要时,再转换回来。
举例:用户创建一个以ziplist为底层的hash键:
Redis-cli > hset book name "programing"
首先进入hsetCommand()函数的hashTypeLookupWriteOrCreate()函数
void hsetCommand(redisClient *c) {
int update;
robj *o; if ((o = hashTypeLookupWriteOrCreate(c,c->argv[])) == NULL) return;
hashTypeTryConversion(o,c->argv,,);
hashTypeTryObjectEncoding(o,&c->argv[], &c->argv[]);
update = hashTypeSet(o,c->argv[],c->argv[]);
addReply(c, update ? shared.czero : shared.cone);
signalModifiedKey(c->db,c->argv[]);
notifyKeyspaceEvent(REDIS_NOTIFY_HASH,"hset",c->argv[],c->db->id);
server.dirty++;
} robj *hashTypeLookupWriteOrCreate(redisClient *c, robj *key) {
robj *o = lookupKeyWrite(c->db,key);
if (o == NULL) {
o = createHashObject();
dbAdd(c->db,key,o);
} else {
if (o->type != REDIS_HASH) {
addReply(c,shared.wrongtypeerr);
return NULL;
}
}
return o;
}
先创建一个空的ziplist,编码方式默认为ziplist ,再add这个Key(book)到DB中
主要的添加操作在hashTpyeSet()中
/* Add an element, discard the old if the key already exists.
* Return 0 on insert and 1 on update.
* This function will take care of incrementing the reference count of the
* retained fields and value objects. */
int hashTypeSet(robj *o, robj *field, robj *value) {
int update = ; if (o->encoding == REDIS_ENCODING_ZIPLIST) {
unsigned char *zl, *fptr, *vptr; field = getDecodedObject(field);
value = getDecodedObject(value); zl = o->ptr;
fptr = ziplistIndex(zl, ZIPLIST_HEAD);
if (fptr != NULL) {
fptr = ziplistFind(fptr, field->ptr, sdslen(field->ptr), );
if (fptr != NULL) {
/* Grab pointer to the value (fptr points to the field) */
vptr = ziplistNext(zl, fptr);
redisAssert(vptr != NULL);
update = ; /* Delete value */
zl = ziplistDelete(zl, &vptr); /* Insert new value */
zl = ziplistInsert(zl, vptr, value->ptr, sdslen(value->ptr));
}
} if (!update) {
/* Push new field/value pair onto the tail of the ziplist */
zl = ziplistPush(zl, field->ptr, sdslen(field->ptr), ZIPLIST_TAIL);
zl = ziplistPush(zl, value->ptr, sdslen(value->ptr), ZIPLIST_TAIL);
}
o->ptr = zl;
decrRefCount(field);
decrRefCount(value); /* Check if the ziplist needs to be converted to a hash table */
if (hashTypeLength(o) > server.hash_max_ziplist_entries)
hashTypeConvert(o, REDIS_ENCODING_HT);
} else if (o->encoding == REDIS_ENCODING_HT) {
if (dictReplace(o->ptr, field, value)) { /* Insert */
incrRefCount(field);
} else { /* Update */
update = ;
}
incrRefCount(value);
} else {
redisPanic("Unknown hash encoding");
}
return update;
}
首先会搜索ziplist ,如果发现有相同的键值,则替换掉,如果找不到,则把新加入的键值push到ziplist 的末尾,在源码中可以发现当其长度大于hash_max_ziplist_entries就需要转换为hash table的编码方式。
完成上述操作之后,就使用addReply()把结果存到buffer中传给客户端。
Redis源码解析之ziplist的更多相关文章
- .Net Core缓存组件(Redis)源码解析
上一篇文章已经介绍了MemoryCache,MemoryCache存储的数据类型是Object,也说了Redis支持五中数据类型的存储,但是微软的Redis缓存组件只实现了Hash类型的存储.在分析源 ...
- Redis源码解析:15Resis主从复制之从节点流程
Redis的主从复制功能,可以实现Redis实例的高可用,避免单个Redis 服务器的单点故障,并且可以实现负载均衡. 一:主从复制过程 Redis的复制功能分为同步(sync)和命令传播(comma ...
- Redis源码解析之跳跃表(三)
我们再来学习如何从跳跃表中查询数据,跳跃表本质上是一个链表,但它允许我们像数组一样定位某个索引区间内的节点,并且与数组不同的是,跳跃表允许我们将头节点L0层的前驱节点(即跳跃表分值最小的节点)zsl- ...
- Redis源码解析:13Redis中的事件驱动机制
Redis中,处理网络IO时,采用的是事件驱动机制.但它没有使用libevent或者libev这样的库,而是自己实现了一个非常简单明了的事件驱动库ae_event,主要代码仅仅400行左右. 没有选择 ...
- Redis源码解析
一.src/server.c 中的redisCommandTable列出的所有redis支持的命令,其中字符串命令包括从get到mget:列表命令从rpush到rpoplpush:集合命令包括从sad ...
- Redis源码解析:26集群(二)键的分配与迁移
Redis集群通过分片的方式来保存数据库中的键值对:一个集群中,每个键都通过哈希函数映射到一个槽位,整个集群共分16384个槽位,集群中每个主节点负责其中的一部分槽位. 当数据库中的16384个槽位都 ...
- Redis源码解析:25集群(一)握手、心跳消息以及下线检测
Redis集群是Redis提供的分布式数据库方案,通过分片来进行数据共享,并提供复制和故障转移功能. 一:初始化 1:数据结构 在源码中,通过server.cluster记录整个集群当前的状态,比如集 ...
- Redis源码解析之跳跃表(一)
跳跃表(skiplist) 有序集合(sorted set)是Redis中较为重要的一种数据结构,从名字上来看,我们可以知道它相比一般的集合多了一个有序.Redis的有序集合会要求我们给定一个分值(s ...
- jedis的publish/subscribe[转]含有redis源码解析
首先使用redis客户端来进行publish与subscribe的功能是否能够正常运行. 打开redis服务器 [root@localhost ~]# redis-server /opt/redis- ...
随机推荐
- mave的依赖范围
compile(编译范围) compile是默认的范围:如果没有提供一个范围,那该依赖的范围就是编译范 围.编译范围依赖在所有的classpath中可用,同时它们也会被打包. provided(已提供 ...
- Python【模块】importlib,requests
内容概要: 模仿django中间件的加载方式 importlib模块 requests模块 rsplit() 用实际使用的理解来解释两个模块 importlib模块 ...
- Linux 入门记录:十、Linux 下获取帮助
一.获取帮助 Linux 提供了极为详细的帮助工具和文档,通过查阅相关文档,可以大大减少需要记忆的东西并提高效率. 二.--help参数 几乎所有命令都可以使用 -h 或 --help 参数获取命令的 ...
- Linux内核中链表的实现与应用【转】
转自:http://blog.chinaunix.net/uid-27037833-id-3237153.html 链表(循环双向链表)是Linux内核中最简单.最常用的一种数据结构. ...
- BZOJ 4516: [Sdoi2016]生成魔咒——后缀数组、并查集
传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=4516 题意 一开始串为空,每次往串后面加一个字符,求本质不同的子串的个数,可以离线.即长度为 ...
- tableView选中行的调用顺序/ 取消选中Cell
UITableViewCell它有两个属性highLighted.selected.很明显一个是高亮状态, 一个是选中状态. UITableViewCell, 对应的2个方法 // 高亮状态调用的方法 ...
- 《Java编程思想》阅读笔记一
Java编程思想 这是一个通过对<Java编程思想>(Think in java)第四版进行阅读同时对java内容查漏补缺的系列.一些基础的知识不会被罗列出来,这里只会列出一些程序员经常会 ...
- leetcode 之Set Matrix Zeroes(10)
设置两个布尔数组,记录行和列是否存在0.需要注意的是如何将行或列设为0. void setZeros(vector<vector<int>> &matrix) { in ...
- 爬虫 requests模块高级用法
一 介绍 #介绍:使用requests可以模拟浏览器的请求,比起之前用到的urllib,requests模块的api更加便捷(本质就是封装了urllib3) #注意:requests库发送请求将网页内 ...
- redis之(八)redis的有序集合类型的命令
[一]增加元素 --->命令:ZADD key score member [score member] --->向有序集合放入一个分数为score的member元素 --->元素存在 ...