1、数据服务器详细设计
数据服务器在设计上采用三个层次的数据同步,实现玩家数据的高速获取和修改。
数据层次上分为:内存数据,redis数据,mysql数据
设计目的:首先保证数据的可靠,防止数据丢失,保证数据完整。然后实现数据的高速访问,减少由玩家数量增加对数据服务器性能造成的影响。最后实现运维数据的入库,以及数据持久化。
在这个基础上数据服务器不再是一个单一服务器,它涉及到与其他服务器之间的交互。
数据服务器的核心在于redis数据层面。通过redis加快玩家数据的快速拉取。
内存数据是直接与逻辑服务器进行交互的,因此内存数据划分到逻辑服务器进行管理,redis数据划分为数据服务器管理,mysql数据是对redis数据库的备份和同步。
主要流程,数据服务器启动时先从mysql数据库加载所有的活跃玩家相关数据到redis数据库中,逻辑服务器与数据服务器之间的交互都是通过国过redis数据进行操作。Redis数据库回写到mysql可以通过回写进程实现。
图 1 数据服务器数据扭转流程图
1.1 数据服务器主进程
数据服务器主进程核心是对redis的操作,通过redis数据库实现数据的高速存储。因此,数据服务器的核心是保证在玩家数量大较大的情况下对玩家数据的增删改都能维持在一定的时间效率内。
针对于redis数据库本身,可以考虑实现redis的主从库搭建,保证redis的数据库的备份和效率。
数据服务器主进程的构成:
1、mysql数据同步到redis数据库中。
2、逻辑服务器和数据服务器的网络库
3、玩家账户修改缓存
4、Redis数据库的回写至mysql
5、逻辑数据库的请求处理
6、内存数据库的组织结构
1.2 数据回存服务
数据服务器接收到逻辑服务器的请求后,就数据库操作请求进行操作的队列缓冲,形成操作队列。操作队列同时提供了redis数据库的及时写入和mysql数据库的异步写入。
1.3 数据服务器提供接口
对于逻辑服务器,数据服务器应为其开放以下接口:
1、数据拉取接口,拉取对应玩家的所有数据,将数据保存到内存数据中
2、修改玩家数据接口,提供玩家的部分或全部信息修改命令
2 数据服务器模块功能
首先redis是一种key-value的内存数据库,以key和value的方式来存储数据实现高速的访问和操作。其次mysql是一种关系型数据库,存储的是结构化的数据。这种差异决定了在数据入库redis的时候需要做相应的处理。
2.1 数据入库redis
通过redis的key-value来存储数据库中的表名以及表的索引和hashkey,来实现构造二维的数据表。
针对于每张表,key结构 [表名]:[id值]
要求所有的数据库表的第一个字段为id,或者所有的表都为主键,并且不能重复,这样保证了所有的key都不重复
图 2 数据入库redis后表现结构
入库流程方法一:
先将redis清空,再将mysql所有的额数据逐条入库到redis。逐条入库的好处是可以对玩家进行其他的操作处理,缺点是启动服务器速度慢。
方法二:
先将redis数据库清空,再将mysql读取处理的数据缓存成redis识别的事务,一次性进行redis入库。
2.2 排序
实现高效的排序功能,可以使用redis的zset进行拍寻队列构建,对于玩家等级或经验改变时,对redis数据库进行修改,当需要或获取排序时直接获取zset集合类已经拍好的顺序就可以实现高效的排序功能
2.3 redis数据回存mysql
对redis数据库的操作需要回存到mysql数据库中。
方案一:采用多线程异步,通过多线程异步实现mysql修改回存同步。在数据服务器多开一个线程进行回存。
优点:设计结构相对简单,不涉及到进程间通信,但需要多线程开发支持
方案二:采用多进程方式,回存由专门的进程进行,没有操作消息redis数据库默认转发进程转发。
优点:简单的单线程即可满足需求,但需要进程间通讯。
2.4 网络交互模块及序列化
服务器之间的网络传输由统一的网络模块进行。序列化功能采用protobuf的序列化功能。
3 具体实现
数据服务器设计到大多都是表和redis数据操作,这些操作具有相似性。这样的繁杂的操作不利于手动修改和书写代码。
因此在此处合理的运用生成器就会减少很大一部分工作量,通过生成器快速生成C++代码,并且灵活的应对数据库结构的更改。
3.1 生成物和生成器
生成器使用了C#语言快速编写访问数据库结构,并生成类代码。
生成物可以设计成各种语言的操作文件,目前生成C++文件。
3.2 Mysql数据回写进程
数据回写进程又一个回写消息队列和一个回写器组成,回写消息队列缓存所有要进行回写操作,再有回写器进行回写。多线程回写消息队列操作加锁。
3.3 玩家登陆信息查询
玩家登陆时将拉取玩家数据到内存中。这个流程现在redis数据库中查询玩家数据,如果存在,返回给逻辑服务器玩家数据,如果redis数据库中不存在玩家数据,将再次在mysql中进行查询,如果存在返回给逻辑服务器,如果不存在返回查询失败。具体流程看下图
图 3 玩家数据查询流程
在修改玩家数据时,需要同步三层数据。数据首先是在内存中进行修改,然后发起修改命令给数据服务器,数据服务器将修改命令分别压入redis数据库命令队列和mysql命令队列。再有两个线程异步对命令队列中的命令执行,完成数据回写。
图 4 数据回写命令执行流程
3.4 玩家活跃程度划分
为了提高数据的访问速度和效率,将玩家进行活跃度分级。正在游戏玩家数据加载到内存,快速访问。活跃玩家加载到redis数据库,实现快速拉取。不活跃玩家存放mysql,需要时访问查询。
优点:根据数据的访问频度对数据进行分级,使得高频数据快速访问,节约了内存空间。
缺点:不活跃玩家登陆时间相对较长,但不影响游戏效率。登陆后,玩家又再次进入活跃玩家存储方案。