Hash

hash可以算是一种两级kv,首先通过key找到一个hash对象,然后再通过field找到或者设置相应的值。 在ledisdb里面,我们需要将key跟field关联成一个key,用来存放或者获取对应的值,也就是key:field这种格式。 这样我们就将两级的kv获取转换成了一次kv操作。 另外,对于hash来说,(后面的list以及zset也一样),我们需要快速的知道它的size,所以我们需要在leveldb里面用另一个key来实时的记录该hash的size。 hash还必须提供keys,values等遍历操作,因为leveldb里面的key默认是按照内存字节升序进行排列的,所以我们只需要找到该hash在leveldb里面的最小key以及最大key,就可以轻松的遍历出来。在前面我们看到,我们采用的是key:field的方式来存入leveldb的,那么对于该hash来说,它的最小key就是"key:",而最大key则是"key;",所以该hash的field一定在"(key:, key;)"这个区间范围。至于为什么是“;”,因为它比":"大1。所以"key:field"一定小于"key;"。后续zset的遍历也采用的是该种方式,就不在说明了。

List

list只支持从两端push,pop数据,而不支持中间的insert,这样主要是为了简单。我们使用key:sequence的方式来存放list实际的值。 sequence是一个int整形,一个list最多存放1<<31 - 2000条数据,至于为啥是1000,我说随便定得你信不? 对于一个list来说,我们会记录head seq以及tail seq,用来获取当前list开头和结尾的数据。 当第一次push一个list的时候,我们将head seq以及tail seq都设置为listInitialSeq。当lpush一个value的时候,我们会获取当前的head seq,然后将其减1,新得到的head seq存放对应的value。而对于rpush,则是tail seq + 1。 当lpop的时候,我们会获取当前的head seq,然后将其加1,同时删除以前head seq对应的值。而对于rpop,则是tail seq - 1。 我们在list里面一个meta key来存放该list对应的head seq,tail seq以及size信息。

ZSet

zset可以算是最为复杂的,我们需要使用三套key来实现。需要用一个key来存储zset的size,需要用一个key:member来存储对应的score,需要用一个key:score:member来实现按照score的排序。

这里重点说一下score,在redis里面,score是一个double类型的,但是我们决定在ledisdb里面只使用int64类型,原因一是double还是有浮点精度问题,在不同机器上面可能会有误差(没准是我想多了),另一个则是我不确定double的8字节memcmp是不是也跟实际比较结果一样(没准也是我想多了),其实更可能的原因在于我们觉得int64就够用了,实际上我们项目也只使用了int的score。 因为score是int64的,我们需要将其转成大端序存储(好吧,我假设大家都是小端序的机器),这样通过memcmp比较才会有正确的结果。同时int64有正负的区别,负数最高位为1,所以如果只是单纯的进行binary比较,那么负数一定比正数大,这个我们通过在构建key的时候负数前面加"<",而正数(包括0)加"="来解决。所以我们score这套key的格式就是这样: key<score:member //<0key=score:member //>=0 对于zset的range处理,其实就是确定某一个区间之后通过leveldb iterator进行遍历获取,这里我们需要明确知道的事情是leveldb的iterator正向遍历的速度和逆向遍历的速度完全不在一个数量级上面,正向遍历快太多了,所以最好别去使用zset里面带有rev前缀的函数。

总结

总的来说,用leveldb来实现redis那些高级的数据结构还算是比较简单的,同时根据我们的压力测试,发现性能还能接受,除了zset的rev相关函数,其余的都能够跟redis保持在同一个数量级上面,具体可以参考ledisdb里面的性能测试报告以及运行ledis-benchmark自己测试。 后续ledisdb还会持续进行性能优化,同时提供expire以及replication功能的支持,预计6月份我们就会实现。 ledisdb的代码在这里https://github.com/siddontang/ledisdb,希望感兴趣的童鞋共同参与。

摘自:http://www.itkeyword.com/doc/0652992288452803948

