Redis源码分析 List实现
在版本3.2之前,Redis中的列表是 ziplist 和 linkedlist 实现的,在3.2之后,由quicklist实现。
双向链表linkedlist在表的两端进行push和pop操作非常方便,但是地址不连续,而且需要保持额外的指针。
ziplist是连续内存,存储效率高。但不利于修改操作,插入和删除需要重新申请和释放内存。
先看quicklist数据结构
/* Node, quicklist, and Iterator are the only data structures used currently. *//* quicklistNode is a 32 byte struct describing a ziplist for a quicklist.* We use bit fields keep the quicklistNode at 32 bytes.* count: 16 bits, max 65536 (max zl bytes is 65k, so max count actually < 32k).* encoding: 2 bits, RAW=1, LZF=2.* container: 2 bits, NONE=1, ZIPLIST=2.* recompress: 1 bit, bool, true if node is temporarry decompressed for usage.* attempted_compress: 1 bit, boolean, used for verifying during testing.* extra: 10 bits, free for future use; pads out the remainder of 32 bits */
typedef struct quicklistNode {struct quicklistNode *prev;struct quicklistNode *next;unsigned char *zl;unsigned int sz; /* ziplist size in bytes */unsigned int count : 16; /* count of items in ziplist */unsigned int encoding : 2; /* RAW==1 or LZF==2 */unsigned int container : 2; /* NONE==1 or ZIPLIST==2 */unsigned int recompress : 1; /* was this node previous compressed? */unsigned int attempted_compress : 1; /* node can't compress; too small */unsigned int extra : 10; /* more bits to steal for future usage */
} quicklistNode;/* quicklistLZF is a 4+N byte struct holding 'sz' followed by 'compressed'.* 'sz' is byte length of 'compressed' field.* 'compressed' is LZF data with total (compressed) length 'sz'* NOTE: uncompressed length is stored in quicklistNode->sz.* When quicklistNode->zl is compressed, node->zl points to a quicklistLZF */
typedef struct quicklistLZF {unsigned int sz; /* LZF size in bytes*/char compressed[];
} quicklistLZF;/* quicklist is a 40 byte struct (on 64-bit systems) describing a quicklist.* 'count' is the number of total entries.* 'len' is the number of quicklist nodes.* 'compress' is: -1 if compression disabled, otherwise it's the number* of quicklistNodes to leave uncompressed at ends of quicklist.* 'fill' is the user-requested (or default) fill factor. */
typedef struct quicklist {quicklistNode *head;quicklistNode *tail;unsigned long count; /* total count of all entries in all ziplists */unsigned long len; /* number of quicklistNodes */int fill : 16; /* fill factor for individual nodes */unsigned int compress : 16; /* depth of end nodes not to compress;0=off */
} quicklist;
quicklist数据结构说明
fill是ziplist大小
compress是节点压缩深度
quicklistNode 数据结构说明
*zl是数据指针,如果没有被压缩,就指向ziplist,否则指向quicklistLZF
count是ziplist的数据个数
encoding : 2; 编码方式,1:ziplist,2:quicklistLZF
quicklist整体数据结构图
PUSH操作
/*-----------------------------------------------------------------------------* List Commands*----------------------------------------------------------------------------*/void pushGenericCommand(client *c, int where) {int j, pushed = 0;robj *lobj = lookupKeyWrite(c->db,c->argv[1]);if (lobj && lobj->type != OBJ_LIST) {addReply(c,shared.wrongtypeerr);return;}for (j = 2; j < c->argc; j++) {if (!lobj) {lobj = createQuicklistObject();quicklistSetOptions(lobj->ptr, server.list_max_ziplist_size,server.list_compress_depth);dbAdd(c->db,c->argv[1],lobj);}listTypePush(lobj,c->argv[j],where);pushed++;}addReplyLongLong(c, (lobj ? listTypeLength(lobj) : 0));if (pushed) {char *event = (where == LIST_HEAD) ? "lpush" : "rpush";signalModifiedKey(c->db,c->argv[1]);notifyKeyspaceEvent(NOTIFY_LIST,event,c->argv[1],c->db->id);}server.dirty += pushed;
}void lpushCommand(client *c) {pushGenericCommand(c,LIST_HEAD);
}void rpushCommand(client *c) {pushGenericCommand(c,LIST_TAIL);
}
lpush和rpush操作都是调用pushGenericCommand
pushGenericCommand调用listTypePush
/*-----------------------------------------------------------------------------* List API*----------------------------------------------------------------------------*//* The function pushes an element to the specified list object 'subject',* at head or tail position as specified by 'where'.** There is no need for the caller to increment the refcount of 'value' as* the function takes care of it if needed. */
void listTypePush(robj *subject, robj *value, int where) {if (subject->encoding == OBJ_ENCODING_QUICKLIST) {int pos = (where == LIST_HEAD) ? QUICKLIST_HEAD : QUICKLIST_TAIL;value = getDecodedObject(value);size_t len = sdslen(value->ptr);quicklistPush(subject->ptr, value->ptr, len, pos);decrRefCount(value);} else {serverPanic("Unknown list encoding");}
}
quicklistPush和quicklistPop
/* Default pop function** Returns malloc'd value from quicklist */
int quicklistPop(quicklist *quicklist, int where, unsigned char **data,unsigned int *sz, long long *slong) {unsigned char *vstr;unsigned int vlen;long long vlong;if (quicklist->count == 0)return 0;int ret = quicklistPopCustom(quicklist, where, &vstr, &vlen, &vlong,_quicklistSaver);if (data)*data = vstr;if (slong)*slong = vlong;if (sz)*sz = vlen;return ret;
}/* Wrapper to allow argument-based switching between HEAD/TAIL pop */
void quicklistPush(quicklist *quicklist, void *value, const size_t sz,int where) {if (where == QUICKLIST_HEAD) {quicklistPushHead(quicklist, value, sz);} else if (where == QUICKLIST_TAIL) {quicklistPushTail(quicklist, value, sz);}
}
quicklistPushHead和quicklistPushTail
/* Add new entry to head node of quicklist.** Returns 0 if used existing head.* Returns 1 if new head created. */
int quicklistPushHead(quicklist *quicklist, void *value, size_t sz) {quicklistNode *orig_head = quicklist->head;if (likely(_quicklistNodeAllowInsert(quicklist->head, quicklist->fill, sz))) {quicklist->head->zl =ziplistPush(quicklist->head->zl, value, sz, ZIPLIST_HEAD);quicklistNodeUpdateSz(quicklist->head);} else {quicklistNode *node = quicklistCreateNode();node->zl = ziplistPush(ziplistNew(), value, sz, ZIPLIST_HEAD);quicklistNodeUpdateSz(node);_quicklistInsertNodeBefore(quicklist, quicklist->head, node);}quicklist->count++;quicklist->head->count++;return (orig_head != quicklist->head);
}/* Add new entry to tail node of quicklist.** Returns 0 if used existing tail.* Returns 1 if new tail created. */
int quicklistPushTail(quicklist *quicklist, void *value, size_t sz) {quicklistNode *orig_tail = quicklist->tail;if (likely(_quicklistNodeAllowInsert(quicklist->tail, quicklist->fill, sz))) {quicklist->tail->zl =ziplistPush(quicklist->tail->zl, value, sz, ZIPLIST_TAIL);quicklistNodeUpdateSz(quicklist->tail);} else {quicklistNode *node = quicklistCreateNode();node->zl = ziplistPush(ziplistNew(), value, sz, ZIPLIST_TAIL);quicklistNodeUpdateSz(node);_quicklistInsertNodeAfter(quicklist, quicklist->tail, node);}quicklist->count++;quicklist->tail->count++;return (orig_tail != quicklist->tail);
}
最重要就是这一行代码:
ziplistPush
unsigned char *ziplistPush(unsigned char *zl, unsigned char *s, unsigned int slen, int where) {unsigned char *p;p = (where == ZIPLIST_HEAD) ? ZIPLIST_ENTRY_HEAD(zl) : ZIPLIST_ENTRY_END(zl);return __ziplistInsert(zl,p,s,slen);
}
__ziplistInsert在 ziplist.c太长了不贴代码了。
关于LZF压缩算法是第三方的,redis直接引用了。
LZF的压缩算法,该算法用于对quicklist的节点进行压缩操作。list的设计目的是能够存放很长的数据列表,当列表很长时,必然会占用很高的内存空间,且list中最容易访问的是两端的数据,中间的数据访问率较低,于是就可以从这个出发点来进一步节省内存用于其他操作。
lzh.h
/** Copyright (c) 2000-2008 Marc Alexander Lehmann <schmorp@schmorp.de>** Redistribution and use in source and binary forms, with or without modifica-* tion, are permitted provided that the following conditions are met:** 1. Redistributions of source code must retain the above copyright notice,* this list of conditions and the following disclaimer.** 2. Redistributions in binary form must reproduce the above copyright* notice, this list of conditions and the following disclaimer in the* documentation and/or other materials provided with the distribution.** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER-* CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO* EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE-* CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH-* ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED* OF THE POSSIBILITY OF SUCH DAMAGE.** Alternatively, the contents of this file may be used under the terms of* the GNU General Public License ("GPL") version 2 or any later version,* in which case the provisions of the GPL are applicable instead of* the above. If you wish to allow the use of your version of this file* only under the terms of the GPL and not to allow others to use your* version of this file under the BSD license, indicate your decision* by deleting the provisions above and replace them with the notice* and other provisions required by the GPL. If you do not delete the* provisions above, a recipient may use your version of this file under* either the BSD or the GPL.*/#ifndef LZF_H
#define LZF_H/***********************************************************************
**
** lzf -- an extremely fast/free compression/decompression-method
** http://liblzf.plan9.de/
**
** This algorithm is believed to be patent-free.
**
***********************************************************************/#define LZF_VERSION 0x0105 /* 1.5, API version *//** Compress in_len bytes stored at the memory block starting at* in_data and write the result to out_data, up to a maximum length* of out_len bytes.** If the output buffer is not large enough or any error occurs return 0,* otherwise return the number of bytes used, which might be considerably* more than in_len (but less than 104% of the original size), so it* makes sense to always use out_len == in_len - 1), to ensure _some_* compression, and store the data uncompressed otherwise (with a flag, of* course.** lzf_compress might use different algorithms on different systems and* even different runs, thus might result in different compressed strings* depending on the phase of the moon or similar factors. However, all* these strings are architecture-independent and will result in the* original data when decompressed using lzf_decompress.** The buffers must not be overlapping.** If the option LZF_STATE_ARG is enabled, an extra argument must be* supplied which is not reflected in this header file. Refer to lzfP.h* and lzf_c.c.**/
unsigned int
lzf_compress (const void *const in_data, unsigned int in_len,void *out_data, unsigned int out_len);/** Decompress data compressed with some version of the lzf_compress* function and stored at location in_data and length in_len. The result* will be stored at out_data up to a maximum of out_len characters.** If the output buffer is not large enough to hold the decompressed* data, a 0 is returned and errno is set to E2BIG. Otherwise the number* of decompressed bytes (i.e. the original length of the data) is* returned.** If an error in the compressed data is detected, a zero is returned and* errno is set to EINVAL.** This function is very fast, about as fast as a copying loop.*/
unsigned int
lzf_decompress (const void *const in_data, unsigned int in_len,void *out_data, unsigned int out_len);#endif
扩展阅读参考:
https://blog.csdn.net/belalds/article/details/91976367
https://blog.csdn.net/harleylau/article/details/80534159
相关文章:

