草庐IT

强扩展、强一致、高可用…GaussDB成为游戏行业的心头爱

华为云开发者社区 2023-03-28 原文
摘要:看GaussDB for Redis强扩展、高可用、强一致、高安全,如何玩转各大游戏场景

本文分享自华为云社区《GaussDB为什么成为游戏行业的心头爱?》,作者: GaussDB 数据库 。

在结束忙碌工作回家的地铁上,难得闲暇的周末下午,和朋友再来一局的入睡前......游戏已经成为了当下大多数年轻人在紧张的工作之余最普遍的放松方式和社交手段之一。而游戏厂商想要增加用户粘性,不仅需要在游戏类别、情节故事、关卡设计上洞察用户喜好,让游戏足够新颖,更要在流畅程度上带给用户更极致的体验。游戏场景复杂多样,应用版本更新迭代,企业的运营成本也水涨船高,这让各大厂商在游戏开发过程中都极度重视一个同样的问题——如何选择一款可靠的数据库来支撑庞大的系统稳定运行?

每一款游戏都要经历开发、上线、运维等多个环节,在这过程中,数据库面临的应用场景非常丰富。不同的应用场景对数据库的要求也不尽相同,比如跨服对战对计算算力的要求,在线聊天产生的数据对存储空间的要求,排行榜实时刷新对数据可用性的要求......这些具体的游戏场景都要求数据库在存储空间、弹性扩容、数据强一致保障以及成本等各方面都交出满意的答卷。

在《迷你世界》、《余烬风暴》等大型游戏的开发和上线过程中,这些厂商都不约而同地将GaussDB for Redis数据库作为首选。为什么GaussDB for Redis成为了游戏行业的心头爱呢?我们不妨先看看在游戏行业,他们都面临着哪些共同的痛点,GaussDB for Redis有什么锦囊妙计。

强扩展——支持业务高峰期海量玩家在线对战畅聊

在一款游戏新开服、活动推广、重大版本上线或发放点券游戏币等时间段,玩家访问量突增,同时在线人数迅速飚高,这种情况下,往往需要提前增加节点扩算力,高峰期后再缩减节点,如果处理不好,极有可能在扩容时发生长时间业务阻塞,甚至出现游戏服务器故障而被迫停服,面临痛失大量游戏玩家的风险。

比如《迷你世界》春节期间在线玩家数从平时的百万级别突增到千万级,玩家协同创作与联机游玩产生大量实时聊天数据,需要存储空间在线快速扩容。而之前使用的开源Redis基于存算一体架构,存储扩容需要十几分钟,扩容期间大量玩家在协同创作、实时聊天时出现语音消息发不出、图片传输失败等问题,持续时间长达十几分钟,极大影响了玩家的游戏体验,极易造成用户流失。

龙图游戏的大型魔幻手游《余烬风暴》采用的是多人跨服对战的游戏模式,在大量玩家跨服战斗的业务高峰期,数据库不仅需要稳定支撑10万级高并发业务,还对算力有一定的要求,需要计算节点达到秒级扩容,才能轻松应对海量用户跨服对战的流量洪峰。

针对这些场景,GaussDB for Redis基于业界首创的存算分离架构,做到了存储空间的弹性伸缩和秒级扩容,扩容只需修改配额,最高可支持单实例TB级数据1秒扩容8倍,即从1TB扩容到8TB,能满足所有海量游戏聊天数据的在线扩容场景,助力千万级玩家畅聊不卡顿,极大提升玩家交流体验。计算节点做到分钟级扩容,扩容仅需修改数据映射,无需搬迁数据,在业务上仅有秒级的时延抖动,支撑《余烬风暴》业务高峰期单日同时在线玩家数达到百万级,且玩家数据访问几乎不受影响。

强一致——保障多个地图数据实时一致性

沙盒游戏是近几年的热门游戏题材之一,这类游戏常见的玩法有两种,一种是普通玩家可以进入多个地图联机游玩,通过“方块”的自由组合,搭建各种场景地图;另一种是高阶玩家与各地玩家联机,跨区域协同创作地图,形成全新的小游戏。

相信不少玩家都遇到过这样的问题:在游戏地图A购买完道具,立即切换到别的地图以后,打开背包发现道具竟丢失了;遇到美女玩家,关注对方后本应自动发送招呼语,对方却迟迟没有收到。

其实主要原因是两个地图从不同的节点读取数据,由于主节点的数据没有及时同步至从节点,更新存在一定的延迟性,最终导致多个地图之间的数据不一致。这说明一个问题,无论什么条件下,游戏地图数据必须要实时保持一致,这样才能为玩家带来良好的用户体验。

