sharedata 的替代品:datasheet
skynet 中有一个用来在多个服务间共享数据表的模块,叫做 sharedata 。
它的设计动机是:当我们有很多服务时,如果需要共享一份只读的数据表,把数据表分别在每个服务类加载会很浪费内存。而且,一旦数据表有热更新的需求,分散在多个服务中的数据更新起来会比较麻烦。
我试过很多方案来达成这个需求,一直都不是特别满意。目前的 sharedata 模块是用的最久、使用项目最多的一个。虽然它基本可用,但使用它的同学也提出了一些问题,我对这些问题做了一些思考。
首先、它其实并不能完全共享一块内存。我们很难做到像 C 数据结构一样,一份只读的数据结构让多个读取者持有指针,完全共享同一块内存。因为对于树结构的数据,如果期望让 lua 用原生的语法来访问,就需要把子结构的指针用 lua userdata 封装起来,这个结构本身是属于 lua vm 的,每个 vm 必须独立,不能共享。它是需要额外占用内存的。
如果把这个结构用 lightuserdata 封装,一是元表的问题很难解决的很好(不同的 lightuserdata 必须共享元表),二是 lightuserdata 无法做热更新:你很难(并非完全不行)通过遍历 lua vm 更新它。
而从节约内存的角度看这个模块的用途,它更多的是做到部分加载:策划会把很多数据打包到一个数据表里,但是一个服务在一段时间只会用到很少的一部分。把整个数据表加载到服务的 lua vm 内是很浪费内存的。
访问速度也是可能成为问题:lua 访问纯 table 的内容还是很快的,如果是 userdata 或带 metatable 的 table 的话,由于要至少多出一次函数调用,性能就大打折扣。sharedata 做了一定的 cache ,我们有一个项目在使用的时候更是自己额外多做了一层 cache ,为了配合 cache 工作,我还特定添加了 deepcopy 方法,把 sharedata 的子树复制成普通的 lua 表。这也可以看出,完全共享内存而减少内存消耗的需求并不那么强烈。
sharedata 在实现的时候,把数据热更新、副本引用管理的部分写在 C 层次,给实现也带来了不必要的复杂度。理论上热更新时,时间效率是很次要的,我们只需要保证正确即可,多长时间可以更新好并不太重要。
我在综合以上重新考虑后,最近重新实现了一个替代模块,暂命名为 datasheet 。它能做的是:把一个复杂的有一定限制的 lua 表,转换为一块 C 内存,由多个 lua 服务共享读取。
这里的一定限制指:表项的 key 只能是字符串,或是正的连续整数(数组)。加上这个限制可以简化实现。
它和 sharedata 的实现有所不同:它仅在第一次访问一个子表时,利用元方法触发,将子表的第一层完全复制到当前的 lua vm 中;如果这一层下还有子表,则只创建一个空表,附上元方法,惰性展开。展开后,它就变成了 lua 中一个普通 table ,原表也已去掉,访问操作完全没有效率损失。
从 lua 表转换为 C 内存块的过程交给独立的子模块 datasheet.builder 来完成,并由调用这个模块的服务来保持 C 内存块的生命期。这个 C 内存块将以 lua string 的形式存在于调用 datasheet.builder 的 lua 服务的 vm 中,只是把字符串指针共享给其它服务。
而任何服务一旦引用一张表,都可以把生命期保持到该服务退出。这些数据表采用引用数据管理,管理部分全部由 lua 实现。
数据热更新是这样实现的:
任何转换为 C 内存的数据表,每个子表都有一个唯一数字 id 。对应在 lua vm 中是一个包含有这个数字 id 的 userdata 。而在需要热更新数据表的时候,需要把老版本的数据表和新版本的数据表做一次差异比较,根据表项的 key 找到新老对应项,尽量保持相同的 id 生成新版本的数据块。
这样,在做热更新的时候,只要通知持有旧表的服务,更新 C 数据块指针,并把 vm 中的 cache 清空,下次访问任何一个子表时,重新从 C 数据块展开数据即可。
目前 datasheet 已经提交到 skynet 的 master 主干,以一个独立模块存在,接口方面和 sharedata 大致相同。有兴趣尝试的同学可以帮忙测试一下。它以后很有可能作为 skynet 1.1 的默认模块提供。
Comments
Posted by: koll | (8) July 19, 2018 06:38 PM
Posted by: shawn | (7) October 31, 2017 02:53 PM
Posted by: mayao11 | (6) June 26, 2017 04:13 PM
Posted by: 开发者 | (5) June 12, 2017 02:42 PM
Posted by: 易哥开始奔跑 | (4) June 12, 2017 02:29 PM
Posted by: Cloud | (3) June 10, 2017 09:11 PM
Posted by: sog | (2) June 10, 2017 12:19 AM
Posted by: heibor | (1) June 7, 2017 05:36 PM