图数据库在游戏开发中使用思考(下)

在游戏开发设计中,玩家作为一个游戏对象,他们之间可能会存在多种关系,比如:

  • 好友关系;
  • 组队关系;
  • 公会关系;
  • 买卖关系;
  • ……

无论是使用关系型数据库还是key/value型的NoSQL ,按照传统思路对于这些关系的存储结构实现大同小异。例如:玩家会有独立的好友关系数据块,数据块内部至少存储着好友唯一ID,可以通过好友ID索引到好友相关信息;公会存在唯一ID,通过这个ID拉取公会成员信息和其他信息;组队与公会类似…

传统数据库与图数据库思路比对

我们列举一下好友与公会的常规操作,在不考虑合法性校验的情况下:

操作传统方式图数据库方式
玩家A和B成为好友玩家A和B分别在好友数据块增加对方ID两对象A和B之间增加
玩家A和B解除好友 玩家A和B分别在好友数据块删除对方ID 两对象A和B之间删除
玩家A加入公会C玩家A记录所属公会ID,公会C在成员列表加入玩家A两对象A和C之间增加
玩家A离开公会C 玩家A删除所属公会ID,公会C在成员列表删除玩家A 两对象A和C之间删除

直观感觉上使用图数据库方式更简洁一些,传统方式若做得严谨一些可能还需要考虑事务的支持,根据数据库的选型不同会导致数据一先一后写入,需处理先写入成功后写入失败的场景。

为了凸显差异性,这次我们再假设一些更复杂的操作,比如在推荐系统中常见的场景,同样也是不考虑合法性校验的情况下:

操作传统方式图数据库方式
查看好友的好友先取得玩家A的好友列表,遍历好友列表,取得每个好友的好友列表边遍历
查找哪些好友的好友与你同公会先取得玩家A的好友列表,遍历好友列表,取得每个好友的好友列表,在好友的好友数据过滤出同公会ID的玩家 边条件遍历

同样的,还是感觉上使用图数据库方式更简洁一些,那么具体简洁在哪里,下面拿用实际的例子来说明。

环境搭建

这里我们选择NebulaGraph作为图数据库演示软件,安装步骤可以查看官方文档,有多种安装方式供选择。我们假设有如下图示关系:

使用NebulaGraphnGQL对上图进行点、关系建立:

//创建图空间game_test并切换
(root@nebula) [(none)]> CREATE SPACE IF NOT EXISTS game_test (vid_type=FIXED_STRING(128))
(root@nebula) [(none)]> use game_test

//创建两种边(EDGE),分别表示好友与公会从属关系,设置了建立关系时时间戳属性
(root@nebula) [game_test]> CREATE EDGE IF NOT EXISTS friend (tm timestamp)
(root@nebula) [game_test]> CREATE EDGE IF NOT EXISTS member (tm timestamp)

//创建两种Tag,分别用来标记玩家和公会,设置了唯一ID属性
(root@nebula) [game_test]> CREATE TAG IF NOT EXISTS player (uid int NOT NULL)
(root@nebula) [game_test]> CREATE TAG IF NOT EXISTS guild (uid int NOT NULL)

//创建点,玩家A、B、C、D和公会G
(root@nebula) [game_test]> INSERT VERTEX IF NOT EXISTS player (uid) VALUES "Player_A":(10001)
(root@nebula) [game_test]> INSERT VERTEX IF NOT EXISTS player (uid) VALUES "Player_B":(10002)
(root@nebula) [game_test]> INSERT VERTEX IF NOT EXISTS player (uid) VALUES "Player_C":(10003)
(root@nebula) [game_test]> INSERT VERTEX IF NOT EXISTS player (uid) VALUES "Player_D":(10004)
(root@nebula) [game_test]> INSERT VERTEX IF NOT EXISTS guild (uid) VALUES "Guild_G":(20001)

//使用边来连接点,表示关系。好友因为是相互关系,可以认为两点间好友关系存在互相指向的两条边
(root@nebula) [game_test]> INSERT EDGE IF NOT EXISTS friend (tm) \
VALUES "Player_A"->"Player_B":(timestamp()),"Player_B"->"Player_A":(timestamp())
(root@nebula) [game_test]> INSERT EDGE IF NOT EXISTS friend (tm) \
VALUES "Player_B"->"Player_C":(timestamp()),"Player_C"->"Player_B":(timestamp())
(root@nebula) [game_test]> INSERT EDGE IF NOT EXISTS friend (tm) \
VALUES "Player_B"->"Player_D":(timestamp()),"Player_D"->"Player_B":(timestamp())
(root@nebula) [game_test]> INSERT EDGE IF NOT EXISTS member (tm) \
VALUES "Player_A"->"Guild_G":(timestamp())
(root@nebula) [game_test]> INSERT EDGE IF NOT EXISTS member (tm) \
VALUES "Player_C"->"Guild_G":(timestamp())

//查看好友关系
(root@nebula) [game_test]> MATCH ()-[e:friend]->() RETURN e limit 10
//查看公会关系
(root@nebula) [game_test]> MATCH ()-[e:member]->() RETURN e limit 10

至此,我们预设的环境准备完毕。

利用图数据库解决问题

  • 查看好友的好友

假设现在需要对Player_A做好友推荐,希望推荐ta好友的好友(按照图例所示结果应该推荐Player_C和Player_D),可以使用这样的nGQL语句:

(root@nebula) [game_test]> GO 2 STEPS FROM "Player_A" OVER friend \
Where id($$) != "Player_A" YIELD dst(edge);

//结果
+------------+
| dst(EDGE)  |
+------------+
| "Player_D" |
| "Player_C" |
+------------+
  • 查找哪些好友的好友与你同公会

假设我们现在需要找到Player_A所属公会成员里,有哪些是Player_A好友的好友(结果应该是Player_C),可以使用这样的语句:

//假设已经知道Player_A属于公会是Guild_G
(root@nebula) [game_test]> GO 2 STEPS FROM "Player_A" OVER friend \
Where id($$) != "Player_A" YIELD dst(edge) AS dstid | \ 
GO FROM $-.dstid OVER member Where id($$) == "Guild_G" YIELD src(edge);

//结果
+------------+
| src(EDGE)  |
+------------+
| "Player_C" |
+------------+

除了这几个例子,相似的需要处理关联数据的场景应该还有不少。比起传统处理过程中冗长的数据拉取筛选过程或者复杂的SQL语句,图数据库在处理关联数据上更清晰、简洁、效率更高。

写在最后

图数据库近10年来也是发展最快的数据库类型,国内游戏公司除了网易之外,目前曝光出来将此类型数据库用在游戏开发中的公司基本很少。

虽然图数据库设计的初衷不是为了解决游戏开发相关的一些问题,但随着游戏系统的复杂度、数据关联性不断增强,期待图数据库在游戏领域被广泛传播的那一天。

(全文结束)


转载文章请注明出处:漫漫路 - lanindex.com

Leave a Comment

Your email address will not be published.