介绍
Fantasy使用的是MongoDB数据库、具体这个数据库是什么我就不多介绍了、资料也很多、大家可以自己搜索一下。Fantasy怎么使用MongoDB来操作数据库已经有视频介绍了如果没有看过的点击这里
普通数据
正常保存一个数据到数据库中大家一般都是这样的,比如一个角色的数据库,这个数据库里的表名咱们暂定义为Role如果保存成功的情况下、数据库里的Role里就会有一行您刚保存的数据了、分别有主键、索引、还有咱们自己定义的内容、比如角色名字、性别等数据
树形结构数据
由于MongoDB是文档形式存储到数据库中的、也就是可以支持一个数据结构的数据、大家可以理解保存的是一个JSON数据、JSON就可以有不止一行数据了。比如多行数据又或者一个树形结构的数据。
Fantasy的Entity是一个树形结构的数据、比如角色(Role)下面可以挂载一个LevelComponent(角色等级组件)。
这种情况如果保存到数据库中、就保存这个整个树形数据到数据库中了、再次数据库读取这个Role数据到程序中后一样是你保存之前的数据结构。
数据过大问题
虽然可以保存一个树形结构数据到一条数据库中了、但其实还是有大小限制的。MongoDB中单条文档的最大大小限制是16MB。虽然正常游戏数据不可能会这么大,但是单条数据过大在读取写入的时候会跟数据造成很大的压力。
就比如游戏中的背包数据、大家知道背包里可能有很多物品、每个物品数据也很大、如果Role下面挂载一个BagComponent(背包组件)、这个组件下面保存着玩家背包的所有物品信息、随着时间这个数据可能会越来越大、就会给数据库早成很大压力。
分库分表策略
面对这样的问题、大家都采用分库分表来缓解数据库的压力、如果做过软件开发的应该很熟悉了、这个是常用的策略了。
Fantasy里也提供了分表的支持、比如角色(Role)下面可以挂载一个LevelComponent、保存数据库的时候会自动把Role和LevelComponent分别存储到单独的一个表中、当读取Role的时候、会自动把Role和LevelComponent加载到程序中、并且自动建立树形结构。
分库就是根据某一个策略把不同数据保存到不同数据库来缓解压力、因为游戏大多都是把数据放到内存中、所以分库用的非常少、咱们框架就不支持了。
比如QQ、当然QQ极大可能不是这样做的、可以按照每5000个号存一个数据库、比如1-5000一个数据库这样存储
框架下的如何进行分表
ISingleCollectionRoot
当实现了这个接口后、Entity保存到数据库的时候会根据子组件设置分离存储特性分表存储在不同的集合表中
public sealed class Role : Entity, ISupportedDataBase, ISingleCollectionRoot
{
public long AccountId;
public string RoleName;
}
这个接口用于定义这个树形接口的第一层、比如Role下面有LevelComponent,那这个接口要定义在Role上
ISupportedSingleCollection
当实现了这个接口后、实体支持单一集合存储的接口。也就是分表存储、当实体需要单独存储在一个集合中,并且在保存到数据库时不会与父组件一起保存在同一个集合中时,应实现此接口、并且需要配合SingleCollectionAttribute这个特性来执行父实体
[SingleCollection(typeof(Role), nameof(LevelComponent))]
public sealed class LevelComponent : Entity, ISupportedSingleCollection
{
public int Level;
}
SingleCollection特性标注在这里组件的头部、用来执行父实体的类型和这个组件分分表存储到数据库的表名,比如我这里设置的父实体是Role、表名是这个组件的名字。这个很重要、如果忘了定义这个是不会分表存储的。
ISupportedSingleCollection在实体实现这个接口后、框架在保存的时候会判定这个需要分表存储然后根据SingleCollection特性的信息来保存数据库。
SingleCollection
如果使用了分表的功能后、需要通过SingleCollection这个单例来进行存储和读取。
SingleCollection.SaveCollections()
保存分表实体数据到数据库中
// 创建一个Role
var role = Entity.Create<Role>(scene);
// Role下挂载LevelComponent组件
role.AddComponent<LevelComponent>();
// 保存Role分表信息
await SingleCollection.Instance.SaveCollections(role);
SingleCollection.GetCollections()
获取当前实体下的所有分表数据
// 数据库中加载Role的数据
var role = await scene.World.DateBase.First<Role>(d => d.AccountId == accountId);
if (role == null)
{
return null;
}
// 数据库加载的需要执行下Deserialize才会注册到框架中
role.Deserialize(scene);
// 加载Unit下面的所有单独存表的子集
await SingleCollection.Instance.GetCollections(role);
当执行完这一步后、LevelComponent组件会自动加载到Role下并成为Role下面的组件
结束语
这个分表功能在游戏开发中十分常用、建议大家把组件都做一个分表、但也不要分的太细、如果不是为了给大家演示功能、这个LevelComponent组件其实就不适合分表因为这个组件里面数据非常小、完全可以跟随Role保存到一起,并没有什么影响。
好的、如果大家有什么问题、欢迎下面评论或群里@我。