一、游戏系统中的Redis
(一)Redis In Gaming Servers :分区分服
游戏的架构非常多,总结来看基本有三种,第一种是分区分服的游戏,第二种是全区全服,第三种是全球同服。最近全同服的游戏比较火,线上做了很多收割,而分区分服也适用于一些游戏场景。
Redis的使用场景可以分为两种,一种场景是当缓存使用,另一种场景是当内存数据库使用。Redis的使用场景在国内外的定位是不一样的,在国外大多数Redis都是作为缓存使用,但在国内,可以说70%的用户是当做内存数据库。
下面就用一个分区分服的场景来介绍下Redis的典型用法。
在游戏登录的时候,这些关系型数据,包括用户数据、用户账单、计费记录等肯定是在关系型数据库里。这里Redis的特点是,比如有一个注册活动或者新服开放,当大量的用户进来后,我们就需要使用一些缓存来抗住压力,
所以这里的Redis的使用主要是缓存场景。
第二块是分区分服里最核心的GameServer。
GameServer是一个强内存的计算型服务,它不会有核心的服务在数据库里面,但它的一些周边服务,比如排行榜,还有一些通用的服务,比如分布式锁,对战匹配,选角记录等都通常放到Redis里边。
还有一块是图中间的部分。当GameServer变更到一些数据,如游戏用户的经验变化,游戏获得的装备,这些核心关键数据也是存入关系型数据库,因为这些东西都是游戏资产,上方红色部分的场景在RDS里是做缓存。
除此之外还有一些周边系统,在这种分区分服游戏里,一般也有一些跨服对战。消息队列,mailbox等。这些数据也经常放在Redis中,使用方便,性能也很好。
还有最后一块是上图右下角的部分,游戏公司经常会做一些活动,比如做一些宣传,还有类似于防作弊、防木马的情况,需要把用户日志落盘,这个每天生成的数据量很大,一般都会放到一个Data Warehouse或者一个分析型数据库如ADB里面供后续挖掘。此时游戏公司还需要分析活动是否成功,是否需要做投放,Redis在这里提供工单缓存/分析加速。
以上是Redis在分区分服游戏中的一些使用场景。
(二)Redis In Gaming Servers:全球同服
另一个是最近热门的全球同服场景,上图是它最基本的技术架构。
全球同服的特点是,所有的国内外节点都是访问了同一份数据库。游戏行业的一个特点是对数据库的性能要求并不高,但是对网络和计算的要求非常高。在游戏行业里,我们常说最远的距离可能不是数据到数据库的距离,而是“电信到联通”的距离。在这种全球同服游戏架构里,有一个很复杂的问题就是数据既然集中了存储在一个地方,那么在长距离访问时就会遇到严重的体验问题。实际上,在这种全球同服的游戏架构里,它都会有一个Cache Svr,把远端的数据都缓存在本地,然后让本地的访问去增删查改它,降低访问时延,这是全球同服游戏架构里的一个核心技术。在cache server技术中,由于Redis本身可以作为很好的缓存,所以这里使用得比较多一点。
由于数据是集中式存储,当所有游戏用户都去访问一个大数据库的时候,要求还是很高的。对于Redis而言,一般都有一个Redis Cluster可以提供访问。
以上是Redis在全球同服游戏里的使用场景。
(三)游戏对Redis的要求
如果游戏要用做高速存储、内存数据库等,那么它对Redis有哪些要求?
首先,对数据库的要求是可靠性。对于Redis来说,在电商场景和游戏场景中可靠性要求都很高,但是在例如链路管理上还是差别较大。例如,对于电商体系而言,链路管理的特点是出问题就断,宁可返回一个错误的结果,也不会Block,因为所有的电商链路都是No Blocking,出现问题断了就好,下次再处理。但是游戏行业不一样,它需要很好的链路保持能力,可以慢慢解决,但是链接不能断,因为断了之后后端的业务就断掉了,业务层还挂着各种游戏的资源信息等,这是游戏行业跟电商行业不一样的一个地方。
一般情况下,很多游戏数据库平均的访问量都不大,但是不能出问题,第一是链路不能断,第二是如果扛不住的时候,要求能够平滑扩缩容。它对平滑性的要求特别高,因为当一个游戏突然火了,或者刚开服时有很多用户去注册登录的时候形成注册风暴,会发生大量扩容的行为,如果在扩容速度或者平滑性上有所欠缺,那么用户体验就特别差。
另外,在做一个全球同服游戏的时候需要考虑容灾和多活的能力。高可靠性对数据库类似于保险,抗热点、抗压力是保险,平滑扩容也是保险。
游戏对数据库有高可运维的要求。电商数据库基本不会涉及太多的运维,因为每天的工单,包括交易,购物,交易快照等都是存下来的,最多索引一下,不会来回搬数据。但是对游戏而言,由于每天产生的数据和日志特别多,并且游戏运营迭代特别快,比如有些分区分服的游戏,尤其做滚服、合服,迅速开一个新的服务器,把数据库导过去,然后去滚动,这些游戏运营模式对数据运维要求特别高。当游戏用户反馈说在游戏中丢了东西,我们需要帮他进行查找,因此需要支持数据恢复,还有内省、清洗等。
此外还有低成本。游戏行业有很明确的冷热效应,游戏刚开始特别火,等游戏热度慢慢降下来之后,需要迅速把数据存下来换出去,降低运营成本,这也是非常关键的一点。低成本考察的是介质,像Redis这种数据库,如果数据量都放到内存里,那么成本一定比较高。有一块节省成本的措施是极致弹性,比如今天游戏做活动,我们把数据给弹上去,明天再拆下来,只要有足够弹性,那么成本是可控的。
最后是快速开发。游戏行业的在开发上的特点是快速迭代,竞争比较激烈,推出游戏的速度特别快,如果成功,则马上准备下一版,不成功就很快收掉做下一个游戏。所以如果有框架支持,更多的API,提供更多的封装和语法糖,那么它可以很迅速地搭建适合自己的游戏体系。
二、云原生内存数据库Tair介绍
云原生内存数据库Tair(Redis企业版)是阿里云推出的支持高并发低延迟访问的云原生内存数据库,完全兼容Redis数据结构和API。支持主从与集群架构,采用多样存储介质应对不同数据温度场景,并提供全球多活、数据闪回和丰富的数据模型等特性,致力于帮助客户构建在线实时场景。
Tair兼容了Redis的数据结构,那么面向游戏行业,和Redis的差别主要是什么呢?
首先是容灾方面,开源的自建Redis没有很可靠容灾,但阿里云Tair通过可靠的高精度容灾模块,能够做到快速容灾,比如10秒级就可以切换掉一个有问题的数据库。
第二个是链接保持能力,游戏领域对链接保持的需求很高,这和电商、直播等领域不太一样。Tair通过代理层的热升级能力,将链接尽可能保持,确保了升级、宕机等多种情况下的可用性。
第三,一般数据库做容灾就是主备容灾,用Redis的时候也可以搭个主备出来,阿里云Tair除了采用多可用区容灾或跨可用区容灾之外,还提供了跨域多活容灾的能力。注意的是跨域容灾不一定是多活,但多活一定是跨域容灾。
还有就是备份,备份很关键,可以防止删库跑路等情况。游戏行业的特点是发版本的速度特别快,如果版本发错则需要快速回滚。Tair提供任意时间点的数据恢复,当游戏厂商发现发布的版本有问题,可以马上回滚,降低影响。除此之外,Tair还支持秒级数据闪回这样更精密的数据回滚能力,可以指定过去任意秒级时间点按key/key pattern或者全量恢复数据。
在这种日常运维中,可观测性是需要建设的重点,业务在运行中不可避免会遇到实时热Key、大Key。由于游戏迭代速度很快,质量方面难免偶尔出现问题,我们能够通过实时和历史Key分析快速找到这些瓶颈并解决问题。在Tair中除去能够迅速观测到热点,还能够迅速把热点加速掉,我们称之为散列技术,可以把热点的抗压能力轻松提升到10~30倍。
成本上,从我们的角度来看Redis,往内存里边放的东西越多,成本就越高,内存是所有的存储里最贵的。但是阿里云Tair有更好方式,我们有Tair持久内存型,很好地解决了存储成本和访问性能的矛盾。最早做持久内存的时候,它没有什么企业级特性,后续我们把企业级能力都搬到持久内存上,如今大家去买持久内存的话,就具备这些企业级能力,包括PITR、多活、加速等。
开源Redis的好处在于它跟一般的Key-Value不一样,它存很多结构化数据,跟数据结构更贴合一点,用得比较快一点。但是我们发现用户真正用起来的时候,这些结果是不够的。游戏用户大量使用JSON,比如存用户属性、道具等,都是JSON结构。所以Tair引擎里已内置了JSON结构,那么用户用起来就非常方便了。
三、Tair游戏场景介绍
(一)Tair全球多活
Tair有一个典型的场景是全球多活。
这个是线上的一个例子,例如游戏里中国用户可以到美国的商店里去买一个光环装备,然后这个用户在国内服务器上就具有了一个光环。
全球多活是基于引擎高可靠的数据多活服务,Tair提供全球同服的高效Cache Server,数据库本身具备多活,数据写入一个地方,那么所有地方都可见。
多活在游戏里基本是用在Cache Server上,所有互联网类的用户都会有一个登录Session系统,如果要去把Session系统做大,做一个很可靠的全球多活Session,Tair也能支持。
(二)数据闪回 - 任意时间点数据恢复
Tair另外一个经典场景是任意时间点的数据恢复,它实际上是数据安全的一部分。数据安全包括链路加密,也包括存储加密,还包括多副本等。
但任意时间点数据恢复这个场景游戏客户用得最多,大约占90%。游戏厂商在发版本的时候,如果出现问题可以快速回滚,也支持只回滚某一些东西。回滚既支持把数据恢复到源,也支持克隆一个新实例来恢复。
(三)CAS/CAD 高性能分布式锁
刚才我们从分区分服的服务器里边也看到了,Redis本身会作为Game Server的一些周边服务,最简单一个例子就是分布式锁。多个机器同时去拿一个资源的时候,牵扯到资源竞争肯定要有锁,业务需要通过分布式锁来解决。但这个分布式锁很少有人实现的对,所以我们就直接把分布式锁做在这个引擎里面了。
分布式锁比起一般的锁,它除了有互斥性,还有租约性。一般会在锁上加一个Timer,设置锁多长时间,当租约到期后,锁自动释放。
分布式锁存在的问题不是加锁的地方,而是释放的地方。举个例子,比如一个应用程序去加了锁,应用程序没有挂,但是时间拖得长一点,然后等到引擎里面的Timer超时,锁就释放了。结果第二个应用程序就拿到锁了。当这个APP又跑起来的时候,我们一删锁就把别人的锁给删掉了,会引发资源错误。
因此,难点在删除这一步,删除时的要求是只能删自己的锁,不能删别人的锁。所以很多的云上用户,如果用去del命令去删Key的话就删错了,正确的实现是一个事务操作,而Tair直接在引擎里实现了分布式锁。
阿里内部90%的分布式锁都是使用Tair的高性能分布式锁,包括许多游戏客户也经常使用。
(四)TairDoc:游戏业务快速迭代
另一个核心的module能力就是TairDoc,因为JSON数据结构是游戏用户里最大的特征结构,我们更新JSON的时候不需要该表,直接在对应的JSON数据中增加相应信息即可。
(五)TairZset: 多维/多条件实时排序
排行榜是游戏场景的最常见应用,也是Redis的一个主要应用场景,甚至有些大型游戏公司有一个专门的排行榜组,给全平台游戏人员提供排行榜服务,而非直接暴露一个Redis zset接口。在具体开发实现中,会遇到如下两个主要的问题。
第一个问题是在对战的时候,玩家选择排行榜上的其他玩家PK,肯定也是选一个分数和时间差不多的,因此,做排行榜的时候是多维度的。例如在直播网站,可以看到在线人数,粉丝点赞数,礼物金额等。如果按照一个个维度去做的时候,那么很难做一个很精确的使用。开源的Redis只能提供一维的排行榜,而Tair则可以提供包含多个Score的多维排行榜,这样的多维/多条件实时排序能力很好地解决了这个问题,开发者使用起来也会非常的敏捷顺手。
第二个问题是当游戏特别大的时候,例如游戏用户有2000万人,那么2000万人的排行榜是非常大的了,在这样规模排行榜程序设计上把所有的用户都存在一个Key下面,Redis就会发生数据倾斜,性能也不是特别好,而如果要把 Key拆开,因为排行榜既不符合分配率,也不符合交换率,所以拆的话也比较难。针对这个痛点,通过TairJedis更可以创建分布式排行榜,非常敏捷的构建大规模、高灵活度的排行榜应用,用户可以根据业务容忍度和性能诉求选择分布式精确/非精确排行榜而不用关注实现细节,让游戏开发者能够更加敏捷地进行游戏逻辑的开发,这些细节都是通过SDK层结合低层TairZset能力帮助用户构建起来的。
后续Tair的产品发展过程将会针对游戏行业的更多场景提供更为灵活敏捷的能力,同时在客户降本增效上做进一步探索。