ledisDB底层实现——本质上就是用leveldb这样的底层存储,和ssdb一样,meta里存的是hash、list等的元数据的更多相关文章

  1. parquet文件格式——本质上是将多个rows作为一个chunk,同一个chunk里每一个单独的column使用列存储格式,这样获取某一row数据时候不需要跨机器获取

    Parquet是Twitter贡献给开源社区的一个列数据存储格式,采用和Dremel相同的文件存储算法,支持树形结构存储和基于列的访问.Cloudera Impala也将使用Parquet作为底层的存 ...

  2. ES transport client底层是netty实现,netty本质上是异步方式,但是netty自身可以使用sync或者await(future超时机制)来实现类似同步调用!因此,ES transport client可以同步调用也可以异步(不过底层的socket必然是异步实现)

    ES transport client底层是netty实现,netty本质上是异步方式,但是netty自身可以使用sync或者await(future超时机制)来实现类似同步调用! 因此,ES tra ...

  3. GET和POST本质上有什么区别,这才是标准答案

    不知道各位读者在面试的时候,有没有被问过这个问题:"请说一下GET和POST两者的本质区别".基本上做过WEB开发的,对这个问题,都可以回答出一堆的区别. 比如: 最直接的区别,G ...

  4. ReactiveCocoa 中 RACSignal 所有变换操作底层实现分析(上)

    前言 在上篇文章中,详细分析了RACSignal是创建和订阅的详细过程.看到底层源码实现后,就能发现,ReactiveCocoa这个FRP的库,实现响应式(RP)是用Block闭包来实现的,而并不是用 ...

  5. Jsp与servlet本质上的区别

    1.jsp经编译后就变成了Servlet.(JSP的本质就是Servlet,JVM只能识别java的类,不能识别JSP的代码,Web容器将JSP的代码编译成JVM能够识别的java类)2.jsp更擅长 ...

  6. jQuery的$.ajax方法响应数据类型有哪几种?本质上原生ajax响应数据格式有哪几种,分别对应哪个属性?

    jQuery的$.ajax方法响应数据类型有:xml.html.script.json.jsonp.text 本质上原生ajax响应数据格式只有2种:xml和text,分别对应xhr.response ...

  7. 使用深度学习检测TOR流量——本质上是在利用报文的时序信息、传输速率建模

    from:https://www.jiqizhixin.com/articles/2018-08-11-11 可以通过分析流量包来检测TOR流量.这项分析可以在TOR 节点上进行,也可以在客户端和入口 ...

  8. 利用CNN进行流量识别 本质上就是将流量视作一个图像

    from:https://netsec2018.files.wordpress.com/2017/12/e6b7b1e5baa6e5ada6e4b9a0e59ca8e7bd91e7bb9ce5ae89 ...

  9. QTime的本质上是一个int,QDateTime本质上是一个qint64

    研究这个问题的起因发现使用<=比较时间的不准确,所以怀疑是一个浮点数(Delphi里的time就是一个浮点数).结果却发现是一个int class Q_CORE_EXPORT QTime { e ...

随机推荐

  1. 01--SQLite学习网站参考

    1.   SQLite基本操作 见我的02—SQLite一步到位 sqlite3编程笔记 . http://blog.csdn.net/wl_haanel/article/details/623141 ...

  2. html5——多媒体(二)

    基本方法 load() //重新加载视频 play() //播放 pause() //暂停 基本属性 currentTime //视频播放的当前进度. duration //视频的总时间 paused ...

  3. [Windows Server 2003] 还原SQL Server数据库

    ★ 欢迎来到[护卫神·V课堂],网站地址:http://v.huweishen.com★ 护卫神·V课堂 是护卫神旗下专业提供服务器教学视频的网站,每周更新视频.★ 本节我们将带领大家:SQL Ser ...

  4. Prime算法生成最小生成树

    虽说是生成树,但我只将生成的边输出了.至于怎么用这些边来创建树...我不知道_(:з」∠)_ //Prime方法生成最小生成树 void GraphAdjacencyListWeight::Gener ...

  5. Python标准库sys

    1.命令行参数sys.argv 我们从Python语言之模块第一部分的例子开始,看看sys.argv中到底存了些什么内容. #Filename: using_sys.py import sys i=0 ...

  6. HDU_1113_字符串处理

    Word Amalgamation Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others ...

  7. Eclipse中自动生成get/set时携带注释给get/set

    Eclipse中自动生成get/set时携带注释给get/set   编码的时候通常要用到 JavaBean ,而在我们经常把注释写在字段上面,但生成的Get/Set方法不会生成,通过修改Eclips ...

  8. Cashier (codeforces 1059A)

    题目倒是不难注意第一个时间段可能不是从零开始的,所以注意第一个时间的开始节点与零之间可能存在休息的时间 还有这个题我打的时候一直谜之RE......发现原来bool函数忘记写return了.....以 ...

  9. LINQ简记(1):基本语法

    关于LINQ(语言集成查询)是.NET 3.5和Visual Studio 2008以上版本中引入的一种有趣的全新概念,语言版本有VB和C#,由于C#与.NET平台结合最为紧密,也是MS当初首推的语言 ...

  10. DFA最小化实例

    原始DFA如下图所示 最小化的定义:1.没有多余的状态(死状态):2.没有两个状态是相互等价的: 两个状态等价的含义:1.兼容性(一致性)——同是终态或同是非终态:2.传播性(蔓延性)——从s出发读入 ...