You are on page 1of 5

海量游戏服务后端的 DB 管理

写一篇正式的作文是一件比较难的事情,作为一个小小的游戏 dba,自己觉得还是很难
深入了解一款游戏的全部,但平时各种游戏 DB 接触多了,对游戏数据层确实有些看法,同
时也不想把作文写的太虚,如今结合实际有条件被迫总结一下:),以供做 db 管理,研发,
或者也负责 db 管理的业务运维同事参考。

本文的立足点,部分总结性质的,架构部分很多东西是很久以前的互娱老前辈留下的遗
产(这些遗产很好的保证了今天还能正常的运营),主要谈一下我的认识;另外在海量服务背
景下,结合质量/成本/效率 3 个点进行描述这 3 年干的一些还算漂亮点的事情以及还没有做
好的。有了这几个立足点基本就可以写一些东西了。

没来这个公司的时候,老是搞不懂,斗地主或者 qq 那么多用户是怎么做数据存取管理
及登陆认证的,带着这个问题就来了。刚来这个公司 1 个月时候,Mysql 的设置让我觉得很
奇怪,竟然为了更高速度回写,有个参数设置了去牺牲事务持久性,是违反了 ACID 这个基
本原则里边 D 的。后边第一次听到海量之道,深感也许这就是互联网服务 DB 运营异于传
统 DB 运营的地方。如果我们拿中移动用户的 arpu 值和我们公司用户 arpu 做一些对比,就
很快明白我们不能像中移动或者银行那样,接下来分别从 DB 架构,DB 性能与负载,DB
日常管理来描述一下在海量游戏服务的后端数据存取管理的一些方法。

一、DB 架构:主要原则有按数据重要程度分级管理,牺牲事务持久性换取高性能,逻
辑简化,平行与垂直分表。
(一)数据分级管理:
a) 硬件资源分离:游戏数据基本可以分类为 2 类:状态类数据,日志类
数据。管理基本原则是状态与日志数据分离。就一款 MMOG 游戏一个 set 而
言,状态类数据一般 10-40G,一个月日志类数据一般在 100-400G 间,通过这
个分离,单机承载的核心数据降低到了实际单区数据 10%的规模,极大程度
降低单用户数据量,提升了单机可支持的核心数据的存取访问能力,同时也可
以让日志相关的统计操作移植到不重要的 DB 上去,减少对核心数据 DB 影响。
b) 热备资源投入:状态类 DB 统一配置热备,方便硬件故障时候切换,
同时日常作为统计资源的投入。日志类 DB 不配置热备,因为日志不影响用户
核心游戏体验。日志类 DB 故障不应影响用户核心的游戏服务,部分游戏日志
直接打印日志文件然后入库,部分游戏直接入库 DB 但支持 DB 损坏情况下,
打印日志文件,然后 DB 恢复再做补入。

(二)牺牲数据回写持久性换取高性能:
a) Mysql 设置:传统数据库管理中,App 成功提交一个事务之后,意味
着这个数据修改是持久的,是刷新到磁盘的,但游戏业务 Mysql DB 的
innodb_flush_log_at_trx_commit 设置为 0,意味 Mysql Server 不对每个事务的
成功提交负责,但 MySQL 会每秒种写入日志文件并和磁盘同步以确保一秒前
的事务持久,害处就是 Mysql 在崩溃(包括硬件故障)的时候会丢失一秒钟的数
据修改,但是这个设置在 benchmark 压力测试中显示有 3-5 倍的性能提升,可
以想象如果这个不设置,现在 DB Server 的量还要翻几番。银行或者财付通类
业务肯定不能用此设置,因为这些传统的业务他们的一个数据丢失会对用户体
验产生的冲击可以想象,而游戏业务基本还属于小额支付,同时,一般还有消
费日志可以保证找回消费记录。
b) cache 定时回写机制:状态类数据必须具备 cache 定时回写机制,
MMOG 游戏中,一个用户的一个角色信息在 gameserver 前端中会是一个较为
复杂的对象,这个对象的数据修改,最终对应到 DB 表的最终修改一般是 5 分
钟定时回写的, 这个要求可配置,也方便遇到紧急性能问题,可以方便临时调
长周期可以临时解决问题。

(三)DB 内逻辑简化:不建议在 DB 内通过 procedure 或者 trigger 放至过多业务逻辑,