Linux cut命令
用途 文本文件按列提取。 特点 过于简单,只能处理固定格式的分隔符,分隔符不能使用正则表达式。 用法 命令基本格式 -b、-c、-f分别表示字节、字符、字段(即byte、character、field);list表示-b、-c、-f操作范围&#…

【MATLAB】符号数学计算(四):符号表达式操作
一、符号表达式合并 Rcollect(S):将表达式S中相同次幂的项合并。S可以是一个表达式,也可以是一个符号矩阵。Rcollect(S,v):将表达式中S中v的相同次幂进行合并。如果v没有指定,则默认将含有x的相同次幂的项进行合并。 >> sy…

Alpha冲刺——day1
Alpha冲刺——day1 作业链接 Alpha冲刺随笔集 github地址 站立式会议 会议安排:alpha冲刺的第一天,我们站立式会议讨论了我们接下来的安排,做出大致的规划,并针对之前的原型设计,讨论了界面设计的大概 项目进展项目进展…

一步一步学习VirtualBox安装CentOS7和CentOS8
个人学习研究Linux推荐安装VirtualBoxCentOS。 CentOS7和CentOS8的安装实际上是非常相似的,改变的地方不多,从CentOS7开始和CentOS6相比改变是非常大的。 VirtualBox本身是免费的,足够正常学习应用了,安装CentOS是因为企业线上大…

