本系列仍是着眼于了解EOS的设计理念,力求在繁杂的版本变动,以及各类区块链技术文章当中,根据源码整理出一个清晰简明的eos实现,而暂时不着眼研究C++工程开发的奇淫巧技,主要是由于还须要时(目)间(前)继(领)续(悟)修(不)炼(到)。git
代码连接github
在eoslib目录下有multi_index.hpp,天然而然的来看看这里面有什么线索。数据库
在这里,找到了以下解释:api
原来multi_index是跟EOSIO数据库相关的API。相对应的,官方开发者文档有DB API的描述,简要解释一下使用场景:数据结构
好比开发一个游戏DApp,用户有本身的Action(很难翻译,能够理解为执行一个函数,好比randint(6),模拟了一次掷骰子,返回结果从1至6)操做, 该游戏智能合约要记录每位用户游戏Actions,本次合约执行完毕后数据不能丢失,就须要将数据存储到 EOS 数据库中。架构
Action在被称为Action执行上下文的环境中运做。以下图所示,dom
Action上下文提供执行Action所需的几件事情。
其中一件事是Action的工做内存。这是Action执行的地方。在处理一个Action以前,EOSIO为该Action进行一次内存清理工做。在新Action的上下文中当另外一个Action执行时可能已经被设置的变量不可用。在Action中传递状态的惟一方法是将其持久存储并从EOSIO数据库中检索。ide
看到这里,感受引入概念愈来愈多,暂时先搬运了一些现成资料(见参考),再后续学习中慢慢消化。函数
这里提到了eosio::multi_index table借鉴了Boost库中的multi_index容器(Boost库中的mult_index)。能够在概念上看做传统数据库中的表格,其中行是容器中的单个对象,列是容器中对象的成员属性,而且索引经过与一个键兼容的键提供对对象的快速查找 对象成员属性。工具
传统的数据库表容许索引成为表中某些列数的用户定义函数。eosio::multi_index一样容许索引是任何用户定义的函数。但其返回值仅限于受支持的一组受限密钥类型之一。
传统数据库表一般有一个惟一的主键,它容许明确标识表中的特定行,并为表中的行提供标准排序顺序。eosio::multi_index支持相似的语义,可是该对象的主键在eosio::multi_index容器必须是惟一的无符号64位整数。eosio::multi_index中的对象容器按主键索引按无符号64位整数主键的升序排序。
智能合约没法直接操做存储在硬盘中的数据表,而是须要使用multi_index做为中间工具(或者叫容器),每一个multi_index实例都与一个特定帐户的特定数据表进行交互(取决于实例化时的参数)。EOS智能合约与EOS数据库的数据交互以下图所示。
每个multi_index都至关于传统数据库的一个数据表(table),但将传统数据库的行与列的形式改成了单纯的列。也就是说multi_index是一个线性排列的表,只有一列,每一行都只存储一个对象。可是通常来讲multi_index存储的对象都是结构体或者类,里面含有多个成员变量,因此multi_index存储数据的灵活性也是不亚于传统数据库的。
咱们使用官方的“汽车维修店”示例,咱们创建一个数据表,储存每一个汽车维修店客户的帐户名、保养时间、车辆里程。那么multi_index数据表储存的项目中,每一个都是以下的结构体:
struct service_rec { uint64_t pkey; // 主键 account_name customer; // 车主用户名 uint32_t service_date; // 维修保养时间 uint32_t odometer; // 车辆里程 }
在传统数据库中,须要创建一个 4 列的数据表,用来储存每一个用户的这个 4 个数据,而multi_index的每一个数据表只有一列,只存储每一个用户的 service_rec 整个结构体便可。下图为multi_index数据结构。
多索引迭代器(multi_index iterator),与仅提供键值(key-value)存储的其余区块链不一样,EOSIO Multi-Index表容许合约开发人员保存按照各类不一样键类型排序的对象集合,这些键类型能够从对象内的数据派生。这使得丰富的检索功能。最多能够定义16个二级索引,每一个索引都有本身的排序和检索表格内容的方式。
EOSIO多索引迭代器遵循C++迭代器通用的模式。全部迭代器都是双向常量,能够是const_iterator或const_reverse_iterator。迭代器能够取消引用以提供对多索引表中的对象的访问。
在 EOS 数据库中,能够将迭代器比喻为一个“电梯”,在整个数据表中上下穿梭。全部对数据的操做必须经过迭代器完成。典型的数据修改过程是这样的:首先使用迭代器的find()方法,在特定的索引中寻找须要的数据,好比在车主用户名索引中寻找某个用户。迭代器会移动到须要的数据对象上。而后就可使用迭代器的modify()方法修改当前迭代器对应的数据。下图为迭代器指向用户 Sue 的状况。