不建议使用临时表,而建议仅仅是作为数据落地管理工具。Mysql 使用了简单的原始 sql 日
志复制,逻辑复制对过多逻辑支持并不好,同时,业务逻辑过多放至在 DB 端,也不利于遇
到特殊情况时候做数据库产品的转换。MMOG 游戏因为逻辑复杂,用户属性繁多,单用户
数据量大,不建议按照大学学的那种严格范式去设计表,中韩游戏较多的 DB 设计是将用户
任务完成信息,道具拥有,用户仓库内的物品等封装成一个或几个 blob 字段进行存储,以
减少按照严格范式设计,导致表多,带来过多的表的 join。

(四)平行与垂直分表:平行分也就是按照 uin 通过后缀或者 uin hash 分布到不同表,


不同库,不同主机,控制单表数据量大小最大的好处在于可以让一些变更变得简单,比如
data rebuid,增加字段,30M 大小的表 1 秒可以搞定,基本可以业务不停机时候做。对于日
志表的平行切分最好是按照时间做存储层顺序存储及分离,如果用户需要查询日志,应该限
定查询的时间区间,这对未来日志删除会有很大好处。垂直分表是按照用户不同属性回写的
频繁程度分离到不同表中,此分离可以很大程度降低单用户回写成本。

二、DB 性能与负载:因为海量用户下 App Server 性能总是一个最关注的点,除了前述


DB 架构设计阶段为性能考虑的各个点,还有必要补充一些运营阶段性能关注点。主要从性
能评估指标及不同阶段的评估任务,性能问题判定通用办法,性能问题解决办法三方面表述。

(一)性能评估指标及不同阶段的评估任务
a) 需要关注的核心指标:
i. 业务单机最高承载在线用户:初步反映业务层面压力
ii. Mysql QPS:每秒 Questions 数量,Mysql 负载逻辑层面反应,已运营业务
对比历史曲线可以发现游戏内容增加带来的负载
iii. Mysql slowquery:判定是否有明显的问题 sql
iv. 核心表平均行长度:跟 Questions/s 成反比
v. 单用户数据量:可以对比同类已公测稳定运行业务对比是否有量级差别。
vi. OS 层面 CPU 利用率
vii. OS 层面 IO 利用率:io 利用率=((r/s+w/s)*await)/1000
b) 运营准备阶段:一款成熟的游戏,在公测前会有较长的准备期,在 alpha,beta,
封测,内测,研发同事会通过压力测试模拟大用户量,得到单机承载,也即单机可以支
撑的最高在线用户数。封测,内测阶段,在相对较多用户进来之后,DBA 需要给出最
高在线出现时刻对应的一些 DB 层的核心指标的数值,及预估公测后单 DB 是否能接受
最高承载的用户量。
c) 公测后的运营阶段:重要版本灰度放量后,评估灰度发布的 set 前述核心指标
负载情况;周期性程序输出核心指标变化曲线,DBA 人工 Review 运行情况,为了发现
不断叠加小版本的一些影响;自动化程序对比发现核心指标的异常变化情况(尚未实
现)。
(二)性能问题判定通用方法
a) 判定路径:
i. 第一步:具体 App 现在遇到了什么问题,哪个环节出了问题
ii. 第二步:业务在线是否增长,查看业务经营分析在线,是否有营销活动
iii. 第三步:业务请求压力是否增长,查看 Mysql QPS 趋势
iv. 第四步:是否有问题 SQL,关注 slowquery 趋势及 slowquery 具体日志,
确定是否集中在少数几类 sql,对于业务核心 OLTP 应用确定是否 sql 检索了过多行。
v. 第五步:OS 指标是否异常,查看 CPU 利用率趋势图,确定是否 CPU 资
源不够,查看 IO 利用率趋势图,确定是否 IO 资源不够
b) 常见 OS 资源不够现象分析:
i. CPU USR 过高:CPU 消耗包含 USR,SYS,IOWait,先抛开 IOWait 过高
(>30%)这个点(下边涉及),有较多情况下出现 USR 部分,Top 查看排除掉 game
app 占用过多 CPU 资源后,如果是 DB 导致了 CPU USR 过高,过往经验主要有两
种行为来源:频繁内存内的比较定位以及频繁的内存内排序,主要原因都是索引不
合理,前者常见场景:公测阶段前,部分表数据量不大,DB 可以做到全部 Cache
到内存,where 条件中使用字段无索引,DB 不能通过索引树快速定位到要需要的
少数 Rows,导致要所有 Rows 去比较一遍,从而导致 CPU USR 繁忙,当表 rows
数量大于万条逐步增多,问题表现的会越来越明显。后者常见场景,有复合索引可
以让 DB 快速找到比较多的 rows,但同时 sql 使用了 order by,而 order by 的列并
没有在复合索引内,导致 DB 拿到较多 rows 后还需要在内存中排序导致 CPU USR
部分升高。
ii. IO 繁忙:io 利用率超过 80%,读写 IOPS 都过百,应该关注 await 这个指
标,如果 await 接近过超过 40ms,应该怀疑 Raid 卡 Cache 被禁用了,比较多的原
因是 raid 卡电池正在充电或者一直不能充满电情况,或者硬盘 offline 导致,Dell
机器较多出现此种状况,请 SA 出马进一步断定解决。