面对这种数据不一致的问题,GaussDB for Redis是怎么做的呢?首先不需要主从分离,在存储用户数据时,存储层使用3副本保存地图数据,基于一致性共识算法率先保证所有副本数据的一致性,避免了原有主从结构数据库在主数据库宕机后,从数据库中的地图数据跟主数据库中不完全一致的问题。玩家不论是在多个地图之间游玩,还是联机协同创作地图,都能实时保障数据的一致性,在游戏的体验感上得到极大的提升。

数据高可用——支持游戏排行榜数据实时刷新

在一款游戏热度逐渐高涨或新游戏上线时,玩家往往对游戏排行榜的参与感会非常强烈,排行榜的刷新对数据实时性的要求更强。因为排行榜的变化,数据变动非常频繁,需要高可用的数据库保证积分数据的稳定读取和实时刷新。

为了保证数据稳定读取,解决数据库高可用问题,基于存算分离架构的GaussDB for Redis将玩家积分数据统一存储在存储池中,而不是存储在计算节点中,这样做的好处是,如果某一个计算节点发生故障后,业务将自动将该节点的业务切换到其它计算节点,如下图所示:

只要集群中的任一节点存活,就可访问全量数据(友商采用的存算一体主从架构,单节点挂掉后,上面存储的积分数据将无法读取),避免了因为节点故障导致排行榜无法实时刷新的情况。

高安全——跨地域双活完整灾备轻松应对游戏故障

游戏场景是典型的互联网场景,用户的数据日志非常之多,一旦发生数据库宕机、机房断电、网络故障等情况造成数据丢失,玩家的游戏体验将大打折扣。当一个可用区或整个地域意外宕机,需要有其它的可用区和补救措施来及时修复数据,这对系统的安全保障能力和容灾能力都提出很高的要求。

GaussDB for Redis支持跨地域双活容灾,即在两个实例之间建立数据同步链路,其中主实例支持读写,备实例只读。如下图所示:

与开源Redis那种简单的命令转发不同,GaussDB for Redis的双活方案是基于WAL日志的数据同步,原理上更类似于MySQL数据库。在双活架构中,RsyncServer进程负责数据的全量和增量同步,数据同步链路采用华为云内部高速网络,同Region内仅毫秒级延迟。

GaussDB已经在游戏行业沉淀出了非常丰富、成功的经验。在《迷你世界》国产沙盒创意平台中,GaussDB for Redis支撑了多区域TB级别的地图数据同步更新,助力亿级用户跨区域协同创作,在整体成本相比开源Redis降低60%的同时,存储用户画像总量提升了3倍,保障了千万级玩家实时聊天数据毫秒级别访问。龙图游戏选择了GaussDB for Redis作为大型魔幻手游《余烬风暴》的核心底座,不仅开服首日流水就破千万,且在业务高峰期单日同时在线玩家数达到百万级,收获了众多玩家的喜爱。

游戏场景只是互联网行业中的其中一种通用型场景,这些核心技术和实践经验也只是GaussDB数据库多年来发展成果的冰山一角。GaussDB在技术上的创新和突破从未止步,在千行百业中的探索也一直在路上。

 

点击关注,第一时间了解华为云新鲜技术~

