最近项目里打算做一个缓存全服玩家的基础信息的功能,由于数据量表面看上去有点大,需要对数据量进行在内存中大小进行评估,同时也要对GC进行评测,才能确定是否批准把该方案落地。
- 缓存方案:服务器启动时把全服玩家基础数据读取出来进行缓存,每个玩家测试字段数量为:6个int, 2个字符串。按这个数据模板测试,得出的大概内存占用数量统计为:
数据量 |
垃圾(单位:M) |
314750 |
108.73 |
可以从上面图看出,对于30万的玩家来说,内存数据大概108M,即使把字段翻一翻,也就216M,对于服务器的开发换来的是不需要异步读取离线玩家数据,基础数据统一管理的结果,这个内存是极具性价比的。
3. 由于数据常驻内存,对于GC来说可能存在回收时的效率问题。对于上述的例子,纯基础数据约30万的table数据量,外加60万的string数据量,这都是可确定的数据量。接下来的测试数据会比这个数据量大得多,为了不仅仅是针对这个数据,是需要对于GC回收有个清楚的认识。
4. 为了测试回收效率,测试的方向是从数据量和GC回收的时候每一个阶段时间做统计。GC主要分了5个阶段,每个阶段还有细分不同情况的统计,目前分的阶段有:markroot, 扫描标记, 扫描标记原子阶段, 清理字符串, 清理Table及其它GCObject阶段, 停止阶段。统计方式为:每个阶段的时间使用clock()时间累加统计,clock的精度也足够了。测试数据方面,string和table测试数量从100万,200万, …1000万,数据量从200万起。对于此次测试,测试目标是数量与时间关系,采用了连续调用单步回收的方式测试,测试统计结果如下:
总数量 |
tabel数据量 |
string数据量 |
垃圾内存大小 |
markroot时间(单位:ms) |
扫描标记(单位:ms) |
扫描标记原子阶段(单位:ms) |
扫描字符串(单位:ms) |
扫描Table及其它GCObject(单位:ms) |
回收阶段(单位:ms) |
test count |
testtablecount |
teststringcount |
garbage |
gcmarkroottime |
gcpropagatetime |
gcatomictime |
gcsweepstringtime |
gcsweeptime |
gcfinalizetime |
2000000 |
1000000 |
1000000 |
187.51M |
1 |
94 |
0 |
462 |
330 |
0 |
4000000 |
2000000 |
2000000 |
248.92M |
0 |
90 |
0 |
973 |
635 |
0 |
6000000 |
3000000 |
3000000 |
302.32M |
0 |
92 |
0 |
1318 |
940 |
0 |
8000000 |
4000000 |
4000000 |
355.73M |
0 |
101 |
0 |
1817 |
1253 |
0 |
10000000 |
5000000 |
5000000 |
425.14M |
0 |
91 |
0 |
2269 |
1569 |
0 |
12000000 |
6000000 |
6000000 |
478.54M |
0 |
87 |
0 |
2661 |
1865 |
0 |
14000000 |
7000000 |
7000000 |
531.95M |
0 |
92 |
0 |
2983 |
2172 |
0 |
16000000 |
8000000 |
8000000 |
585.35M |
0 |
84 |
0 |
3397 |
2489 |
0 |
18000000 |
9000000 |
9000000 |
670.76M |
0 |
99 |
0 |
4096 |
2828 |
0 |
20000000 |
10000000 |
10000000 |
724.16M |
0 |
93 |
0 |
4448 |
3104 |
0 |
6. 分析
1. mark rook阶段:操作不多,时间可以忽略不计,多次测试为0。
2. 扫描标记阶段: 这个阶段的耗时比较稳定,从200万到2000万,时间都处于毫秒级
3. 扫描标记原子阶段: 由于我们测试table没有弱表,并且是连续阻塞调用,所以扫描标记原子阶段是为0的。即使使用默认方式调用,可以影响到原子阶段的情况就是:在回收过程中被扫描过的table会被修改了,同时又再被扫描到,就需要放到指定的链表里(grayagain)里,等到原子阶段再处理。对于这种情况,短时间内被修改的和操作表的频率会对此情况造成影响,可预见的数量并不会多。这个对于项目来说,可以说不具什么影响。
4. 扫描字符串: 时间与数量大概是成线性增加,每增加100万数据量,大约耗时增加450ms。
5. 扫描Table及其它GCObject:时间与数量大概是成线性增加,每增加100万数据量,大约耗时增加300ms。对比字符串时间计算,统计显示清理字符串稍微会久一点。
6. 回收阶段: 操作不多,时间可以忽略不计,测试耗时为0。
7. 总耗时:对于总耗时来算,从800ms到7500ms,完全回收的时间并不算是预料中的多。对于项目实践来说,采用定时回收策略的话,可以把这些时间分摊到每一步定时回收里,大概回收过程能在2分钟内(依赖参数设置,此处使用默认参数)可以完成。对于时间分布,大部分时间耗在清理阶段,而对于扫描阶段的耗时相对还是要稳定和少。
测试统计代码稍后整理存放在github上
|
请发表评论