欢乐西游通用缓存系统设计—应用Redis

2024-05-09 16:08

本文主要是介绍欢乐西游通用缓存系统设计—应用Redis,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一、需求背景:

       欢乐西游是一款手机卡牌游戏,采用分区分服架构,目前已经登录手Q和微信平台。有些全区玩法需要使用到离线玩家数据:1、天梯PVP系统(按竞技积分匹配全区玩家,包括离线玩家)2、大闹天宫(按战斗力匹配全区玩家,包括离线玩家)

       欢乐西采用TcaplusKV方式存储玩家数据,数据读写以角色gid作为key进行,因此通过积分或者战斗力的方式匹配玩家将会非常麻烦。以天梯系统为例,为了通过竞技积分查找玩家,需要Cache玩家离线数据并且建立以积分作为数据索引的数据结构,方便通过竞技积分找出符合要求的角色。PVP玩法都是全区级的,因此Cache数据应该在大区级,便于不同的gamesvr访问。

二、缓存设计关键点:

1、 数据持久化。如果进程coredump或者服务器down机怎么办?缓存数据怎么恢复,是否需要通过数据上报机制来重建缓存?

2、 数据量。一个大区缓存数据量级有多少?能否做到缓存在一台机器还是数据分片在不同机器?

3、 性能问题。缓存全区数据,百万级,能否保证业务需要的读写和匹配性能?

4、 数据结构设计。竞技积分匹配(1000分为例),需要匹配900~1100范围玩家,并且整体匹配满足密度分布,比如[900,920]玩家分布占[900,1100]范围50%的数量,那么从整体来看,匹配到[900,920]的角色要占50%的概率。如何设计数据结构,能够方便管理缓存数据并且能够很好支持匹配规则?

5、 可扩展性。缓存数据数据管理应该具备一定通用和扩展性,例如后续大闹天宫通过战斗力来匹配玩家,扩展新的字段和结构。

三、缓存数据结构设计:

       按积分分段管理缓存数据,例如每20分一个积分段,所有符合当前积分段的玩家放在一起。最终呈现的数据结构如下:

  

       当需要匹配[900,1100]角色时,1、计算出匹配范围涉及到的具体积分段  2、计算出每个积分段人数,再根据每个积分段人数比例算出对应积分段的匹配概率  3、通过概率计算匹配最终落在哪个积分段。

       上面是一个比较通用的匹配算法,但欢乐西游天梯匹配规则更复杂,他会首先从积分段总共匹配200(匹配人数符合积分段人数比例分布),再根据这200人等级和自己等级差按概率分布再匹配。具体细节不在赘述,但总体思想如上描述。

四、架构设计方案一:

       游戏自己实现MatchSvrcache玩家离线数据,实现匹配逻辑。使用共享内存方式存储cache数据,保证不停机更新后数据不丢失。

优缺点分析:

1、自己实现一个MatchSvr的缓存系统,会增加开发工作量和复杂度。

2、等级段100+个,每个等级段角色数不固定,用链表来维护增大实现难度。同时,在匹配对手中,需要随机匹配也会增大链表操作的复杂度,查找和删除需要遍历链表,性能低。

3MatchSvr缓存数据落地磁盘,保证停机后的数据恢复。磁盘落地可以每天晚上开个线程一条一条写磁盘,整个开发量较大。或者使用mmap映射磁盘文件到内存,定时msync同步内存脏数据到磁盘,但这样内存固定,如果以后加字段或新增系统会非常麻烦。

4、扩展性不够好,没有一个通用的缓存系统,新系统需要额外开发。

