分析完leveldb以后,接下来的时间准备队tair的源码进行阅读和分析。我们刚刚分析完了leveldb而在tair中leveldb是其几大存储引擎之一,所以我们这里首先从tair对leveldb的使用和修改来窥探在这个分布式的存储引擎中是如何将area和bucket持久化到存储,并且方便bucket和area的处理的。

我们首先来看tair中key的结构,我们以大致梳理存储和查询一个KV的流程来确认key的处理。tair_client_api::put直接调用tair_client_impl::put,impl中首先会对key和value(data)进行合法性判断,然后调用get_server_id取得存储当前key的server_id后将组装好的request_put发送到服务器端。服务端接收到消息后handlePacketQueue解码然后会调用对应的process函数,这里是 request_processor::process(request_put *, ……)这个函数。这个函数会进行一些plugin和是否存储在本地等一些处理和判断,我们这里只需关注其调用到了tair_manager::put,这个函数里面对可以进行了一定的处理

data_entry mkey = key; // key merged with area
mkey.merge_area(area);
int bucket_number = get_bucket_number(key); 而
void merge_area(int _area)
{
if(has_merged) {
return;
}
if(size < ) return;
//now should check is alloc by me. I have 2 extra head.
if(m_true_size==size+)
{
m_true_data[]=(_area & 0xFF);
m_true_data[]=((_area >> ) & 0xFF);
size=m_true_size;
data=m_true_data;
}else{
//一些重新分配空间然后和if分支类似的处理
}
}

这里即将area编码后放到了key_entry的最前两位,接下来看LdbInstance::put,里面处理得到cdate、mdate、edate,然后获得一个

LdbKey ldb_key(key.get_data(), key.get_size(), bucket_number, edate);
//再看LdbKey的处理 LdbKey(const char* key_data, int32_t key_size, int32_t bucket_number, uint32_t expired_time = ) :
data_(NULL), data_size_(), alloc_(false)
{
set(key_data, key_size, bucket_number, expired_time);
} inline void set(const char* key_data, int32_t key_size, int32_t bucket_number, uint32_t expired_time)
{
if (key_data != NULL && key_size > )
{
free();
data_size_ = key_size + LDB_KEY_META_SIZE;
data_ = new char[data_size_];
build_key_meta(data_, bucket_number, expired_time);
memcpy(data_ + LDB_KEY_META_SIZE, key_data, key_size);
alloc_ = true;
}
}

        static void build_key_meta(char* buf, int32_t bucket_number, uint32_t expired_time = )
{
encode_fixed32(buf, expired_time);
encode_bucket_number(buf + LDB_EXPIRED_TIME_SIZE, bucket_number);
}

        static void encode_bucket_number(char* buf, int bucket_number)
{
for (int i = ; i < LDB_KEY_BUCKET_NUM_SIZE; ++i)//LDB_KEY_BUCKET_NUM_SIZE = 3
{
buf[LDB_KEY_BUCKET_NUM_SIZE - i - ] = (bucket_number >> (i*)) & 0xFF;
}
}

从代码可以看出,这次再往key的前面分别编码了4字节的expired_time和3字节的bucket_no,到此时整个可以的组成就变成了

expire(4B) | bucket_no(3B) | area (2B) | user_key

再往下可以看到会有db_version_care_和put的version_care的判断,这里不进行详细的描述,主要功能是如果设置的db_version_care_那么每次put的时候需要先查询是否已经有插入过数据将其取出来,在后在根据本次插入的version_care判断本次插入传入的version是否和当前数据库中存储的version是否相等,如果不等则本次插入失败,如果相等则可以插入(更新),并且将改key的version加1,进行完相应的这些设置以后就可以把对应的KV对插入leveldb中了。这里需要说明的是在tair的存储引擎中实现了一种纯内存存储的memdb,而tair的leveldb也对此进行了一些修改增加了一个db_cache的概念,这里你就可以选择将memdb作为leveldb的一个cache以加快查询的速度。

