如果你是EOS的合约开发者,相信你很有可能跟我一样对内存(RAM)的使用量感到不解。在使用multi_index
进行数据存储时,明明只存了一点数据,但区块链浏览器中显示的内存占用量却上升了不少。在这篇文章中,我们就来对内存用量一探究竟,精确计算出存储数据所需要的RAM。我们会首先编写一个简单的合约,用以向multi_index
内存入数据。部署合约后,每次调用接口前后都查询一下RAM用量,并予以记录。
合约编写
新建合约目录:
$ mkdir addrow $ cd addrow $ touch addrow.cpp
这里是一份简单的合约,用户每次调用add
时,数据库中都会多存一行记录
#include <eosiolib/eosio.hpp> class [[eosio::contract]] addrow : public eosio::contract { public: addrow( eosio::name receiver, eosio::name code, eosio::datastream<const char*> ds ) : eosio::contract(receiver, code, ds), _students(receiver, code.value) {} //添加学生 [[eosio::action]] void add(eosio::name user) { require_auth(user); eosio::print("Add student ", user); _students.emplace(get_self(), [&](auto& p) { p.id = _students.available_primary_key(); }); } struct [[eosio::table]] students { uint64_t id; // primary key uint64_t primary_key() const { return id; } }; //数据表根据age排序 typedef eosio::multi_index<"students"_n, students > studentstable; //students数据库表 studentstable _students; }; EOSIO_DISPATCH( addrow, (add) )
使用eosiocpp
工具进行编译后,得到addrow.wasm
与addrow.abi
文件:
$ eosiocpp -o addrow.wast addrow.cpp $ eosiocpp -g addrow.abi addrow.cpp
部署合约
cleos -u https://jungle2.cryptolions.io:443 set contract zmcheng12345 /root/github.com/addrow -p zmcheng12345@active 查账户内存使用情况: cleos -u https://jungle2.cryptolions.io:443 get account zmcheng12345 -j
我查询到的数值为:
"ram_usage": 52665
这个数值可能根据你的编译工具和参数不同而有所变化。我们以查询到的数值为基础,进行后续的差值计算。
调用合约,新增一行数据:
$ cleos push action eosio.ramfee add '' -p eosio.ramfee
查询用量,得到的结果是:
"ram_usage": 52897
再新增一行并查询,这次的结果是:
"ram_usage": 53017
通过计算可以得出,新增第一行的内存占用量达到了232字节!而第二行的占用量也高达120字节!如果继续新增行数,你会发现后续的行占用量均为120字节。
也就是说,存储一个仅有一个uint64_t
字段(大小为8字节)的数据表,首行占用232字节,后续行数占用120字节。
总结
在EOS合约内使用multi_index
存储数据(不使用secondary index的情况下),需要承担以下RAM使用量:
- 首先,每新增一行记录,都会占用112字节+实际数据大小
- 如果这个数据表本身不存在,需要再额外收取112字节的RAM占用
可以看到,每行数据的overhead(112字节)还是相当大的。如果单纯地使用数据结构大小来计算,预期使用量与实际使用量的误差会非常大(本例中,首次存储8字节数据的实际用量是数据大小的29倍!)。而得知了精确的占用量算法之后,就可以更好地规划dApp所需要的内存量了。