五、架构设计方案二:

       针对方案一,其实可以将匹配逻辑和缓存数据分离,解耦合。缓存数据层是一个通用的存储和数据管理方案,后续新增字段和数据都能非常方便扩展,常驻进程,修改少,稳定,支持数据持久化。匹配逻辑层用于读写缓存数据并且应用各种匹配规则,无状态服务,即使匹配逻辑修改,只需要更新逻辑层。

       我们采用开源的Redis作为缓存数据层,提供高效数据管理和持久化功能。Redis是一款开源的、高性能的key-value存储。Redis 和其他很多 key-value 数据库的不同之处在于, Redis 不仅支持简单的字符串键值对,它还提供了一系列数据结构类型值, 比如列表、哈希、集合和有序集, 并在这些数据结构类型上定义了一套强大的 API 。通过对不同类型的值进行操作, Redis 可以很轻易地完成其他只支持字符串键值对的 key-value 数据库很难(或者无法)完成的任务。例如天梯系统一个积分段对应一个集合,在redis层,key就是积分段,value就是集合,可以方便查找删除等操作。

Redis 分别提供了 RDB  AOF 两种持久化模式。在 Redis 运行时, RDB 程序将当前内存中的数据库快照保存到磁盘文件中 Redis 重启动时,RDB 程序可以通过载入 RDB 文件来还原数据库的状态。

Redis提供通用的数据缓存服务,服务各个需要的子模块。这个进程类似mysql,只需要启动一次,以后不再启停。CacheProxy使用tapp多线程模式,每个线程同步访问redis。经测试,基本数据接口操作本机2.5W/s,跨机器3500/s。因此,CacheProxy3~5个线程同步访问不会有任何性能问题。当然,使用cacheproxy能够保证和redis部署在同一台机器上。

优点:

1、 Redis 不仅支持简单的字符串键值对,它的Value还支持列表、哈希、集合和有序集, 并在这些数据结构类型上定义了一套强大的 API  在具体应用时,key是一个具体积分段,value就是积分段下所有角色数据组成的集合,Redis底层实现用hashmap方式,集合元素可以动态增加,查找删除都是O(1)时间复杂度。因此可以很轻易解决方案一12点。

2、 Redis支持RDB持久化模式,通过配置,可以定期将全量数据写到磁盘文件中。Redisfork一个子进程,由子进程完成写磁盘的工作。

3、 提供通用的Cache数据服务,拥有较好的扩展性,后续新增系统都可以来使用

4、 节省开发工作,不需要重复实现各种数据结构管理,数据落地等模块。

5 Redis提供强大的客户端,可以方便查看各种数据和key,便于开发测试和运维,而且还有很多脚本工具,便于后期数据分析。