到此我们就将重新组装后的KV对插入到leveldb中了,然而你有没有发现有何问题呢?对,就是这里的key是以expire开头的,而我们回忆leveldb内部默认使用的Comparator是BytewiseComparator,如果是使用率这个Comparator的话leveldb内部就将首先按照expire的时间来排序。从数据过期的处理来说,按expire排序那么我们就可以很容易的将expire的key排出掉,但是我们知道leveldb是会进行迁移的,当我们需要将一个area或者一个bucket的数据迁移到另外的机器上时那么我们岂不是要扫描整个数据库?二期查找的时候我如何能够确定这个key的过期时间呢?如果不能确定就不能获取到相应的值了。别担心,我们的leveldb不是有Comparator这个东西么,使用默认的BytewiseComparator不能达到要求,那么tair自己实现了一个Comparator叫做LdbComparator的东西来满足我们的需要。我们看看其Compare的代码

int LdbComparatorImpl::Compare(const leveldb::Slice& a, const leveldb::Slice& b) const
{ // LDB_COMPARE_SKIP_SIZE = LDB_EXPIRED_TIME_SIZE = sizeof(uint32_t);
assert(a.size() > LDB_COMPARE_SKIP_SIZE && b.size() > LDB_COMPARE_SKIP_SIZE);
const int min_len = (a.size() < b.size()) ? a.size() - LDB_COMPARE_SKIP_SIZE :
b.size() - LDB_COMPARE_SKIP_SIZE;
int r = memcmp(a.data() + LDB_COMPARE_SKIP_SIZE, b.data() + LDB_COMPARE_SKIP_SIZE, min_len);
if (r == )
{
if (a.size() < b.size())
{
r = -;
}
else if (a.size() > b.size())
{
r = +;
}
}
return r;
}

这里compare的时候首先跳过了4个字节再进行memcmp,而我们知道4个字节正是expire的长度。

所以我们最终得出的结论是tair在使用leveldb时对key进行了重构,传入重构后的key结构为

expire(4B) | bucket_no(3B) | area (2B) | user_key

而又使用了自己实现的Comparator跳过开始的expire4个字节,这样最后就得到了整个leveldb中存储的数据就先以bucket_no排序,然后是area,最后才是安装user_key,经过这样的处理后我们以bucket进行迁移时就只需安装bucket_no生成对应的区间扫描的key就可以了。

本文介绍leveldb的key存储结构,希望以此为起点理清tair内部存储然后再以此为脉络进一步分析tair的实现。

