- 浏览: 1404088 次
- 性别:
- 来自: 火星
文章分类
最新评论
-
aidd:
内核处理time_wait状态详解 -
ahtest:
赞一下~~
一个简单的ruby Metaprogram的例子 -
itiProCareer:
简直胡说八道,误人子弟啊。。。。谁告诉你 Ruby 1.9 ...
ruby中的类变量与类实例变量 -
dear531:
还得补充一句,惊群了之后,数据打印显示,只有一个子线程继续接受 ...
linux已经不存在惊群现象 -
dear531:
我用select试验了,用的ubuntu12.10,内核3.5 ...
linux已经不存在惊群现象
阿宝同学推荐了这个东西,因此周末就大概看了下源码,这里就简要的分析下它的实现,下面是他的特性。
项目地址在这里:
http://code.google.com/p/ydb/
ydb,它是把index存储在内存中,index的内存模型就是一颗红黑树,每次通过红黑树的key得到对应硬盘上的数据文件,以及所需数据在当前文件的偏移。从而得到当前的数据。它的存储是类似log,就是将数据每次append到文件结尾。
先来看它的几个数据结构:
首先就是db,它表示了当前的一个db对象,包括了索引,以及log文件的表示。
接下来就是tree结构,它用来表示数据index的根结点。。
item表示了索引的红黑树的叶子节点:
还有一个index_item,它就是索引文件的直接映射,它和上面的item很类似就是加了校验位,以及magic,我们最终要通过这个结构来生成item。
然后是loglist,它其实是包含了log数据结构的一个数组。
然后来看log结构,它结构很简单,就是表示了一个数据文件的句柄,文件名以及大小:
然后我们主要来看ydb_open的实现,其他的方法就大概描述下:
其中第一个参数是索引以及数据文件的目录,第二个参数表示,第三个参数表示数据文件的最小值,最后一个也就是上面的事个宏。意思基本就是字面的意思。
这次我们将代码分片(省略了一些合法性判断)来看。
首先新建一个db对象,然后将传递进来的参数赋值给它,并
然后通过top_dir,来得到索引的文件名,这里可以看到索引的文件名为top_dir/index.ydb.得到文件名后,进入tree_open,将索引加载到内存中,这里可以看到传递给tree_open的logno是一个指针,这标明当它返回后会修改这个值,最终这个值会被修改为当前的写的那个数据文件的number。
最后调用loglist_open来初始化loglist,也就是数据文件的链表。
之所以需要同步索引,是为了防止ydb异常关闭而导致的数据和索引文件的不匹配。(因为这里索引文件是通过内存映射来操作的)
接下来比对索引树和数据文件,其实也就是根据数据文件链表来重新构造索引树,它是用来防止数据文件和索引树不同步。
它是通过遍历loglist,并读取每个数据文件的元数据然后来同步index树。
最后一步,也是一个校验,为了防止索引文件中的key所对应的数据文件没有被加载到loglist中。
然后来看下tree_open和log_listopen的实现:
treeopen主要任务就是初始化红黑树,然后加载索引文件到内存,其中加载索引文件到内存是通过tree_load_index来实现的
tree_load_index就不分析了,只大概说下它的步骤。
1 首先通过mmap映射index。db到内存。
2 然后通过index_item结构来存取每个索引,并进行合法性校验。
3 最后将通过校验的item插入到红黑树。
PS:这里觉得它做了两个数据结构不太好,
然后是loglist_open函数。它的主要流程是:
1 搜索top_dir目录,找到所有的数据文件。
2遍历这些文件,生成log数据结构,以文件的numbet(文件名中包含的)为索引插入到loglist中。
3 最后打开最后一个数据文件(也就是将要写的那个文件),并将它的相关属性赋值给loglist.
它的get其实很简单,就是根据传递进来的key,在红黑树中得到相应的item结构,然后通过item的logno在loglist得到对应的数据文件,然后通过offset和传递进来的buf_sz从文件中read到需要的数据。
最后来看下ydb_add的实现:
这里我们暂时略过ydb的gc处理。在ydb-add中,主要调用了db_add方法。
它的主要流程是:
1 调用loglist_append,来将数据写入到对应的log文件,并更新loglist的对应域
2 调用tree_add,来新建或者修改一个item,并加入到索引树中。
项目地址在这里:
http://code.google.com/p/ydb/
引用
# YDB is a key-value storage library.
# Simple API, only 6 methods (open, close, sync, get, set, del).
# The data files are append-only (aka: log or journal).
# As the data files are immutable it's rsync-friendly (though index is mutable, but should be reasonably small)
# Index is stored in the memory and rarely dumped to the disk.
# As the index is in memory, GET requests need at most one disk-seek.
# SET request requires at most one disk-seek to write the data. On average the cost is 0 disk seeks, due to append-only file structure and operating system write caches.
# It's spinning-disk friendly: it's optimized to take an advantage the fast sequential access to the disk.
# it's flash-disk friendly: data is never modified, so there's no need to read-clear-write flash sectors.
# Once in a while the garbage-collector is started to remove old log files to save disk space. This can slow down the db up to two times.
# Simple API, only 6 methods (open, close, sync, get, set, del).
# The data files are append-only (aka: log or journal).
# As the data files are immutable it's rsync-friendly (though index is mutable, but should be reasonably small)
# Index is stored in the memory and rarely dumped to the disk.
# As the index is in memory, GET requests need at most one disk-seek.
# SET request requires at most one disk-seek to write the data. On average the cost is 0 disk seeks, due to append-only file structure and operating system write caches.
# It's spinning-disk friendly: it's optimized to take an advantage the fast sequential access to the disk.
# it's flash-disk friendly: data is never modified, so there's no need to read-clear-write flash sectors.
# Once in a while the garbage-collector is started to remove old log files to save disk space. This can slow down the db up to two times.
ydb,它是把index存储在内存中,index的内存模型就是一颗红黑树,每次通过红黑树的key得到对应硬盘上的数据文件,以及所需数据在当前文件的偏移。从而得到当前的数据。它的存储是类似log,就是将数据每次append到文件结尾。
先来看它的几个数据结构:
首先就是db,它表示了当前的一个db对象,包括了索引,以及log文件的表示。
struct db { u32 magic; ///这个就是你的数据以及index的存储文件的目录。 char *top_dir; ///tree用来表示index。 struct tree tree; ///保存了当前的所有的数据文件的信息。 struct loglist loglist; /// int overcommit_ratio; int flags; ///下面这几个是用来gc。 int gc_enabled; int gc_running; int gc_finished; pthread_t gc_thread; pthread_mutex_t lock; /**/ };
接下来就是tree结构,它用来表示数据index的根结点。。
struct tree { char *fname; /* Base name of index file. */ ///这里是为了防止索引文件被破坏,因此会有个备份。 char *fname_new; /* Name of new index file, *.new */ char *fname_old; /* Name of previous index, *.old */ ///红黑树的根结点。 struct rb_root root; ///表示最后一次提交,也就是写入的数据文件的number以及在当前文件的偏移。 int commited_last_record_logno; u64 commited_last_record_offset; ///和上面的区别就是这里的两个值是没有提交的。 int last_record_logno; u64 last_record_offset; ///key的个数和大小。 u64 key_counter; u64 key_bytes; /* used to store keys (incl header and padding) */ u64 value_bytes; /* used to store values (incl header and padding) */ ///这个域包含了每个数据文件的引用计数(也就是每个数据文件所包含的元数据的个数) r_arr refcnt; };
item表示了索引的红黑树的叶子节点:
struct item { struct rb_node node; ///当前key对应的数据所在的文件的number。 int logno; ///当前key所对应的数据所在的文件的偏移 u64 value_offset; ///数据的大小。 u32 value_sz; ///key的大小以及key的值。 u16 key_sz; char key[]; };
还有一个index_item,它就是索引文件的直接映射,它和上面的item很类似就是加了校验位,以及magic,我们最终要通过这个结构来生成item。
struct index_item{ u32 magic; u32 checksum; int logno; u64 value_offset; u16 key_sz; u32 value_sz; char key[]; };
然后是loglist,它其实是包含了log数据结构的一个数组。
struct loglist { ///这里包含了log(也就是数据文件)的数组。 r_arr logs; char *top_dir; char *unlink_base; ///当前最新的一个文件的number,以及fd。(由于ydb的结构类似log,因此每次写都是写在最新的那个文件) int write_logno; int write_fd; u64 min_log_size; u64 total_bytes; /* total size of all logs */ u64 appended_bytes; }
然后来看log结构,它结构很简单,就是表示了一个数据文件的句柄,文件名以及大小:
struct log { int fd; char *fname; u64 file_size; };
然后我们主要来看ydb_open的实现,其他的方法就大概描述下:
#define YDB_CREAT (0x01) #define YDB_RDONLY (0x02) #define YDB_GCDISABLE (0x04) YDB ydb_open(char *top_dir, int overcommit_ratio, unsigned long long min_log_size, int flags)
其中第一个参数是索引以及数据文件的目录,第二个参数表示,第三个参数表示数据文件的最小值,最后一个也就是上面的事个宏。意思基本就是字面的意思。
这次我们将代码分片(省略了一些合法性判断)来看。
首先新建一个db对象,然后将传递进来的参数赋值给它,并
struct db *db = (struct db *)zmalloc(sizeof(struct db)); db->magic = YDB_STRUCT_MAGIC; db->top_dir = strdup(top_dir); db->overcommit_ratio = overcommit_ratio; db->lock = (pthread_mutex_t)PTHREAD_MUTEX_INITIALIZER; db->flags = flags;
然后通过top_dir,来得到索引的文件名,这里可以看到索引的文件名为top_dir/index.ydb.得到文件名后,进入tree_open,将索引加载到内存中,这里可以看到传递给tree_open的logno是一个指针,这标明当它返回后会修改这个值,最终这个值会被修改为当前的写的那个数据文件的number。
最后调用loglist_open来初始化loglist,也就是数据文件的链表。
char buf[256]; snprintf(buf, sizeof(buf), "%s%s%s", top_dir, PATH_DELIMITER, "index.ydb"); int logno = 0; u64 record_offset = 0; if(tree_open(&db->tree, buf, &logno, &record_offset, flags) < 0) { if(!(flags & YDB_CREAT)) { log_error("Failed to load index file from %s", top_dir); /* TODO: memleaks here */ return(NULL); } } int r = loglist_open(&db->loglist, top_dir, min_log_size, max_descriptors()); if(r < 0){ /* TODO: memleaks here */ return(NULL); }
之所以需要同步索引,是为了防止ydb异常关闭而导致的数据和索引文件的不匹配。(因为这里索引文件是通过内存映射来操作的)
接下来比对索引树和数据文件,其实也就是根据数据文件链表来重新构造索引树,它是用来防止数据文件和索引树不同步。
它是通过遍历loglist,并读取每个数据文件的元数据然后来同步index树。
int record_sz = END_OF_FILE; while(1) { if(record_sz == END_OF_FILE) { /* find an edge */ ///得到当前的数据文件。 struct log *log = slot_get(&db->loglist, logno); 。。。。。。。。。。。。。。。。。。。。。。。。。。。。。 char key[MAX_KEY_SIZE]; u16 key_sz = MAX_KEY_SIZE-1; int flags; u64 value_offset; u32 value_sz; ///其中flags是元数据的属性,因为在ydb中的删除是标记删除的,因此当flags为FLAG_DELETE时,我们需要从索引树中删除这条索引。 record_sz = loglist_get_record(&db->loglist, logno, record_offset, key, &key_sz, &value_offset, &value_sz, &flags); ///这个表示一个数据文件已经遍历结束,该遍历下一个文件。 if(record_sz == END_OF_FILE) { logno++; record_offset = 0; continue; } ///文件遍历完毕。 if(record_sz == NO_MORE_DATA) break; if(FLAG_DELETE & flags) { ///删除索引 tree_del(&db->tree, key, key_sz, logno, record_offset); }else{ tree_add(&db->tree, key, key_sz, logno, value_offset, value_sz, record_offset); } record_offset += record_sz; }
最后一步,也是一个校验,为了防止索引文件中的key所对应的数据文件没有被加载到loglist中。
int start = MIN(rarr_min(db->tree.refcnt), rarr_min(db->loglist.logs)); int stop = MAX(rarr_max(db->tree.refcnt), rarr_max(db->loglist.logs)); for(logno=start; logno<stop; logno++) { ///得到当前数据的引用计数 uint refcnt = refcnt_get(&db->tree, logno); ///表示当前的数据文件。 struct log *log = slot_get(&db->loglist, logno); if(refcnt == 0 && log == NULL) continue; if(refcnt && log) continue; if(refcnt) { log_error("Log %i(0x%x) used, but file is not loaded!", logno, logno); log_error("Sorry to say but you'd lost %i key-values.", refcnt); log_error("Closing db"); /* we're in inconsistent state */ db_close(db, 0); return(NULL); } if(log) continue; } return db; }
然后来看下tree_open和log_listopen的实现:
treeopen主要任务就是初始化红黑树,然后加载索引文件到内存,其中加载索引文件到内存是通过tree_load_index来实现的
tree_load_index就不分析了,只大概说下它的步骤。
1 首先通过mmap映射index。db到内存。
2 然后通过index_item结构来存取每个索引,并进行合法性校验。
3 最后将通过校验的item插入到红黑树。
PS:这里觉得它做了两个数据结构不太好,
int tree_open(struct tree *tree, char *fname, int *last_record_logno, u64 *last_record_offset, int flags) { char buf[256]; tree->fname = strdup(fname); tree->refcnt = rarr_new(); ///防止索引文件丢失。进行备份。 snprintf(buf, sizeof(buf), "%s.old", fname); tree->fname_old = strdup(buf); snprintf(buf, sizeof(buf), "%s.new", fname); tree->fname_new = strdup(buf); ///红黑树的根结点赋值。 tree->root = RB_ROOT; ///加载索引文件到红黑树 return tree_load_index(tree, last_record_logno, last_record_offset, flags); }
然后是loglist_open函数。它的主要流程是:
1 搜索top_dir目录,找到所有的数据文件。
2遍历这些文件,生成log数据结构,以文件的numbet(文件名中包含的)为索引插入到loglist中。
3 最后打开最后一个数据文件(也就是将要写的那个文件),并将它的相关属性赋值给loglist.
int loglist_open(struct loglist *llist, char *top_dir, u64 min_log_size, int max_descriptors) { llist->min_log_size = min_log_size; llist->logs = rarr_new(); llist->top_dir = strdup(top_dir); char unlink_base[256]; snprintf(unlink_base, sizeof(unlink_base), "%s%s%s%s.old", top_dir, PATH_DELIMITER, DATA_FNAME, DATA_EXT); llist->unlink_base = strdup(unlink_base); .................................... /* Load data files */ ///系统调用,用来搜索满足glob_str模式的文件。 glob(glob_str, 0, NULL, &globbuf); for(off=globbuf.gl_pathv; off && *off; off++) { ///通过文件名得到logno int logno = logno_from_fname(*off, prefix_len, suffix_len); log_info("Opening log: %s (%04i)", *off, logno); ///生成log,并插入到数组。 if(log_open(llist, logno) < 0) { log_error("Unable to open log %5i/0x%04x", logno, logno); continue; } max_logno = MAX(max_logno, logno); } globfree(&globbuf); if(max_logno < 0) { /* empty directory yet*/ if(log_create(llist, 0) < 0) return(-1); max_logno = 0; } ///打开writer。 if(log_open_writer(llist, max_logno) < 0) return(-1); return(0); }
它的get其实很简单,就是根据传递进来的key,在红黑树中得到相应的item结构,然后通过item的logno在loglist得到对应的数据文件,然后通过offset和传递进来的buf_sz从文件中read到需要的数据。
/* Retrieve a value for selected key */ int ydb_get(YDB ydb, char *key, unsigned short key_sz, char *buf, unsigned int buf_sz);
最后来看下ydb_add的实现:
/* Add/modify a key */ int ydb_add(YDB ydb, char *key, unsigned short key_sz, char *value, unsigned int value_sz);
这里我们暂时略过ydb的gc处理。在ydb-add中,主要调用了db_add方法。
它的主要流程是:
1 调用loglist_append,来将数据写入到对应的log文件,并更新loglist的对应域
2 调用tree_add,来新建或者修改一个item,并加入到索引树中。
int db_add(struct db *db, char *key, u16 key_sz, char *value, u32 value_sz) { /* TODO: error handling on write? */ struct append_info af; af = loglist_append(&db->loglist, key, key_sz, value, value_sz, FLAG_SET); tree_add(&db->tree, key, key_sz, af.logno, af.value_offset, value_sz, af.record_offset); return 1; }
发表评论
-
gcc的几个自动优化
2009-11-10 00:44 5155我的gcc版本是4.4.1 先来看const和define以 ... -
gdb学习笔记(一)
2009-10-17 14:11 11766这里只是一个摘要。具体的细节还需要去看manual。 1 ... -
glibc中strlen的实现
2009-08-04 09:10 4534glibc中的strlen的实现主要的思想就是每次检测4个字节 ... -
libevent源码浅析(四)
2009-05-15 23:02 4434最近刚刚一个项目自己用libevent,因此这几天又把libe ... -
libevent源码浅析(三)
2009-03-17 00:08 4602这次我们来看libevent的信号的处理。 在libeven ... -
libevent源码浅析(二)
2009-02-22 00:11 4083我们来看下libevent的定时器的实现 在libevent ... -
libevent源码浅析(一)
2009-02-14 13:23 7450这里分析的是libevent-1.4.9。 PS:前面还看了 ... -
linux下的time处理
2009-01-04 18:02 6863在内核中有3个不同的时间: Wall time(real t ... -
libev简单使用介绍
2008-12-30 09:52 11581更详细的用法请看他的 ... -
linux下的elf结构
2008-12-12 00:20 5200可以看到链接器和加载器看待elf是完全不同的,链接器看到 ... -
php的c扩展
2008-12-07 18:24 4597在php中最核心的一个数据结构就是这个: typedef u ... -
linux下的管理内存相关的函数
2008-11-27 00:56 4497malloc的实现,在linux下的实现是这样的,当所需 ... -
linux下的数据对齐
2008-11-25 12:15 3660数据对齐也就是通过硬件来估算在数据的地址和内存块之间的联系。当 ... -
linux下检测ip冲突
2008-11-16 20:18 8200原理其实很简单,那就是广播一个arp包,然后recv,如果没有 ... -
今天碰到的一个问题
2008-10-29 22:33 1295将位图用 bmptopnm 转成pcl6的打印语言,然后直接c ... -
ftruncate和msync
2008-10-23 22:10 3523int ftruncate(int fd, off_t le ... -
GUN C正则表达式
2008-09-25 23:47 6198最近项目中要处理文本,因此就用了gun的正则表达式,它是pos ... -
看代码看的头晕
2008-09-06 01:04 1890最近工作需要在看ghostscript的代码,看得我头晕眼花, ... -
[转帖]MISRA--作为工业标准的C编程规范
2008-08-21 13:19 2841本文档转贴自孟岩的blog ... -
代码大全读书笔记1
2008-04-26 19:16 3830这么好的书,觉得写点东西,记录一下比较好。 4.1选择编程语 ...
相关推荐
在数据存储层,YDB支持多种数据模型,如键值对、列族和文档型,适应不同的业务场景。计算层则采用了并行计算技术,如MapReduce或Spark,提高了数据处理效率。管理层则负责元数据管理和资源调度,确保系统的稳定运行...
YDB全称延云YDB,是一个基于Hadoop分布式架构下的实时的、多维的、交互式的查询、统计、分析引擎,具有万亿数据规模下的秒级性能表现,并具备企业级的稳定可靠表现。 YDB是一个细粒度的索引,精确粒度的索引。数据...
### 延云YDB:万亿数据秒级查询与分析引擎 #### 一、概述 随着信息技术的飞速发展,大数据已经成为推动企业决策、产品创新和业务增长的关键力量。面对日益增长的数据规模和复杂的数据结构,传统的数据分析工具已经...
ydb也可以称为一个在线调试工具,什么叫在线调试?就是在线上生产环境进行调试,假设有一天某个用户报某个页面某个数据怎么不对啊,看来线上出BUG了,于是你要迅速找出原因,首先看日志,可是悲剧的没有足够的...
YDB 199-2018 移动互联网+智能家居系统总体要求.pdf
YDB 101-2012 物联网安全需求
YDB144-2014云计算服务协议参考框架
延云YDB安装与使用说明书 超千亿规模的数据,数据库根本就运行不了,怎么办? 数据从产生到能够查询,要延迟一天才能看到,如何能做到分钟级延迟? 50台规模的hadoop集群,几亿条数据,一个MR任务要运行几小时,每天...
YDB是我们自主研发的一个大型分布式索引系统。旨在为数据总量为万亿级别、每天千亿级别数据增量的项目提供近似实时的数据导入,并提供近似实时响应的多维查询与统计服务。 大索引技术 为什么要使用大索引?使用后会...
php在线调试工具,一个php扩展,适用于php5.2版本 md5: 638e7f30d2f4d25b5ce5128e39e597bf 具体文档介绍见 http://blog.csdn.net/micweaver/article/details/17891163
YDB 165-2016 面向物联网的蜂窝窄带接入(NB-IoT) 无线网总体技术要求 通信行业的参考标准,规范了通信物联网的蜂窝窄带接入(NB-IOT)网络的架构,规定了物理层,高层,及PRC层的技术要求及移动管理性,本标准使用...
本资料“Java ORM框架和迁移工具的官方YDB方言”可能是关于如何在Java ORM工具中集成和使用YDB特性的详细指南。 1. **Java ORM框架介绍** - Hibernate:是最流行的ORM框架之一,提供了一种在Java应用程序中操作...
《YDB编程指南》是一本专注于YDB编程的最新版参考书籍,旨在帮助开发者深入了解和掌握YDB这一特定编程语言的使用。YDB可能是一种专为处理大规模数据设计的数据库系统,因此它与大数据处理紧密相关。以下是根据提供的...
###yDB:基于 RTM 的高性能、可扩展的内存键值存储团队成员:Zhiyuan Yang、Zhizhou Yang ###SUMMARY 我们基于英特尔 RTM(受限事务内存)和乐观并发控制 [2] 实现了一个名为 yDB 的内存键值存储。 yDB 在多核机器...
如何使用 安装依赖和编译库 npm ci npm run build:lib 设置环境变量 不应为特定安装设置的变量显式设置为空值,以便启动脚本正确检测您所针对的安装。 常见变量 export YDB_SDK_LOGLEVEL=debug ...export YDB_METADAT
延云YDB安装与使用说明书 超千亿规模的数据,数据库根本就运行不了,怎么办? 数据从产生到能够查询,要延迟一天才能看到,如何能做到分钟级延迟? 50台规模的hadoop集群,几亿条数据,一个MR任务要运行几小时,...