EOS中用了Multi-index,这是一种区块链下的数据永久存储和查询方式,其实原理和普通的数据库中的行与列的定义本质上是一样的,虽然我们平时看到数据库是行与列,以及数据,但底层的数据存储方式,假定是采用C++的话,无非是一行一个对象,每列都是一个属性,而对象的ID,也就是数据库的primary key。
EOS之中数据存储的方式是一样的,不同的是,对于每一个object,有一个基础的index,该Index是uint64_t,其排列按升序排列,而又定义了很多不同种类的seconde_key, 每个代表行的object中有iterator, 在second index/key 中遍历查找。
最终action 的内容都需要存储在DB中,才能成为永久性存储数据,而multi-index API提供了EOS的block 和数据库的接口。

eosio::multi-index 是从boost::multi-index 继承扩展的
其定义比较奇特,hpp 的内容定义在:~/eos/libraries/chain/include/eosio/chain/multi_index_includes.hpp
而实现在另一个hpp文件中:(是不是写错文件后缀名了??)
~/eos/contracts/eosiolib/multi_index.hpp
其中需要注意的是emplace函数,find, get 等等,他们负责和chainbase 的交互。
template<typename Lambda>
const_iterator emplace( uint64_t payer, Lambda&& constructor ) {
using namespace _multi_index_detail;
eosio_assert( _code == current_receiver(), "cannot create objects in table of another contract" ); // Quick fix for mutating db using multi_index that shouldn't allow mutation. Real fix can come in RC2.
auto itm = std::make_unique<item>( this, [&]( auto& i ){
T& obj = static_cast<T&>(i);
constructor( obj );
size_t size = pack_size( obj );
//using malloc/free here potentially is not exception-safe, although WASM doesn't support exceptions
void* buffer = max_stack_buffer_size < size ? malloc(size) : alloca(size);
datastream<char*> ds( (char*)buffer, size );
ds << obj;
auto pk = obj.primary_key();
i.__primary_itr = db_store_i64( _scope, TableName, payer, pk, buffer, size );
if ( max_stack_buffer_size < size ) {
free(buffer);
}
if( pk >= _next_primary_key )
_next_primary_key = (pk >= no_available_primary_key) ? no_available_primary_key : (pk + 1);
hana::for_each( _indices, [&]( auto& idx ) {
typedef typename decltype(+hana::at_c<0>(idx))::type index_type;
i.__iters[index_type::number()] = secondary_index_db_functions<typename index_type::secondary_key_type>::db_idx_store( _scope, index_type::name(), payer, obj.primary_key(), index_type::extract_secondary_key(obj) );
});
});
const item* ptr = itm.get();
auto pk = itm->primary_key();
auto pitr = itm->__primary_itr;
_items_vector.emplace_back( std::move(itm), pk, pitr );
return {this, ptr};
}
emplace 函数所做的事情,简单总结如下:
用primary key调用db_store_i64函数将数据存储到chainbase中,如果没有second key, 创建second key. emplace 返回一个主键的迭代器。
后续在检索中继续使用了db_find_i64, 进行查询,从而完成和chainbase的交互。