tair源码分析——leveldb存储引擎使用的更多相关文章

  1. tair源码分析——leveldb新增的CompactRangeSelfLevel过程

    tair是一个分布式KV存储引擎,当新增机器或者有机器down掉的时候,tair的dataserver会根据ConfigServer生成的新的对照表进行数据的迁移和清理.在数据清理的过程中就用到了在t ...

  2. Mysql源码分析--csv存储引擎

    一直想分析下mysql的源码,开始的时候不知道从哪下手,先从csv的文件存储开始吧,这个还是比较简单的.我是用的是mysql5.7.16版本的源码. csv源码文件在mysql源码的mysql-5.7 ...

  3. [源码分析] Dynomite 分布式存储引擎 之 DynoJedisClient(1)

    [源码分析] Dynomite 分布式存储引擎 之 DynoJedisClient(1) 目录 [源码分析] Dynomite 分布式存储引擎 之 DynoJedisClient(1) 0x00 摘要 ...

  4. [源码分析] Dynomite 分布式存储引擎 之 DynoJedisClient(2)

    [源码分析] Dynomite 分布式存储引擎 之 DynoJedisClient(2) 目录 [源码分析] Dynomite 分布式存储引擎 之 DynoJedisClient(2) 0x00 摘要 ...

  5. Nmap源码分析(脚本引擎)

    Nmap提供了强大的脚本引擎(NSE),以支持通过Lua编程来扩展Nmap的功能.目前脚本库已经包含300多个常用的Lua脚本,辅助完成Nmap的主机发现.端口扫描.服务侦测.操作系统侦测四个基本功能 ...

  6. phpcms 源码分析七: 模板引擎实现

    这次是逆雪寒对模板引擎实现的分析: 1 /* 函数 template函数是在global.func.php 里面定义的. 在前面的phpcms 的首页 index.php 里就见到了. 用法: inc ...

  7. sqlalchemy 源码分析之create_engine引擎的创建

    引擎是sqlalchemy的核心,不管是 sql core 还是orm的使用都需要依赖引擎的创建,为此我们研究下,引擎是如何创建的. from sqlalchemy import create_eng ...

  8. 消息队列中间件 RocketMQ 源码分析 —— Message 存储

  9. spark源码分析以及优化

    第一章.spark源码分析之RDD四种依赖关系 一.RDD四种依赖关系 RDD四种依赖关系,分别是 ShuffleDependency.PrunDependency.RangeDependency和O ...

随机推荐

  1. JS魔法堂:精确判断IE的文档模式by特征嗅探

    一.前言 苦逼的前端攻城狮都深受浏览器兼容之苦,再完成每一项功能前都要左顾右盼,生怕浏览器不支持某个API,生怕原生API内含臭虫因此判断浏览器类型和版本号成了不可绕过的一道关卡,而特征嗅探是继浏览器 ...

  2. IE11之F12 Developer Tools--概述篇

    打开Developer Tools的方法: a. 点击F12 b. 在浏览器中选择Tools-->F12 Develooper Tools 打开后图示: 从上图我们可以看到,Developer ...

  3. [Solution] 简单数字识别之Tesseract

    图像识别涉及的理论:傅里叶变换,图形形态学,滤波,矩阵变换等等. Tesseract的出现为了解决在没有这些复杂的理论基础,快速识别图像的框架. 准备: 1.样本图像学习,预处理 (平均每1个元素出现 ...

  4. 在aspx怎么引用public string getPicurl(string picurl)?

    刚才在论坛上看到一帖: Insus.NET尝试做了一下,直接使用一个Img标签是无法实现.因为函数中返回的即是一个img html标签,因此在aspx页再不能使用Img了. 现在可以回到网友的问题,那 ...

  5. 使用HttpRequester模拟发送及接收Json请求

    1.开发人员在火狐浏览器里经常使用的工具有Firebug,httprequester,restclient......火狐浏览器有一些强大的插件供开发人员使用!需要的可以在附加组件中扩展. 2.htt ...

  6. 响应式布局(Responsive layout,RL)的简单Demo

          ★背景:       响应式布局是Ethan Marcotte在2010年5月份提出的一个概念,简而言之,就是一个网站能够兼容多个终端--而不是为每个终端做一个特定的版本.这个概念是为解决 ...

  7. ExtJs4 笔记(4) Ext.XTemplate 模板

    ExtJs4 笔记(4) Ext.XTemplate 模板 摘自:http://www.cnblogs.com/lipan/ 本篇将涉及到ExtJs中一个重要的概念,模板.话说Razor很神奇,但是我 ...

  8. IIS 503日志文件在哪

    概述  503:“服务不可用”错误是一个非自定义的错误,该错误表示服务器当前无法处理该请求. 可能原因:1.管理员可能关闭应用程序池以执行维护.2.当请求到达时应用程序池队列已满.3.应用程序池标识没 ...

  9. C#初入串口通信(串行通信)总结

    使用WinFrom来实现: 首先要知道串口通信协议以及原理 原理大概提一下:要自己翻阅看.(http://book.51cto.com/art/200911/162532.htm或者http://hi ...

  10. 将表数据生成Insert脚本

    set ANSI_NULLS ONset QUOTED_IDENTIFIER ONgo-- =============================================-- Author ...