Skip to content
ScutGame edited this page Jan 29, 2015 · 12 revisions

此章节介绍如何使用服务端的Cache缓存

目的与用途

面对开发人员使用数据库在操作数据时,一般会使用查询、删除、更新等Sql语句操作数据库;使用开发时间长又不免方便,对于游戏需要频繁读取和更新数据的需求,性能上又满意;那有没有更好的方案呢?这里给大家介绍一种NoSql的Redis内存数据库,它提供高读写的IO性能,可以满足我们的需求。

在Redis内存数据库基础上,为了更好的使用和性能,在服务器端增加一层缓存访问层(CacheStruct),有区分ShareCacheStruct和PersonalCacheStruct两种方式访问缓存数据(下面会介绍),对于常用的数据在第一次使用后,它会存留在内存中,一定时间内(24h)未使用则会自动移除,再次使用时会再次加载;在数据的增加、修改和删除操作上,使用CacheStruct对象来增加、修改和删除数据,它会监听数据的变动,采用异步的方式自动更新到Redis内存数据库中(更新间隔100ms);不需要关心与数据库或Redis内存数据库的交互。

游戏数值类型的配置数据,变动比较小,不需要在程序里对它修改,是只读类型的;此类数据可以使用SQL或MySql数据库存储,CacheStruct可以针对此类数据也可以缓存,提高读取性能;有修改时,可以重载数据。

注意:Redis内存数据库使用

  • 为了读取性能,它的数据也是在内存中的,不能随意关闭Redis内存数据库,需要等待它自动备份或手动备份后,才可关闭,否则数据会丢失;
  • 打开Redis.conf配置文件,搜索“save”配置项可查看它数据的同步频率配置;
  • “dbfilename”配置项是它的存储磁盘文件,用于备份或数据恢复, 需要自己手动备份;

缓存设计

它提供数据加载,数据重载,数据卸载,缓存查找,缓存修改删除等功能。

缓存存储结构

存储方式使用静态的字典模型,数据以结构化的实体对象Entity放入字典模型。

缓存结构:

缓存池(
    缓存容器(
        缓存项[ 
            实体对象 |
            字典对象 |
            队列对象 |
        ]
    )
)

接下来介绍各个结构

1 缓存池(CachePool)结构如下:(实体类名:表示Entity的完整类名)


字典主键   |  字典值项 
----------------------
实体类名   |  缓存容器
----------------------

2 缓存容器(CacheContainer)结构也是一个字典模型,有Entity、Dictionary和Queue几种类型存储。

实体主键:表示Entity的属性有带“EntityField(true)"标记的主键值。

  • Entity类型存储:它用于公有的ShareEntity实体模型存储,使用ShareCacheStruct缓存访问
    字典主键   |  字典值项 
    ----------------------
    实体主键   |  缓存项
    ----------------------
  • Dictionary类型存储:它用于私有的BaseEntity实体模型存储,PersonalId表示数据的所属者ID是Entity的GetIdentity方法返回值,可以使用UserID作为所属者ID,使用PersonalCacheStruct缓存访问
    字典主键   |  字典值项 
    ----------------------
    PersonalId |  缓存项
    ----------------------
  • Queue类型存储:它用于存储一组Entity对象列表,缓存访问需要继承BaseCacheStruct基类扩展
    字典主键   |  字典值项 
    ----------------------
    队列主键   |  缓存项
    ----------------------

3 缓存项(CacheItemSet)

  • 如果是Entity类型存储:存储Entity对象的值

  • 如果是Dictionary类型存储:值也是一个字典模型,如下

    字典主键   |  字典值项 
    ----------------------
    实体主键   |  Entity   
    ----------------------
  • 如果是Queue类型存储:值是一个队列模型,如下
    Queue项
    ----------------------
    Entity1
    Entity2
    ...
    EntityN
    ----------------------

缓存结构特点:

  • Entity结构:一般常用于Key/Value字典取值,查找速度快,加载数据时只能全部一起加载,过期时则全部一起移除;
  • Dictionary结构:针对一类或一组数据的存储方式,结构是字典的字典,查找同一类的数据速度快,加载数据时可以只加载同一类的数据标识ID的数据,过期时只会移除这一类数据,非常适用于存储玩家的数据,以玩家的ID归类;
  • Queue结构:用于存储一组数据,查找速度比较慢;

缓存更新机制

缓存使用Redis内存数据库为主要存储,监听对实体数据的变动操作放入消息队列等待同步处理,再由线程间隔100ms处理保存到到Redis内存数据库。

当然也支持可选配置的方式存储在SQL或MySql数据库,由于数据库的写入性能差,增加一个同步等待消息队列,间隔5分钟同步一次,如果当中有相同的实体多次操作,同步时只会被执行一次,可以减少更新的频率提供更高的性能。

缓存访问层

操作使用缓存的类它不存储缓存数据,只提供操作方法,缓存数据存储在缓存池CachePool对象中,操作缓存分为以下几种:

  • ShareCacheStruct:访问实体模型使用ShareEntity定义的实体对象;
  • PersonalCacheStruct:访问实体模型使用BaseEntity定义的实体对象;

提示:历史版本使用的操作类

  • ConfigCacheSet:早期版本使用访问ShareEntity实体操作类,可以使用此ShareCacheStruct类替换;
  • GameDataCacheSet:早期版本使用访问BaseEntity实体操作类,可以使用此PersonalCacheStruct类替换;

ShareCacheStruct缓存访问类

提供的常用方法

| 方法  | 描述  
| :------------ |:---------------|
| bool Add(Entity)      | 增加到缓存中,且自动同步Redis存储,如果存在不增加并返回false |
| bool AddOrUpdate(Entity)      | 增加到缓存,存在则更新,且自动同步Redis存储 |
| bool Delete(Entity) | 从缓存中删除实体,并同步到Redis |
| bool RemoveCache(Entity) | 从缓存中删除实体, 但不同步 |
| void Foreach(Func<string, T, bool>) | 遍历字典数据,参数是Lamda表达式,第一个参数是主键,第二个参数是实体值,最后是返回值,如果返回false则中断遍历 |
| Entity FindKey(Keys) | 通过实体的主键进行查找,以字典Key的方式取值,时间复杂度(1) |
| Entity Find(Predicate<T>) | 通过Lamda表达式条件匹配查找,取满足条件的第一个,时间复杂度(n) |
| List<Entity> FindAll(Predicate<T>, bool) | 获取所有或通过Lamda表达式条件匹配的数据,参数ture表示以实体主键排序后的结果列表 |

使用示例:

  • 添加操作

  • 修改操作

  • 删除操作

  • 查找操作

PersonalCacheStruct缓存访问类

提供的常用方法

使用示例:

  • 添加操作

  • 修改操作

  • 删除操作

  • 查找操作

自定义缓存模型

自定义数据加载

自定义更新机制

Clone this wiki locally