有关强扩展、强一致、高可用…GaussDB成为游戏行业的心头爱的更多相关文章

  1. ruby - 使用 C 扩展开发 ruby​​gem 时,如何使用 Rspec 在本地进行测试? - 2

    我正在编写一个包含C扩展的gem。通常当我写一个gem时,我会遵循TDD的过程,我会写一个失败的规范,然后处理代码直到它通过,等等......在“ext/mygem/mygem.c”中我的C扩展和在gemspec的“扩展”中配置的有效extconf.rb,如何运行我的规范并仍然加载我的C扩展?当我更改C代码时,我需要采取哪些步骤来重新编译代码?这可能是个愚蠢的问题,但是从我的gem的开发源代码树中输入“bundleinstall”不会构建任何native扩展。当我手动运行rubyext/mygem/extconf.rb时,我确实得到了一个Makefile(在整个项目的根目录中),然后当

  2. ruby-on-rails - 如何使辅助方法在 Rails 集成测试中可用? - 2

    我在app/helpers/sessions_helper.rb中有一个帮助程序文件,其中包含一个方法my_preference,它返回当前登录用户的首选项。我想在集成测试中访问该方法。例如,这样我就可以在测试中使用getuser_path(my_preference)。在其他帖子中,我读到这可以通过在测试文件中包含requiresessions_helper来实现,但我仍然收到错误NameError:undefinedlocalvariableormethod'my_preference'.我做错了什么?require'test_helper'require'sessions_hel

  3. c - mkmf 在编译 C 扩展时忽略子文件夹中的文件 - 2

    我想这样组织C源代码:+/||___+ext||||___+native_extension||||___+lib||||||___(Sourcefilesarekeptinhere-maycontainsub-folders)||||___native_extension.c||___native_extension.h||___extconf.rb||___+lib||||___(Rubysourcecode)||___Rakefile我无法使此设置与mkmf一起正常工作。native_extension/lib中的文件(包含在native_extension.c中)将被完全忽略。

  4. ruby - Ruby gsub 替换中的行为不一致? - 2

    两个gsub产生不同的结果。谁能解释一下为什么?代码也可在https://gist.github.com/franklsf95/6c0f8938f28706b5644d获得.ver=9999str="\tCFBundleDevelopmentRegion\n\ten\n\tCFBundleVersion\n\t0.1.190\n\tAppID\n\t000000000000000"putsstr.gsub/(CFBundleVersion\n\t.*\.).*()/,"#{$1}#{ver}#{$2}"puts'--------'putsstr.gsub/(CFBundleVersio

  5. ruby-on-rails - 向 Rails 3 添加 Ruby 扩展方法的最佳实践? - 2

    我有一个要在我的Rails3项目中使用的数组扩展方法。它应该住在哪里?我有一个应用程序/类,我最初把它放在(array_extensions.rb)中,在我的config/application.rb中我加载路径:config.autoload_paths+=%W(#{Rails.root}/应用程序/类)。但是,当我转到railsconsole时,未加载扩展。是否有一个预定义的位置可以放置我的Rails3扩展方法?或者,一种预先定义的方式来添加它们?我知道Rails有自己的数组扩展方法。我应该将我的添加到active_support/core_ext/array/conversion

  6. ruby - 我需要从 facebook 游戏中抓取数据——使用 ruby - 2

    修改(澄清问题)我已经花了几天时间试图弄清楚如何从Facebook游戏中抓取特定信息;但是,我遇到了一堵又一堵砖墙。据我所知,主要问题如下。我可以使用Chrome的检查元素工具手动查找我需要的html-它似乎位于iframe中。但是,当我尝试抓取该iframe时,它​​是空的(属性除外):如果我使用浏览器的“查看页面源代码”工具,这与我看到的输出相同。我不明白为什么我看不到iframe中的数据。答案不是它是由AJAX之后添加的。(我知道这既是因为“查看页面源代码”可以读取Ajax添加的数据,也是因为我有b/c我一直等到我可以看到数据页面之后才抓取它,但它仍然不存在)。发生这种情况是因为

  7. ruby - 如何在 ruby​​ 中复制目录结构,不包括某些文件扩展名 - 2

    我想编写一个ruby​​脚本来递归复制目录结构,但排除某些文件类型。因此,给定以下目录结构:folder1folder2file1.txtfile2.txtfile3.csfile4.htmlfolder2folder3file4.dll我想复制这个结构,但不包含.txt和.cs文件。因此,生成的目录结构应如下所示:folder1folder2file4.htmlfolder2folder3file4.dll 最佳答案 您可以使用查找模块。这是一个代码片段:require"find"ignored_extensions=[".cs"

  8. ruby - 扩展类和实例 - 2

    这个问题有两个部分。在RubyProgrammingLanguage一书中,有一个使用模块扩展字符串对象和类的示例(第8.1.1节)。第一个问题。为什么如果您使用新方法扩展类,然后创建该类的对象/实例,则无法访问该方法?irb(main):001:0>moduleGreeter;defciao;"Ciao!";end;end=>nilirb(main):002:0>String.extend(Greeter)=>Stringirb(main):003:0>String.ciao=>"Ciao!"irb(main):004:0>x="foobar"=>"foobar"irb(main):

  9. ruby-on-rails - self 在 Rails 模型中的值(value)是什么?为什么没有明显的实例方法可用? - 2

    我的rails3.1.6应用程序中有一个自定义访问器方法,它为一个属性分配一个值,即使该值不存在。my_attr属性是一个序列化的哈希,除非为空白,否则应与给定值合并指定了值,在这种情况下,它将当前值设置为空值。(添加了检查以确保值是它们应该的值,但为简洁起见被删除,因为它们不是我的问题的一部分。)我的setter定义为:defmy_attr=(new_val)cur_val=read_attribute(:my_attr)#storecurrentvalue#makesureweareworkingwithahash,andresetvalueifablankvalueisgiven

  10. ruby - 动态扩展现有方法或覆盖 ruby​​ 中的发送方法 - 2

    假设我们有A、B、C类。Adefself.inherited(sub)#metaprogramminggoeshere#takeclassthathasjustinheritedclassA#andforfooclassesinjectprepare_foo()as#firstlineofmethodthenrunrestofthecodeenddefprepare_foo#=>prepare_foo()neededhere#somecodeendendBprepare_foo()neededhere#somecodeendend如您所见,我正在尝试将foo_prepare()调用注入

随机推荐