内存数据库和进程内cache

上家公司是做TPS游戏的,因此其实对玩家数据的操作其实不是很频繁,而且由于当时服务器的数据库部分是单独剥离开给指定的人维护的。因此对于cache这一块,从来没有仔细的考虑过。新公司的游戏需要与离线玩家进行战斗,因此在写业务逻辑时是需要cache支持的。但是总觉得cache设计之初的定位有些问题,由此引发了一些思考。


最初我一直在纠结于内存数据库,我觉得诸如redis这种内存数据库已经把所有数据内容都常驻内存了。那么设计一个cache真的有必要么?直接随用随取不就行了么。在服务器进程中再cache一份冗余数据, 是否真的有必要呢? 如果服务器进程中有必要cache一份数据那么内存数据库的存在是为了解决什么问题呢?

最终我发现我被内存数据库这几个字给带偏了,内存数据中数据常驻内存是不假, 但是他是以独立进程存在的。也就是说服务器想要获取内存数据库中的数据,首先需要通过socket发送请求,然后数据库进程接收到请求后取出相应数据通过socket返回给服务器进程。 这中间牵涉到系统调用,上下文切换引cpu cache大面积失效,网络传输延迟等各种因素的存在。因为相对于服务器进程直接读写本地内存来讲,性能相差了不止一个数量级。

那么即然如此,直接用服务器+mysql不就好了嘛。为什么会有memcache和redis的出现呢?他们的一定是为了解决某类问题才出现的。

首先数据库的作用是容灾,即在服务器进程出现bug崩溃时,可以将玩家丢失数据的代价降到最低。而游戏服务器本质上就是一直在操作玩家数据(所以才说游戏服务器一般属于io密集型,所以才越来越流行使用脚本来编写业务逻辑),因此数据库的写入和读取速度在很大程度上会影响服务器程序的性能。由于mysql中所有DB数据都是存入硬盘的,读写性能很容易成为瓶颈,有好多服务器为了提高mysql的读写性能,都会专门配备ssd。

而memcache/redis的机制是把数据全部cache进内存的,因此在读写性能上要比mysql出彩不少。但是由于memcache没有数据落地功能。因此一般是挂在mysql前面做缓存使用。

换句话来说,内存数据库也是数据库,他的作用也是用来容灾的,之所以会出现主要是为了解决在大规模频繁读写数据库时的性能瓶颈。因此内存数据库并不能替换进程内cache。


那么进程内cache主要是用来解决什么问题呢?

从cpu的cache发展历史来看,程序一般有一个特点。那就是读的频率要远远大于写的频率。

如果读和写都直接穿透到数据库的话,由于上下文切换,网络传输延迟等原因,与仅仅在写数据时才穿透数据库,而读直接读取时程内cache的数据相比,性能应该会差上几个数量级。

因此进程内cache实现最简单的一种方式就是write through。当然,也明一些其他的设计方式,比如在进程内cache对数据置脏标志位,在写入关键数据时,会将此玩家脏数据写入DB,无疑这种方式效率最高,但实现起来与业务逻辑偶合度也会更高。

由于有了进程内cache, 因此在对数据库结构设计时,有时候可以存在更多的优化空间。

比如有些逻辑中需要数据A, 而数据A是通过数据B查表或计算就可以得到的。 这个时候就没有必要将数据A存入数据库,也没有必要逻辑代码使用数据A时每次都去计算一下。仅仅在进程序内cache加载数据完成的那一时刻去计算一次并放入进程内cache即可。这样即提高了逻辑的运行,又减少了数据库的占用空间。最重要的时不会出现由于程序Bug导至A与B不致的情况。况且将A存入数据库然后直接从数据库加到到进程内cache的网络开销并不一定比进程内cache加载完成后从B算出A的值会少。



发表评论