建模原语:四象图
原文地址:http://www.douban.com/note/164191021/ “模型、状态和行为特征、场景”和“四象图”,建模观的命名与立象。 建模原语:四象图 作者:achieveideagmail.com 命名:模型、结构特征、行为特征、场景(及其规约&…

【MATLAB】符号数学计算(五):符号函数的替换
一、subs替换函数 Rsubs(S):用工作区中的变量值替换符号表达式中的某一特定符号。Rsubs(S,New):用新符号变量New来替换符号表达式S中的默认变量。Rsubs(S,Old,New) >> syms x y >> fsym(x^2x*yy^2)f x^2 x*y y^2>> x2; >> su…

Ubuntu阿里云搭建Mono.net环境
查看磁盘信息 我们买的系统默认情况下只是安装了系统,而数据盘需要自己挂载,例如我这里的系统占用20多G,还有40多G的数据盘默认是没有挂载的,首先我们运行df -h查看: rootAY1212241134392134698:~# df -hFilesystem Si…

MongoDB分布式原理以及read-preference和readConcern解决读写一致性问题
MongoDB词汇表: https://docs.mongodb.com/manual/reference/glossary/#term-replica-set MongoDB分布式原理 primary In a replica set, the primary is the member that receives all write operations. See Primary. 在副本集中,主库是接收所有写…

