-
Notifications
You must be signed in to change notification settings - Fork 572
Cache
此章节介绍如何使用服务端的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
类替换;
提供的常用方法:
| 方法 | 描述
| :------------ |:---------------|
| 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表示以实体主键排序后的结果列表 |
使用示例:
-
添加操作
-
修改操作
-
删除操作
-
查找操作
提供的常用方法:
使用示例:
-
添加操作
-
修改操作
-
删除操作
-
查找操作