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的更多相关文章

  1. .Net Core缓存组件(Redis)源码解析

    上一篇文章已经介绍了MemoryCache,MemoryCache存储的数据类型是Object,也说了Redis支持五中数据类型的存储,但是微软的Redis缓存组件只实现了Hash类型的存储.在分析源 ...

  2. Redis源码解析:15Resis主从复制之从节点流程

    Redis的主从复制功能,可以实现Redis实例的高可用,避免单个Redis 服务器的单点故障,并且可以实现负载均衡. 一:主从复制过程 Redis的复制功能分为同步(sync)和命令传播(comma ...

  3. Redis源码解析之跳跃表(三)

    我们再来学习如何从跳跃表中查询数据,跳跃表本质上是一个链表,但它允许我们像数组一样定位某个索引区间内的节点,并且与数组不同的是,跳跃表允许我们将头节点L0层的前驱节点(即跳跃表分值最小的节点)zsl- ...

  4. Redis源码解析:13Redis中的事件驱动机制

    Redis中,处理网络IO时,采用的是事件驱动机制.但它没有使用libevent或者libev这样的库,而是自己实现了一个非常简单明了的事件驱动库ae_event,主要代码仅仅400行左右. 没有选择 ...

  5. Redis源码解析

    一.src/server.c 中的redisCommandTable列出的所有redis支持的命令,其中字符串命令包括从get到mget:列表命令从rpush到rpoplpush:集合命令包括从sad ...

  6. Redis源码解析:26集群(二)键的分配与迁移

    Redis集群通过分片的方式来保存数据库中的键值对:一个集群中,每个键都通过哈希函数映射到一个槽位,整个集群共分16384个槽位,集群中每个主节点负责其中的一部分槽位. 当数据库中的16384个槽位都 ...

  7. Redis源码解析:25集群(一)握手、心跳消息以及下线检测

    Redis集群是Redis提供的分布式数据库方案,通过分片来进行数据共享,并提供复制和故障转移功能. 一:初始化 1:数据结构 在源码中,通过server.cluster记录整个集群当前的状态,比如集 ...

  8. Redis源码解析之跳跃表(一)

    跳跃表(skiplist) 有序集合(sorted set)是Redis中较为重要的一种数据结构,从名字上来看,我们可以知道它相比一般的集合多了一个有序.Redis的有序集合会要求我们给定一个分值(s ...

  9. jedis的publish/subscribe[转]含有redis源码解析

    首先使用redis客户端来进行publish与subscribe的功能是否能够正常运行. 打开redis服务器 [root@localhost ~]# redis-server /opt/redis- ...

随机推荐

  1. 用python玩微信(聊天机器人,好友信息统计)

    1.用 Python 实现微信好友性别及位置信息统计 这里使用的python3+wxpy库+Anaconda(Spyder)开发.如果你想对wxpy有更深的了解请查看:wxpy: 用 Python 玩 ...

  2. Part1-HttpClient快速入门案例

    前言 最近这段时间在学习爬虫方面的知识,接触了几个优秀的爬虫框架,也爬取了一些自己喜欢网站的信息.通过官网学习HttpClient的过程中,希望通过写此博客来巩固自己的学习,也为有需要的人提供学习帮助 ...

  3. Android日历开发之右上角标注红点事件

    1.效果如下所示: 2.方法:      前提:已经知道如何在右上角画圆点的情况下.      这是一个任务显示器,每个任务都有一个时间,比如2014.01.12.      如果要标注2016.08 ...

  4. Vue组件-使用插槽分发内容

    在使用组件时,我们常常要像这样组合它们: <app> <app-header></app-header> <app-footer></app-fo ...

  5. [Leetcode Week14]Path Sum II

    Path Sum II 题解 原创文章,拒绝转载 题目来源:https://leetcode.com/problems/path-sum-ii/description/ Description Giv ...

  6. 【设计模式】享元模式(Flyweight)

    摘要: 1.本文将详细介绍享元模式的原理和实际代码中特别是Android系统代码中的应用. 纲要: 1. 引入享元模式 2. 享元模式的概念及优缺点介绍 3. 享元模式在Android源码中的应用 1 ...

  7. Open WATCOM指南 - 哦这样的孤单 你冷若冰霜

    https://my.oschina.net/GIIoOS/blog/126701 WATCOM的历史可以追溯到1965年 加拿大的学生Waterloo的团队开发了叫WATFOR的Fortran编译器 ...

  8. git学习笔记三

    1.每个分支的历史版本维护信息位置是.git/logs/refs/heads/master,这个位置的信息是文本文件,不是引用. harvey@harvey-Virtual-Machine:~/dem ...

  9. thinkphp模板常用的方法

    thinkphp模板我是看了3.2的文档,对里面的东西过了一遍,然后在写到需要用到模板的东西的时候就有印象,有的能直接回顾,但是有的就可能只知道有这个东西,但是不知道怎么用,所以就重新查手册,这个的话 ...

  10. Django Ajax学习二之csrf跨站请求伪造

    方式1 $.ajaxSetup({ data: {csrfmiddlewaretoken: '{{ csrf_token }}' }, }); 方式2 # html文件from表单中<form& ...