Lua(Codea) 中 table.insert 越界错误原因分析
2019独角兽企业重金招聘Python工程师标准>>> Lua(Codea) 中 table.insert(touches, touch.id, touch) 越界错误原因分析 背景介绍 在 Codea 上运行其他人以前写的代码时, 发现某段处理 touch 事件的代码总是报错, 开始报浮点数没有整型的表示, 修改代码增加类型转换…

【MATLAB】符号数学计算(六):符号函数的操作
一、复合函数的操作 compose(f,g):返回复合函数f(g(y)),此处ff(x),gg(y);compose(f,g,x,z):返回自变量是z的复合函数f(g(z)) >> syms x y >> fsym(xx^-1); >> gsym(sin(x)); >> h(1y^2); >…

java中如何应对读改写场景
前言 volatile可以确保数据及时刷新到主存,但是对于读改写场景还是无能为力 举个例子 public class ConcurrentHashMapExample {public static void main(String[] args) throws InterruptedException {Map<String, Long> ordersMap new ConcurrentHashMap&l…

Apache Hudi的写时复制和读时合并
Apache Hudi http://hudi.apache.org/ http://hudi.apache.org/docs/quick-start-guide.html Hudi是什么 Hudi将流处理带到大数据,提供新数据,同时比传统批处理效率高一个数量级。 Hudi可以帮助你构建高效的数据湖,解决一些最复杂的底层…

顶尖程序员不同于常人的 5 个区别
2019独角兽企业重金招聘Python工程师标准>>> 《The Effective Engineer》的作者在写书的过程中,为了了解那些顶级程序员和普通程序员的区别,采访了很多硅谷顶级科技公司的顶尖软件工程师。他发现这些给世界带来巨大影响的的工程师们至少有以下…
【MATLAB】符号数学计算(七):符号微积分、符号微分方程求解、符号代数方程求解
一、符号表达式的极限 limit(F,x,a):求当时,符号表达式F的极限。limit(F,a):符号表达式F采用默认自变量(可由函数findsym求得),该函数求F的自变量趋于a时的极限值。limit(F):符号表达式采用默认…

Qt运行时中文乱码的解决办法
QT5的解决办法,在类之前添加: #pragma execution_character_set("utf-8")QT4解决办法: QTextCodec::setCodecForLocale(QTextCodec::codecForLocale());转载于:https://www.cnblogs.com/bjxingch/articles/9992998.html

更换yum的源为阿里云或者网易
1.备份原本的yum源: #mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup 2.下载阿里云的yum源: CentOS6,CentOS7,CentOS8下对应的即可 wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Ce…

socket编程:多路复用I/O服务端客户端之poll
一. 关于poll对于IO复用模型,其优点无疑是免去了对一个个IO事件就绪的等待,转而代之的是同时对多个IO数据的检测,当检测等待的事件中至少有一个就绪的时候,就会返回告诉用户进程“已经有数据准备好了,快看看是哪个赶紧…
【MATLAB】符号数学计算(八):符号分析可视化
一、funtool分析界面 在命令行窗口中输入: funtool 这里就说一下第四排: Insert:把当前激活窗的函数写入列表Cycle:依次循环显示fxlist中的函数Delete:从fxlist列表中删除激活窗的函数Reset:使计算器恢复…

java 根据实体对象生成 增删改的SQL语句 ModelToSQL
2019独角兽企业重金招聘Python工程师标准>>> java 根据实体对象生成 增删改的SQL语句 ModelToSQL 转载于:https://my.oschina.net/miaojiangmin/blog/2907010

深入浅出SpringBoot源码分析
Spring源码非常多,不要迷失在源码的汪洋大海里,抓住主要脉络,有需要再研究即可。 Bean的初始化 1.发现所有的bean ComponentScanAnnotationParser.parse()调用doScan()扫包 这里只是扫用户定义的bean,系统的自然不用扫 ClassPathBeanDefinitionScanner.doScan protected…

HBase基本知识
为什么80%的码农都做不了架构师?>>> 概述 HBase 特性: 强一致性读写: HBase 不是 "最终一致性(eventually consistent)" 数据存储. 这让它很适合高速计数聚合类任务。自动分片(Automatic sharding): HBase 表通过region分布在集群…

【编程题】猜年龄
题目标题: 猜年龄 美国数学家维纳(N.Wiener) 智力早熟,11岁就上了大学。他曾在1935~1936年应邀来中国清华大学讲学。 一次,他参加某个重要会议,年轻的脸孔引人注目。于是有人询问他的年龄,他回答说: “我年龄的立方是个…

XenServer和VMware vSphere技术比较
此次将Citrix XenServer7.1和VMware ESXi 6.5从技术角度进行比较,并从企业角度对企业关心的项进行比较。主要包含市场地位、服务器虚拟化底层稳定性、管理架构、兼容性上进行分析。 市场地位 VMware在虚拟化的地位类似于大型存储中的EMC、小型机中IBM、网络中的思科…

阿里巴巴开源的缓存框架JetCache创建缓存
官网:https://github.com/alibaba/jetcache/wiki/CacheAPI_CN ======================= 多层嵌套缓存无效的问题: https://github.com/alibaba/jetcache/issues/424 某个service的方法加缓存注解,然后引用同一个类的另一个加缓存注解service的方法,这样必须在类里面注入…

【Python】百度翻译的爬虫实现(前篇)
该程序只能实现中文到英文的翻译 import requestsimport jsonurl "http://fanyi.baidu.com/basetrans"query_str input("请输入要翻译成英文的内容:")data{ "query": query_str,"from": "zh","to"…

github每次推送都要输入用户名和密码
/****************************************************************************** github每次推送都要输入用户名和密码* 说明:* 今天开始使用github管理一些东西,但是每次提交都出现要输入用户名和密码,* 这简直让人…

ELASTIC SEARCH 性能调优
ELASTICSEARCH 性能调优建议 创建索引调优 1.在创建索引的使用使用批量的方式导入到ES。 2.使用多线程的方式导入数据库。 3.增加默认刷新时间。 默认的刷新时间是1秒钟,这样会产生太多小的SEGMENT,导致未来的合并压力,如果调整这个大小&…

Android开源中国客户端学习 (自定义View)左右滑动控件ScrollLayout
左右滑动的控件我们使用的也是非常多了,但是基本上都是使用的viewpager 等 android基础的控件,那么我们有么有考虑过查看他的源码进行定制呢?当然,如果你自我感觉非常好的话可以自己定制一个,osc的ScrollLayout就是自己定义的View 和Viewpager的区别还是不小的 代码不是很多不…
【Python】有道翻译的爬虫实现(前篇)
import requestsimport jsonurl "http://fanyi.youdao.com/translate_o?smartresultdict&smartresultrule"data {"i": "我喜欢学习", "from": "AUTO", "to": "AUTO", "smartresult":&q…

自动生成纯文本表格的工具
https://tableconvert.com/?outputtext 有时候需要写文档的时候生成这种纯文本表格,这个工具真的很方便,贴上数据就可以了。