一些思考:

      独立一个CacheProxy进程?

     最早的想法是直接通过interface来访问Redis服务,但可能会有两个缺点:

     1、 Interface作为world<=>zone, zone<=>zone之间的公共纽带,作用日益加重。Interface直接影响到天梯系统、帮派系统、登录踢人、idip请求、邮件体力通知、公告等。为了保证interface本身作为消息转发的简单和稳定,将redis客户端api访问独立出来会安全许多

     2、 Redis客户端访问Redis服务采用多线程同步方式。如果interfaceRedis不在同一个机器,将会极大影响同步访问性能(具体参照上面数据,性能瓶颈在网络io层。按照目前的服务器部署,world层会使用一台机器,interfaceredis分布在一台机器肯定不会影响访问性能,但以后可能会将interface部署到不同机器,保证interface容灾。

      同步还是异步访问redis

异步访问优点:

1、性能非常高。单线程简单读写压测显示,异步20W+/s, 同步2.5W/s

2、异步访问基本不受跨机器影响,瓶颈在redis自身数据处理速度。

但异步相比同步,也有些缺点:

1、异步api本身教复杂,需要用到libevent或者libev库事件机制驱动。 

2、异步api会使业务流程变得非常复杂。例如天梯系统更新一个玩家积分,涉及到更新玩家积分数据、从原来积分段删除、添加到新的积分段三个步骤,每个步骤都需要回调处理。而且cacheproxy本身无状态,不希望有数据缓存来支持回调处理。

综上:使用同步接口已经拥有非常高的性能,而且使用cacheproxy能够保证和redis部署在同一台机器,性能完全满足业务需求。同时,同步接口api极为简单,上述更新玩家积分的3个操作,都可以当成本地直接完成,业务流程很清晰。

六、运行流程:

l  上报竞技积分

l  匹配对手

七、压测性能:

Cacheproxy采用多线程同步方式,在v8.2机型压测(816G内存),缓存数据1000W,内存占用2G,按上述实际综合业务场景压测数据如下:

Cacheproxy线程数

上报积分/s

Cacheproxy单线程cpu占用

Redis Cpu占用

1

0.89W

47%

42%

2

1.62W

41%

58%

3

2.38W

38%

76%

4

2.72W

31%

84%

5

2.56W

26%

81%

6

2.46W

22%

72%

7

2.29W

15%

66%

15

1.95W

7%

58%

 

Cacheproxy线程数

匹配数/s

Cacheproxy单线程cpu占用

Redis Cpu占用

1

3030

31%

62%

2

4370

22%

84%

3

4500

16%

85%

4

5050

15%

86%

5

5100

12%

86%

6

5700

11%

88%

7

5700

10%

88%

15

5000

4%

82%

综上:cacheproxy线程并发4~5个性能最好。当线程数较少时,同步访问瓶颈在网络IO层。当线程数过多时,由于cpu8核,其他线程会抢占Rediscpu资源,导致Redis本身数据处理减慢。

八、运营数据:

欢乐西游是分区架构,redis服务部署在每个大区,cacheproxy并发5个线程,以191太上老君大区为例:

注册用户:50W

Redis内存占用:100M

Redis RDB备份文件:28M

Pvp竞技上报+匹配峰值:44 * 5 =220/s (tnm2每分钟统计峰值44/s,瞬间峰值按5倍算)

CacheproxyRedis cpu占用在1%左右,基本可以忽略。

以下是一些性能截图:

九、容灾:

       根据业务场景需求,pvp缓存数据不是十分关键的数据,哪怕全部丢失也可以自动上报,重新建立缓存数据。

       因此Redis采用RDB模式备份数据,每天全量备份一次到本地磁盘,并拷贝一份到其他机器。即使Redis挂了或者整个机器down机,也能通过加载RDB文件非常方便的恢复数据。哪怕恢复的数据是老的,对于玩家也是毫无感知。

       如果整个world机器down机,gamesvr层在一定时间收不到心跳包也会将world层置为超时。这时匹配流程会转入匹配机器人,玩家还是能进行竞技玩法,提供一定程度有损服务。直到world机器恢复,匹配流程将自动切换回正常模式。

十、其他:

       总体而言,匹配逻辑层和数据层分离,数据层采用redis具有良好的扩展性,数据管理、持久化、高性能都能非常好的满足业务需求。后续的大闹天宫系统,通过战斗力匹配离线玩家,也使用类似的方式,1天时间就完成整个数据上报和匹配流程,极大简化了开发流程。

       当然,Redis还有非常丰富的功能,集群管理、AOF备份等机制,我们只是用到其中很小的一部分功能,满足业务需求并具有一定扩展性就可以,关键是能够方便部署,不需要额外的机器资源成本和运维成本。

       使用Redis经验不多,希望大家多多指正。  

这篇关于欢乐西游通用缓存系统设计—应用Redis的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/973849

相关文章

不懂推荐算法也能设计推荐系统

本文以商业化应用推荐为例,告诉我们不懂推荐算法的产品,也能从产品侧出发, 设计出一款不错的推荐系统。 相信很多新手产品,看到算法二字,多是懵圈的。 什么排序算法、最短路径等都是相对传统的算法(注:传统是指科班出身的产品都会接触过)。但对于推荐算法,多数产品对着网上搜到的资源,都会无从下手。特别当某些推荐算法 和 “AI”扯上关系后,更是加大了理解的难度。 但,不了解推荐算法,就无法做推荐系

中文分词jieba库的使用与实景应用(一)

知识星球:https://articles.zsxq.com/id_fxvgc803qmr2.html 目录 一.定义: 精确模式(默认模式): 全模式: 搜索引擎模式: paddle 模式(基于深度学习的分词模式): 二 自定义词典 三.文本解析   调整词出现的频率 四. 关键词提取 A. 基于TF-IDF算法的关键词提取 B. 基于TextRank算法的关键词提取

基于人工智能的图像分类系统

目录 引言项目背景环境准备 硬件要求软件安装与配置系统设计 系统架构关键技术代码示例 数据预处理模型训练模型预测应用场景结论 1. 引言 图像分类是计算机视觉中的一个重要任务,目标是自动识别图像中的对象类别。通过卷积神经网络(CNN)等深度学习技术,我们可以构建高效的图像分类系统,广泛应用于自动驾驶、医疗影像诊断、监控分析等领域。本文将介绍如何构建一个基于人工智能的图像分类系统,包括环境

水位雨量在线监测系统概述及应用介绍

在当今社会,随着科技的飞速发展,各种智能监测系统已成为保障公共安全、促进资源管理和环境保护的重要工具。其中,水位雨量在线监测系统作为自然灾害预警、水资源管理及水利工程运行的关键技术,其重要性不言而喻。 一、水位雨量在线监测系统的基本原理 水位雨量在线监测系统主要由数据采集单元、数据传输网络、数据处理中心及用户终端四大部分构成,形成了一个完整的闭环系统。 数据采集单元:这是系统的“眼睛”,

csu 1446 Problem J Modified LCS (扩展欧几里得算法的简单应用)

这是一道扩展欧几里得算法的简单应用题,这题是在湖南多校训练赛中队友ac的一道题,在比赛之后请教了队友,然后自己把它a掉 这也是自己独自做扩展欧几里得算法的题目 题意:把题意转变下就变成了:求d1*x - d2*y = f2 - f1的解,很明显用exgcd来解 下面介绍一下exgcd的一些知识点:求ax + by = c的解 一、首先求ax + by = gcd(a,b)的解 这个

hdu1394(线段树点更新的应用)

题意:求一个序列经过一定的操作得到的序列的最小逆序数 这题会用到逆序数的一个性质,在0到n-1这些数字组成的乱序排列,将第一个数字A移到最后一位,得到的逆序数为res-a+(n-a-1) 知道上面的知识点后,可以用暴力来解 代码如下: #include<iostream>#include<algorithm>#include<cstring>#include<stack>#in

嵌入式QT开发:构建高效智能的嵌入式系统

摘要: 本文深入探讨了嵌入式 QT 相关的各个方面。从 QT 框架的基础架构和核心概念出发,详细阐述了其在嵌入式环境中的优势与特点。文中分析了嵌入式 QT 的开发环境搭建过程,包括交叉编译工具链的配置等关键步骤。进一步探讨了嵌入式 QT 的界面设计与开发,涵盖了从基本控件的使用到复杂界面布局的构建。同时也深入研究了信号与槽机制在嵌入式系统中的应用,以及嵌入式 QT 与硬件设备的交互,包括输入输出设

JAVA智听未来一站式有声阅读平台听书系统小程序源码

智听未来,一站式有声阅读平台听书系统 🌟&nbsp;开篇:遇见未来,从“智听”开始 在这个快节奏的时代,你是否渴望在忙碌的间隙,找到一片属于自己的宁静角落?是否梦想着能随时随地,沉浸在知识的海洋,或是故事的奇幻世界里?今天,就让我带你一起探索“智听未来”——这一站式有声阅读平台听书系统,它正悄悄改变着我们的阅读方式,让未来触手可及! 📚&nbsp;第一站:海量资源,应有尽有 走进“智听

zoj3820(树的直径的应用)

题意:在一颗树上找两个点,使得所有点到选择与其更近的一个点的距离的最大值最小。 思路:如果是选择一个点的话,那么点就是直径的中点。现在考虑两个点的情况,先求树的直径,再把直径最中间的边去掉,再求剩下的两个子树中直径的中点。 代码如下: #include <stdio.h>#include <string.h>#include <algorithm>#include <map>#

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]