(三)性能问题几个解决办法
a) 合适的索引:如上一节所述如果只是简单的 sql 与 schema 表索引设计问题,
可以通过合适的索引来减少计算和 IO 开销,可以通过 explain 查询一个 sql 的执行计划,
判断一个 sql 的执行成本(需要检索的行数,是否需要排序等),然后做一些小的索引优
化即可以解决。
b) 拿 CPU 计算能力换取 IO 存取能力:游戏逻辑复杂,如前“DB 内逻辑简化”
所述,多数开发人员会将一个角色大量的道具,任务或者仓库条目信息作为一个结构体
存放于 Blob 内。这个是个很简便的方法,但很遗憾是几乎所有游戏开发都将前段 app
中一个角色的内存块直接回传给后台 DB 存放,没有注意存放到 Blob 里边的是否有一
些不必要的信息,以至于之前几乎所有 MMOG 游戏中 Blob 中 NULL 字符占了 90%的
信息容量。从寻仙业务注意到单角色用户数据量过大开始,采用 Blob 压缩后,从 50K
降低到 25K。后来 DNF,幻想世界都采用到存取数据到 DB 前先压缩或解压的方法取
得非常好的效果。DNF 的数据量当时数据量降低了 25 倍,极大提高 Mysql Buffer 使用
效率。此措施其实反映了 zengyu 05 年说的一个点:存储问题的解决之道,就是要善用
CPU 和内存来缓解磁盘读写的压力。Cpu 计算能力前进速度实在是远快于 IO 存取能力
前进速度。
c) 适时的 Scale-up 升级引入新的机型,降低成本:
i. 内存扩容升级:互联网硬件升级解决性能问题的传统模式是 Scale-out,1
扩 2,2 扩 4.....,应用设计中的平行扩展能力都已经很强。记忆中 QQGame 主业务
(斗地主等小游戏)DB 曾经有过 32 扩 64,64 扩 128,之后 QQGame 没有热备资源,
后来应用端增加 Cache 异步回写 DB,优化到 64 台。但是到了 09 年下半年,因为
新功能不断增加,玫瑰园(SNS)一些游戏的交互增加,白天 IO 利用率峰值>90%,
平均值 71%,借鉴 08 年 Speed 8G 内存升级 32G 内存的经验,采用内存+64 位升级
的方式之后,io 利用率峰值峰值及平均值降低至 8%和 3%,取得数十倍的承载能
力提升,同时相比扩容机器成本节约了 752W 的人民币,之后很多业务扩容也采用
了升级内存的方式。而 09 年下半年新引入的 A5 机型最终经过 benchmark 测试验
证,也采用了 32G 内存。
ii. Z2 机型引入:在 CF 这个业务本身较快发展,而应用没有平行扩展能力
的情况下,又没有那么多时间去增强应用该能力情况下,将原来的专用的 IBM Aix
小型机+IBM 存储,转移到了 Linux+EMC 的通用方案上,单 Set 5 年成本从 89w
降低到 60w,而承载在线用户数从 12.5W 提高到 25W,同时较好满足用户聚群效
应,实现项目对于提升在线的需求。

