如果这是您第一次阅读本系列文章,请阅读本文末尾的本系列其他文章。
本节内容
截至上一篇文章,我们已经介绍了网络基础知识、服务器架构、登录流程、服务器身份验证、短链接、长链接等。
在正式讲具体游戏功能的开发之前,我还需要完成最后一部分:数据存储。
对于网络游戏来说,玩家的真实身份实际上是服务器上保存的相关数据。独特的数据造就了独特的球员。
今天我们就来说说用户数据的存储,这样后面讲具体功能开发的时候就更容易理解了。
数据存储的核心如下:
数据库选择关系型/非关系型
数据存储频率
分布式存储
数据一致性
数据的添加、删除、查看、修改
用户数据表设计
虽然普通的项目不需要如此深入和周到的考虑,但是熟悉以上内容对于我们处理项目问题时会有很大的帮助。
数据库选择
有些朋友一提到数据库就害怕,认为这是自己从来没有接触过的东西。
其实,这件事很简单。数据库主要做两件事:
保存数据
获取数据
它与您在磁盘上写入的文件相同。很多早期的游戏(比如西游记)都是直接写文件的。
但当数据量增大时,简单的文件读写性能就跟不上。需要完善文件读写机制。
慢慢的,就发展成了现在的数据库:一个独立的服务进程,负责数据的读写。
在游戏服务器领域,目前流行的数据库有三种:MySQL、Redis、MongoDB。
目前常见的选项有以下几种:
MySQL+内存:对于读写频率不高,或者内存损耗不在意的项目,选择这种方式。
MySQL + Redis:Redis做缓存,提高读写频率和数据安全性,MySQL做持久化
MongoDB:与方案2相比,可以兼顾两方面,对于很多项目来说完全足够了。只是需要大内存支持。
MongoDB + Redis:如果选项3无法持续,可以考虑Redis。但在这种情况下,使用MongoDB的优势并不明显。如果要使用Redis,最好配合MySQL使用,这样条件查询和数据分析会更强。
在TGX中,我选择使用MongoDB,这对于很多项目来说已经足够了。单一数据库还避免了数据一致性问题。
只要不是非常大的项目,使用MongoDB作为游戏数据库肯定会带来最快的开发和迭代速度。
数据存储频率
并非所有数据都需要及时存储。对于用户基本数据(姓名、金钱、性别、背包物品)的变化,响应不需要太快,但丢失会产生严重后果。因此,我们立即存储此类数据。
对于需要持久化的数据,比如MMO中的玩家技能CD、位置等,并不需要立即更新。可以设置为脏标记,并使用定时器来触发存储。甚至许多游戏也不存储这些数据。
对于需要持久化的游戏数据,比如象棋、五子棋等,希望即使服务器重启,棋局仍然可以恢复。
这种情况下,可以使用Redis直接写入备份,当服务器启动时,从Redis中读取并恢复数据。游戏逻辑不需要实时从Redis读取。
对于不需要持久化的数据,比如Moba对战的游戏内数据,或者一些PK游戏,就不需要涉及到数据库。
总体来说,数据库存储分为几个步骤:
如果不需要保存,就不要保存。
只有需要备份的才应该定期备份网络游戏开发,但不需要实时读取。
如果需要实时读写,需要注意并发瓶颈和一致性
分布式存储
数据库的读写速度受数据量的影响很大。当数据量达到一定程度时,保证读写速度的最好方法就是将它们分开。
我们可以看到,王者荣耀等游戏对用户数据进行了划分,不同的用户存储在不同的数据库中。
由于整个系统可以访问所有数据库,因此您只需带上数据库号(区号)即可完成不同账户数据之间的交互。
数据一致性
我们可能会同时在多个地方修改数据库。最典型的就是购买限量产品。
当多个用户一起从商城购买商品时,需要保证数量扣除没有错误。
虽然大多数数据数据库都提供事务机制,但事务速度非常慢,在游戏开发过程中应该避免。
在游戏中,我们通常使用两种类型的锁来解决这个问题。
1. 乐观锁
简单来说网络游戏开发,乐观锁就是给每个数据分配一个版本号。每当数据发生变化时,这个版本号就会发生变化。
写入数据时,我们按照以下步骤进行:
获取数据,获取此时数据的版本号
处理数据
写入数据并传递你得到的版本号进行比较。
如果发现版本号发生了变化,说明我们拿到数据后,已经被别人写入了,写入失败。做好故障处理。
如果发现版本号没有变化,说明没有人写过,写入成功。
2.分布式锁
分布式锁是网络锁的一种。整个系统中,有一个进程(如Redis)负责分配锁。当锁被拿走时,需要等待直到锁可用。
分布式锁不适合在高频交互的情况下使用,但对于普通用户系统功能来说,由于交互频率较低,完全可以。
总结与预览
由于篇幅有限,本文仅讨论游戏数据库中需要涵盖的核心主题。具体的用户数据存储内容将在下一节讨论。
下一节我们将讨论用户数据存储: