本系列还是着眼于了解EOS的设计理念,力求在繁杂的版本变更,以及各种区块链技术文章当中,根据源码整理出一个清晰简明的eos实现,而暂时不着眼研究C++工程开发的奇淫巧技,主要是因为还需要时(目)间(前)继(领)续(悟)修(不)炼(到)。
正文
这个multi_index是什么呢?
在eoslib目录下有multi_index.hpp,自然而然的来看看这里面有什么线索。
在这里,找到了如下解释:
- EOSIO Multi-Index API provides a C++ interface to the EOSIO database. It is patterned after Boost Multi Index Container.
- EOSIO Multi-Index table requires exactly a uint64_t primary key. For the table to be able to retrieve the primary key,
- the object stored inside the table is required to have a const member function called primary_key() that returns uint64_t.
- EOSIO Multi-Index table also supports up to 16 secondary indices. The type of the secondary indices could be any of:
- uint64_t
- uint128_t
- uint256_t
- double
- long double
原来multi_index是跟EOSIO数据库相关的API。相对应的,官方开发者文档有DB API的描述,简要解释一下使用场景:
比如开发一个游戏DApp,用户有自己的Action(很难翻译,可以理解为执行一个函数,比如randint(6),模拟了一次掷骰子,返回结果从1至6)操作, 该游戏智能合约要记录每位用户游戏Actions,本次合约执行完毕后数据不能丢失,就需要将数据存储到 EOS 数据库中。
Action在被称为Action执行上下文的环境中运作。如下图所示,
Action上下文提供执行Action所需的几件事情。
其中一件事是Action的工作内存。这是Action执行的地方。在处理一个Action之前,EOSIO为该Action进行一次内存清理工作。在新Action的上下文中当另一个Action执行时可能已经被设置的变量不可用。在Action中传递状态的唯一方法是将其持久存储并从EOSIO数据库中检索。
声明
看到这里,感觉引入概念越来越多,暂时先搬运了一些现成资料(见参考),再后续学习中慢慢消化。
这里提到了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 的情况。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。