LOADING...
LOADING...
LOADING...
当前位置: 玩币族首页 > 新闻观点 > Achain星系研究院:Achain2.0chainbase解析

Achain星系研究院:Achain2.0chainbase解析

2020-04-17 Achain 来源:区块链网络

Achain2.0的?数据存储库是一个事务型数据库,它支持多表多索引,支持状态持久化,还支持嵌套式写事务,支持回滚,支持多线程操作,正是因为这些特征让他能够满足区块的存储需求。

database的具体实现源码在目录?\libraries\chainbase中的chainbase.hpp,chainbase.cpp。database的底层技术实现是通过boost库中的?boost::multi_index_container 和?boost::interprocess::managed_mapped_file 实现的。这其中multi_index_container 用来为容器实现增删改查,而managed_mapped_file用来实现内存映射文件shared_memory.bin 并为multi_index_container 提供内存分配子allocator(具体技术可以参阅boost库)。

先来看看libraries\chainbase\include\chainbase\chainbase.hpp头文件中具体的方法接口,为方便理解,直接在头文件上加入注释。

class database {

public:

//...

// db 复制构造函数,启动入口

database(const bfs::path& dir, open_flags write = read_only, uint64_t shared_file_size = 0, bool allow_dirty = false);

// db析构函数,解除该database的文件映射,关闭文件;

~database();

// 同步mmap的内存到文件(msync)

void flush();

// 设置标识符,在一些操作时,是否需要锁

void set_require_locking( bool enable_require_locking );

//...

// session是abstract_session的一个容器

// 用来存储实际的undo会话(事务)

struct session{ //... }

// 启动一个undo会话(事务),如果enabled=true,实际启动一个undo会话,

// 否则没有任何影响,返回一个空session

session start_undo_session( bool enabled );

// 获取最小的undo索引的变更号

// 与事务相关

int64_t revision()const{ //... }

// 对database中所有undo会话执行undo命令

// 与事务相关

void undo();

// 合并临近的变更

// 与事务相关

void squash();

// 对database中所有undo会话执行提交命令

// 与事务相关

void commit( int64_t revision );

// 对database中所有undo会话执行undo_all命令

// 与事务相关

void undo_all();

// 对database中所有undo会话执行set_revision命令

// 与事务相关

void set_revision( uint64_t revision ) { //... }

// 增加一张表,注册MultiIndexType类型的index

template<typename MultiIndexType>

void add_index() {

//...

}

// 获取mmap内存管理器的handle

auto get_segment_manager() -> decltype( ((bip::managed_mapped_file*)nullptr)->get_segment_manager()) {

return _segment->get_segment_manager();

}

auto get_segment_manager()const -> std::add_const_t< decltype( ((bip::managed_mapped_file*)nullptr)->get_segment_manager() ) > {

return _segment->get_segment_manager();

}

// 获取剩余内存

size_t get_free_memory()const{ //... }

// 获取执行MultiIndexType类型的index,不可修改

template<typename MultiIndexType>

const generic_index<MultiIndexType>& get_index()const{ //... }

template<typename MultiIndexType, typename ByIndex>

auto get_index()const -> decltype( ((generic_index<MultiIndexType>*)( nullptr ))->indices().template get<ByIndex>() ) { //... }

template<typename MultiIndexType>

generic_index<MultiIndexType>& get_mutable_index() { //... }

// 根据索引tag来查找响应内部对象,CompatibleKey是查找时的比较器。

// 其会根据绑定关系,从ObjectType得到MultiIndexType,然后在该表下获取指定tag的的索引,

// 然后调用索引的find方法进行查找。未找到时返回nullptr。

// 索引的find方法是一个模板,支持多种查找方法,CompatibleKey可以是一个具体的类型,也

// 可以是一个仿函数。

template< typename ObjectType, typename IndexedByType, typename CompatibleKey >

const ObjectType* find( CompatibleKey&& key )const { //... }

template< typename ObjectType >

const ObjectType* find( oid< ObjectType > key = oid< ObjectType >() ) const

private:

unique_ptr<bip::managed_mapped_file> _segment;

unique_ptr<bip::managed_mapped_file> _meta;

read_write_mutex_manager* _rw_manager = nullptr;

};

在具体业务逻辑中,通过调用?database的复制构造函数来实现db实例化,

chainbase::database old_reversible( backup_dir, database::read_only, 0, true );

chainbase::database new_reversible( reversible_dir, database::read_write, cache_size );

具体后台处理逻辑如下:

1. 创建database数据目录,在目录中创建数据库持久化文件shared_memory.bin以及shared_memory.meta,并将shared_memory.bin文件扩容到指定大小?(可以使用配置项中的shared-file-size进行配置)

2. 将该bin文件放到boost::interprocess::managed_mapped_file来管理,由成员变量?_segment负责维护。将meta文件用另?一个相同的成员变量_meta来管理。当在?_segment 上面构造好?generic_index 类型对象之后,返回的指针要存储起来,以方便对?_segment 管理内存上面构造的?generic_index 类型对象进行操作。?database::_index_map 成员变量主要用来存储此指针。

3. 然后存储一个environment_check 用以记录该数据库生成的环境,用以启动时校验。Meta文件用来管理数据库的读写锁资源。

而其中database::add_index 在内存映射文件上面创建?generic_index 类型对象,此对象的成员变量?_indices 为?multi_index_container。在该函数中,在generic_index实现了很多index的基本操作,该函数会将generic_index<MultiIndexType>再用index 包装成index<generic_index<MultiIndexType>>,然后存储在chainbase的稀疏map中。此外通过generic_index<MultiIndexType> 也实现了对写操作事务性的支持。在generic_index<MultiIndexType>存储着2个成员:

1. _revision事务号,每开启一个新的事务该值加1,然后分配给新开启的事务

2. _next_id主键id,每创建一个ObjectType该值加1,然后分配给新创建的对象

通过调用start_undo_session 开启undo事务,该方法会调用database中每一个表的generic_index<MultiIndexType>::start_undo_session 方法,该方法创建一个新的session,同时在generic_index<MultiIndexType>内部的undo_state栈?上压入一个对象。undo_state 用以记录在该session被创建后数据的变更情况。

—-

编译者/作者:Achain

玩币族申明:玩币族作为开放的资讯翻译/分享平台,所提供的所有资讯仅代表作者个人观点,与玩币族平台立场无关,且不构成任何投资理财建议。文章版权归原作者所有。

LOADING...
LOADING...