原文:https://blog.csdn.net/narlon/article/details/81835003
db(一般是mysql,或是类似的,比如mariadb)在游戏服务器中,启动了数据落地的作用。游戏服务器可以直接和db建立访问连接操作数据,或者通过一个中间人(DbServer)来完成这个工作。本文,我们以独立DbServer来分析带来的好处,以及遇到的问题。
下文提到的一些优势,如果不单独封装一个db服务器(通过一个存储模块的方式来实现)也可能可以做到。但如果有一个独立的db服务器时,更容易把这一点实现的比较纯粹。
适配存储过程,隐藏存储细节
db服务器可以做到,缓存数据存储到redis,超时数据mysql落地,而外层数据完全无感。同样的,当底层进行存储切换,或是升级时,只需要改动db服务器的代码。举一个例子,我们的服务器支持部分机器人玩家进入游戏,但是机器人玩家的数据我们不希望会改变,从而方便我们重复做一些性能测试。于是,我们再dbserver上做了差异化,机器人的数据加载后,不再接受数据回写,感觉上是这些数据是只读的。
db服务器可以额外做一层逻辑业务有关的cache。比如玩家登出后30分钟,他的数据实际还在db服务器上,再次登录不需要从mysql走一套。
外围服务器(这里称除了db服务器,都是外围服务器)不再需要链接db相关的库。
外围服务器需要发起存储,只能通过网络消息等方式发起,而无法直接访问数据库。
隐藏存储方案的细节
我们知道,每次mysql的访问都需要建立一条单独的mysql连接。而通过是同步的mysql访问会独占当前线程一定的cpu时间。由此,我们自然可以推出,dbserver需要有一个线程池和一个连接池,这样可以最大化mysql的存储吞吐量。
如果这样的连接池和线程池实现在所有的游戏进程中(假设我们是一个多进程的服务器组)。如果滥用,mysql的总连接数已经系统的总线程数容易暴涨;而如果非常谨慎开支,又无法最大化IO。而如果单独放置在DBServer上来调度,就比较容易控制和调度这些资源,做到兼顾性能和资源浪费问题了。
隔离逻辑类型
在我们的方案中,大部分数据在db中都是以blob的形式存储的。那么,在dbServer中,我们需要有一个从对象转换为byte[]的过程。而我们系统中只有注册的存储类型才能够正常序列化。这就要求,逻辑服务器中使用的数据结构和最终存储的数据结构是一个数据结构(比如逻辑运算使用PackItem,存储使用DbPackItem)。
这么做的好处
类型功能清晰,归属明确。
存储的数据字段,不会因为逻辑的临时需要,多存储一些无用数据。也就是双方(逻辑和存储)不用互相迁就。
坏处也比较明显,可能需要多一次对象创建和内存拷贝。
不足
加载周期变得更长,因为跨进程的缘故,如果是串联读取多个数据,需要进行异步等待,代码复杂度和时间消耗都会增加。
增加了服务器启动和关闭的流程复杂性。比如逻辑服务器因为需要取一些系统上次缓存的数据来恢复游戏,但必须等db服务器启动完成,才能这么做。
存储的数据结构增多时,需要定义大量的数据同步协议,相比于多线程可以限制性的直接操作(如果多线程情况下使用消息传递数据的情况,和我们遇到的问题应该差不多),要繁琐和难以维护一些。
服务器组复杂度上升后的问题。比如如果db服务器和逻辑服务器的socket连接断了,数据该怎么办。
社区交流群:221273219
Golang语言社区论坛 :
www.Golang.Ltd
LollipopGo游戏服务器地址:
https://github.com/Golangltd/LollipopGo
社区视频课程课件GIT地址:
https://github.com/Golangltd/codeclass
有疑问加站长微信联系(非本文作者)