Open smallnewer opened 7 years ago
有时候会有这种需求:需要你记录下玩家是否完成过某个任务/副本。而任务/副本的总量又非常大(比如有几万)。 这种需求很少出现也比较棘手,但也不是完全没有解决办法。 通常我们会用这种数据结构存储
var data = { id1: true, id2: true, .... }
一个10W key的上述对象,在V8里约占用3M内存,如果考虑编解码+传输压力,则不可能在线上服务器环境使用这种方案。 如果利用二进制存储,大约只需要3K内存(纯估算,但基本在K的量级)。而读写速度也远比10Wkey的大对象快得多(太大没法利用hidden class提高性能了)。
原理是: 二进制的每一位代表一个任务/副本是否已完成。 由于JS的位运算只支持32位整形(即只能存31个记录),所以会利用数组存储多个int32。虽然这会导致额外的内存占用和存储压力,但也有一定的优点:兼容Int32Array,前后端更通用。
因此也有几个限制:
下面是一个DEMO代码
// 用二进制来存储进度 function setData (data, index, bool) { if (bool) { return data | (1 << (index - 1) ); }else{ return data & ~(1 << (index - 1) ); } } function getData (data, index) { return data >> (index - 1) & 1; } // [0~31, 32~63, ...] function get (arr, index) { var num = arr[Math.floor(index / 32)]; var ind = index % 32 + 1; return getData(num, ind); } function set (arr, index, bool) { var num = Math.floor(index / 32); var ind = index % 32 + 1; // 从1开始 if (num >= arr.length - 1) { for (var i = arr.length; i <= num; i++) { arr.push(0) }; }; arr[num] = setData(arr[num], ind, bool); } // tester ;(function () { // 10W次随机存取 function test (index) { var arr = []; var hash = {}; for (var i = 0; i < 100000; i++) { var num = Math.round(Math.random() * 10000); if (hash[num]) { if (get(arr, num) !== 1) { throw new Error('存取错误.') } set(arr, num, false); hash[num] = 0; if (get(arr, num) !== 0) { throw new Error('存取错误..') }; }else{ if (get(arr, num) !== 0) { console.error(arr, num) throw new Error('存取错误...') } hash[num] = 1; set(arr, num, true); if (get(arr, num) !== 1) { throw new Error('存取错误....') } } }; } for (var i = 0; i < 100; i++) { test(); }; console.log('测试通过') })();
基于上面的思路。 利用MongoDB持久化,把JS数据==>BSON优化为JS==>Binary==>BSON,好处多多。
传统的JS数据==>BSON方案缺点:
如果改为新的方案:
最主要的还是1~2点。这是Node.JS单进程的负载瓶颈之一。譬如传统的机器是8核16G,即一个Node用2G内存~~
需要自己撸一套二进制编码协议,需要支持:
有时候会有这种需求:需要你记录下玩家是否完成过某个任务/副本。而任务/副本的总量又非常大(比如有几万)。 这种需求很少出现也比较棘手,但也不是完全没有解决办法。 通常我们会用这种数据结构存储
一个10W key的上述对象,在V8里约占用3M内存,如果考虑编解码+传输压力,则不可能在线上服务器环境使用这种方案。 如果利用二进制存储,大约只需要3K内存(纯估算,但基本在K的量级)。而读写速度也远比10Wkey的大对象快得多(太大没法利用hidden class提高性能了)。
原理是: 二进制的每一位代表一个任务/副本是否已完成。 由于JS的位运算只支持32位整形(即只能存31个记录),所以会利用数组存储多个int32。虽然这会导致额外的内存占用和存储压力,但也有一定的优点:兼容Int32Array,前后端更通用。
因此也有几个限制:
下面是一个DEMO代码