d)DB 高版本新特性引入:
i. 08 年中引入了 mysql5.1 版本,使用了 partition 功能,应用逻辑基本不变,
日志按照时间做分区管理,而让原先每天日志删除从 delete 变成 drop partition,从
几个小时的操作减少为几秒钟。
ii. 09 年 oracle 10g stream 引入,让使用 Oracle 业务不允许配置小型机资源做
热备的情况有所改观,stream 支持异构复制,拥有了像 Mysql 相同的热备资源,实
现数据热备同时可以提供日常统计。

三、DB 日常管理:主要解决游戏新功能不断,版本快速迭代推出背景下如何高效率运
营和动态运营。主要经历 4 个阶段:标准化,自动化,服务化,动态运营。
(一)标准化:
a) 机型标准化:3 年多以前,我们的 DB 有 B2,B3,B4,A1 设置 C 类多种机型,
同时每种机型还有不同的 raid。每个地方的问题都很有个性,很难通过对比方法快速定
位问题,之后逐步统一机型到 B4,A1,A5,核心状态类 DB 统一至 Raid10,日志类
DB 统一至 Raid5。
b) 监控备份标准化:全业务 DB 分类为 gamedb,gamedr,logdb,ossdb,统一部
署标准 DB 监控备份,5 分钟检测可用性问题及搜集性能数据并上报,容易误告的告警,
采用出现次数可配置的再触发告警来做告警收敛,通过配置告警指定处理脚本,来自动
化解决一些告警问题。
(二)自动化:09 年开始,将日常繁琐的开区,迁移重做热备,安装 DB,访问权限授
予等操作简化到输入标准 IP 后一键操作。
(三)服务化:10 年开始将原来较多的已经自动化日常 DB 管理工作以友好的服务界
面形式对外提供,用户可以直接输入 IP,经可配置的角色审核步骤,然后一键执行,减少
沟通成本,加速流程运转效率,同时为下一阶段全流程的自动化及日常运维工作一二线人力
分级做好准备。
(四)动态运营:针对产品的动态运营追求灰度放量,快速搜集运营数据感知问题和用
户需求,高频次快速版本迭代来完善产品。游戏作为一个成熟度要求较高的产品,经过较长
时间的准备之后,经历公测进入正式运营阶段后,因需要不断推出不同的活动,新的玩法来
吸引用户,新版本推出速度也相当之快,而且游戏产品也越来越多,作为 DBA 需要做的是
将可以重复的工作内容自动化,将更多的精力投入到 DB 运行状况变化,建设核心指标的异
常变化自动化发现体系,在灰度发布阶段尽早主动发现问题及改进点,辅助产品在快速迭代
变化下实现稳定运营。

四、DB 管理现存的问题
上述说到动态运营还是怎么去快捷满足业务发展需要,应对 DB 频繁的变化及可能
出现的高负载,相对比较容易解决。但 DB 资源管理现存的另一个问题可以简单说如何应对
低负载或者如何动态减轻 DB 负载,也可以说是动态运营做的不够好。

除 QQGame,SNS 和 QQPet 之外的 MMOG 和 ACG 游戏基本都是按照 set 来分布


的,一般而言每个 set 的 DB 硬件资源配置是相同的,但每个 set 实际承载用户数却因先后
对外开放而大为不同,同时游戏本身每个 set 的用户也因为活动刺激会急剧升高,也会因新
内容减少逐步成熟衰退,IED 一直有通过合服这个动作去合并 set,缩减资源,因为涉及到
数据角色的完全存储合并,人力投入相当之大,因为游戏复杂性,每次合服必须有研发根据
当前版本内容写不同合服工具,工具完成后,见过最快的是 1 天停机合服,慢的有 7 天的,
因此造成合服节奏不会很快。所有,在多数的时间里边,一个 IDC 内不同不同 set 的 DB 资
源投入不是均衡的,可能有些 set db 闲死,还有可能有些 set db 忙死。

资源动态运营还存在一个问题是:怎么从物理存储层面区分一个 set 内的活跃与沉


淀用户,现在所有的游戏用户底层存储是混杂在一起的,这种方式造成内存是大量的浪费,
因为 DB buffer 是按照 page 与 disk 交换的,一个 page 里边用户数据可能是活跃也可能是不
活跃。

解决上述 2 点需要 DB 前端有一个通用代理来帮助 DB 或者 DB 本身看起来完全像


一个云存储解决方案,开发人员不用关心,后台 DB 是物理数据落地可以支持完全动态调度
的,而不是现在一个产品的一个 set 严格对应到一台物理 DB 上去。

You might also like