bitshares数据库
第1章 【绪言】
1.1 编写目标
本文的目的是讲解bitshares数据库,让读者对bitshares数据库存储有清晰的认识。
1.2 术语定义
block_database:区块数据库,bitshares因为也是一条区块链,链上的数据管理是使用block_database进行读写等操作的。
object_database:见证人节点为了方便对用户提供各种服务,比如查询转账等,维护了区别于区块数据库的另一套数据库系统,称之为object_database,管理bitshares上所有业务数据,包括账户、资产、订单等等一切数据,各种数据分门别类以二进制文件存储在硬盘中,并使用一套索引系统,维护对象增删改查,以便服务于连接到见证人节点的用户。
第2章 【bitshares数据库】
2.1 block_database
bitshares整个网络上所有的区块数据最终也是要存储于硬盘中, 区块数据的管理依赖的就是 chain::block_database 模块。block_database维护了两个文件流: _blocks 存储具体区块数据, _block_num_to_pos 则是索引数据. 当我们知道了区块 id 或者区块编号时, 就可以从 _block_num_to_pos 查询出此区块在 _blocks 中的偏移和大小, 进而从 _blocks 中取出整个块数据。
读写 _blocks 文件流时会涉及到区块数据的序列化与反序列化, 这里依赖的也是 对象序列化 中介绍的 fc::raw::pack/fc::raw::unpack, 不再敖述。
而读写 _block_num_to_pos 流时关于区块索引的序列化与反序列化就简单了, 它没有使用 fc::raw::pack/fc::raw::unpack, 而是简单粗暴的强转成字符序列。
反序列过程就是拿区块 id 或者区块编号从 _block_num_to_pos 查询出此区块在 _blocks 中的偏移和大小, 然后从 _blocks 中取出整个块数据。
2.2 object_database
object_database是用于管理对象数据的,在object_database在使用中,会结合bitshares的索引系统,对对象数据进行增删改查的一系列操作。
如图中所示,双重vector构建了一个二维的index数组,在操作某一类型数据时,只要根据index类型从二维数组中找到相对应的index,就可以进行读写操作。如下图所示:
需要指出的是block_database和object_database都不是单独使用的,在bitshares中有chain::database这个类,由database这个类统一进行管理block_database和object_database。在db_init.cpp 177行initialize_indexes函数中,调用add_index方法,新建所有需要的index对象。在上面的代码,我们可以看到,index会根据space_id和type_id被分门别类存储,而space_id和type_id的定义在types.hpp中,如下所示:
这是space_id的定义
这是space_id为1,即protocol_ids时,该space_id下面的所有的type_id定义,还有space_id为implementation_ids时的一系列type_id,这里不作详细展示了。
_index二维数组就是根据在此处定义的space_id和type_id,将各种类型index存储在二维数组相对应的位置中。
在types.hpp 187行开始,进行如下定义
此处代码定义了一系列类型映射关系,定义了每一种对象的space_id,type_id是什么。
database类的open函数(db_management.cpp 136行),会依次调用block_database的open和object_database的open函数。其中object_database的open函数如下
已经初始化好的_index数组,其中每一个index对象会从数据库目录下,根据space_id和type_id,找到名字对应的文件,打开相应的文件。
索引模型:
索引结构的定义位于 index.hpp, generic_index.php 和 simple_index.hpp 这三个头文件中. 这里面核心的几个类有 index, generic_index, simple_index, 以及 base_primary_index, primary_index。
index 和 object 一样是个抽象类, 定义的是关于索引的基本操作方法并且成员基本都是纯虚成员, 所以 index 也不能被直接实例化, 需要被继承并被实例化其子类, generic_index 和 simple_index 就是继承自 index 的两个子类, 而且它俩也是模板类, 模板实例化时需以具体对象为参数, generic_index 除了对象参数, 还需要提供一个索引类型参数 MultiIndexType。
在index.hpp中,可以看到index定义了一系列基本操作函数,如 insert(), create(), find(), modify(), remove() 方法. insert() 是真正将对象 “插入” 索引数据结构的方法, 任何对象要存入索引, 最终都会经过这个 insert(); 而任何需要存入索引的对象的创建都会经过 create() 方法, create() 会用传进来的构造函数实例化对象并调用 insert(), 同时还会负责 _next_id 的自增工作; find(), modify(), remove() 则分别是在索引结构中查找, 修改, 以及删除对象。
再然后是 open(), load(), save(). 这三个方法是处理索引数据的落盘和加载的, 涉及到对象的序列化和反序列化。序列化和反序列化此处不作详细说明。
其它方法
object_database 里还提供了几个方便获取索引和对象的方法,比如 find_object 和 get_object 方便我们根据对象 id 找对象, 它们的唯一区别就是一个返回指针一个返回引用。
get_index 和 get_mutable_index 则是让我们直接获得指定 space 和 type 的索引对象, 这样我们就能遍历这个索引下所有的对象, 这俩方法的区别从名字就能看出, 一个允许你改变索引结构一个不允许改变. 从方法定义中一个带 const 一个不带 const 也印证了这一点。
第3章 【总结】
bitshares中数据库分block_database和object_database这两部分,block_database功能相对简单一些,负责区块数据的读写操作,object-database功能相对复杂一些,负责所有对象数据的管理,object_database下则是根据索引类,将对象数据分门别类进行增删改查操作。
Leave bitshares数据库 to:
Read more #bitshares posts
Best Posts From lpbottle
We have not curated any of lpbottle's posts yet. But you can encourage our curation team to review posts by visiting them regularly and by referring other readers. Because we give priority to frequently read content.