游戏配置二级缓存一致性问题解决方案

这篇具有很好参考价值的文章主要介绍了游戏配置二级缓存一致性问题解决方案。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

游戏服务器进程在启动的时候,一般会把所有策划配置数据加载到内存里,将主键以及对应的记录存放在一个HashMap容器里,这称为一级缓存。部分功能可能还需要缓存其他数据,这些称为二级缓存。举个例子,对于如下的玩家升级表记录

游戏配置二级缓存一致性问题解决方案,漫谈游戏服务器,游戏,配置缓存

程序缓存level与ConfigPlayerLevel的对应关系,同时也缓存level与needExp的对应关系。

public class ConfigPlayerLevelStorage implements Reloadable {

	private Map<Integer, ConfigPlayerLevel> levels = new HashMap<>();

	private Map<Integer, Long> level2Exp = new HashMap<>();

	@Override
	public void reload() {
		String sql = "SELECT * FROM ConfigPlayerLevel";
		try {
			List<ConfigPlayerLevel> datas = DbUtils.queryMany(DbUtils.DB_DATA, sql, ConfigPlayerLevel.class);
			levels = datas.stream().collect(Collectors.toMap(ConfigPlayerLevel::getLevel, Function.identity()));
			level2Exp = new HashMap<>();
			levels.forEach((x,y)->{
				level2Exp.put(x, y.getNeedExp());
			});
		} catch (Exception e) {
			LoggerUtils.error("", e);
		}
	}

}

其中的reload()方法,代表程序在运行期间进行配置数据重载(这是运营项目是非常常见的)。这段代码初看起来没什么问题,但在程序运行期间遇到线程并发问题就会出现二级缓存状态不一致问题。假设热更新配置的线程在执行”level2Exp = new HashMap<>();“这行代码,level2Exp的数据已经被清空了,但同时业务线程访问level2Exp这个缓存的时候,就会出现level2Exp是完整数据,而level2Exp是初始数据的情况了。

这个问题的解决思路有两种(通过加锁来分离读写的方式不现实,即麻烦又低效)

1,先保证所有缓存的数据已经完整再切换引用,代码如下

@Override
	public void reload() {
		String sql = "SELECT * FROM ConfigPlayerLevel";
		try {
			List<ConfigPlayerLevel> datas = DbUtils.queryMany(DbUtils.DB_DATA, sql, ConfigPlayerLevel.class);
			Map<Integer, ConfigPlayerLevel> _levels = datas.stream().collect(Collectors.toMap(ConfigPlayerLevel::getLevel, Function.identity()));
			Map<Integer, Long> _level2Exp = new HashMap<>();
			_levels.forEach((x,y)->{
				_level2Exp.put(x, y.getNeedExp());
			});

			this.levels = _levels;       // 临时执行行数1
			this.level2Exp = _level2Exp; // 临时执行行数2

		} catch (Exception e) {
			LoggerUtils.error("", e);
		}
	}

如此修改,即使出现线程并发问题,也能保证一级二级缓存的数据是完整的。

但细心的朋友也发现了,假设热更新线程指定到临时执行代码1的时候,业务线程就开始访问level2Exp的数据,这个时候仍然存在状态不一致的问题。(业务线程访问了热更后的一级缓存和热更前的二级缓存,这种情况虽然不甚完美,但在业务上是容许的)

2,直接替换配置容器。说得高大上一点,就是clojure在处理并发所采用的方式,分离标识与状态(程序在获取一个标识的当前状态,无论将来对这个标识怎样修改,获取的那个状态将不再改变。)

当热更新的时候,我们先重新初始化一个新的配置容器,等所有缓存的数据填充完毕之后,再把原先旧的配置容器整个替换掉。参考代码如下:

public class ConfigDataPool {

	private static ConfigDataPool instance = new ConfigDataPool();
	public static ConfigDataPool getInstance() {
		return instance;
	}

	private ConcurrentMap<Class<?>, Reloadable> datas = new ConcurrentHashMap<>();
	/**
	 * 单表重载
	 * @param configTableName 配置表名称
	 */
	public boolean reload(String configTableName) {
		for (Map.Entry<Class<?>, Reloadable> entry : datas.entrySet()) {
			Class<?> c = entry.getKey();
			if (c.getSimpleName().toLowerCase().indexOf(configTableName.toLowerCase()) >= 0) {
				try {
                    // 初始化新的数据容器
					Reloadable storage = (Reloadable) c.newInstance();
                    // 读取新数据,并缓存
					storage.reload();
                    // 替换旧容器
					datas.put(c, storage);
					return true;
				} catch (Exception e) {
					LoggerUtils.error(c.getName() + "配置数据重载异常", e);
				} 
				break;
			}
		}
		return false;
	}

}

这种方式能完美解决所有缓存的状态一致性问题,可以说无懈可击。文章来源地址https://www.toymoban.com/news/detail-835571.html

到了这里,关于游戏配置二级缓存一致性问题解决方案的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • ARMv8-缓存一致性(cache coherency)解决方案:MOESI protocol

    目录 一,MOESI状态释义 二,MOESI状态转换    1, Invalid after Reset 2, Invalid = Exclusive 3, Exclusive = Modified  4.1, Modified = Owned, Invalid = Shared  4.2 Modified = Invalid , Invalid = Modified 5,Owned = Invalid, Shared = Invalid, Shared = Modified  6,Clean 和Invalidate操作对MOESI状态的影响 6.1 对 Owned状态进行clea

    2024年02月08日
    浏览(39)
  • Redis缓存(双写一致性问题)

    前言 : 什么是缓存? 缓存就像自行车,越野车的避震器 举个例子:越野车,山地自行车,都拥有\\\"避震器\\\", 防止 车体加速后因惯性,在酷似\\\"U\\\"字母的地形上飞跃,硬着陆导致的 损害 ,像个弹簧一样; 同样,实际开发中,系统也需要\\\"避震器\\\",防止过高的数据访问猛冲系统,导致其操作线程无法

    2024年02月02日
    浏览(59)
  • 缓存和数据库一致性问题分析

    目录 1、数据不一致的原因 1.1 并发操作 1.2 非原子操作 1.3 数据库主从同步延迟 2、数据不一致的解决方案 2.1 并发操作 2.2 非原子操作 2.3 主从同步延迟 2.4 最终方案 3、不同场景下的特殊考虑 3.1 读多写少的场景 3.2 读少写多的场景 导致缓存和数据库数据不一致的原因有三个

    2024年02月14日
    浏览(39)
  • Redis生产实战-热key、大key解决方案、数据库与缓存最终一致性解决方案

    热 key 问题就是某一瞬间可能某条内容特别火爆,大量的请求去访问这个数据,那么这样的 key 就是热 key,往往这样的 key 也是存储在了一个 redis 节点中,对该节点压力很大 那么对于热 key 的处理就是通过热 key 探测系统对热 key 进行计数,一旦发现了热 key,就将热 key 在 jv

    2024年02月05日
    浏览(49)
  • Redis缓存问题(穿透, 击穿, 雪崩, 污染, 一致性)

    目录 1.什么是Redis缓存问题? 2.缓存穿透 3.缓存击穿 4.缓存雪崩 5.缓存污染(或满了)    5.1 最大缓存设置多大    5.2 缓存淘汰策略 6.数据库和缓存一致性    6.1 4种相关模式    6.2 方案:队列+重试机制    6.3 方案:异步更新缓存(基于订阅binlog的同步机制) 在高并发的业

    2024年02月12日
    浏览(30)
  • 深入理解高并发下的MySQL与Redis缓存一致性问题(增删改查数据缓存的一致性、Canal、分布式系统CAP定理、BASE理论、强、弱一致性、顺序、线性、因果、最终一致性)

    一些小型项目,或极少有并发的项目,这些策略在无并发情况下,不会有什么问题。 读数据策略:有缓存则读缓存,然后接口返回。没有缓存,查询出数据,载入缓存,然后接口返回。 写数据策略:数据发生了变动,先删除缓存,再更新数据,等下次读取的时候载入缓存,

    2024年03月20日
    浏览(48)
  • Redis缓存问题:穿透,击穿,雪崩,双写一致性等

    在高并发场景下,数据库往往是最薄弱的环节,我们通常选择使用 redis 来进行缓存,以起到缓冲作用,来降低数据库的压力,但是一旦缓存出现问题,也会导致数据库瞬间压力过大甚至崩溃,从而导致整个系统崩溃.今天就聊聊常见的 redis 缓存问题. 缓存击穿 缓存击穿一般指redis中的一

    2024年04月27日
    浏览(48)
  • Redis 性能管理/优化 双一致性问题 缓存雪崩/击穿/穿透

    used_memory_rss:是Redis向操作系统申请的内存。 used_memory:是Redis中的数据占用的内存。 mem_fragmentation_ratio:内存碎片率。 used_memory_peak:redis内存使用的峰值。 内存碎片如何产生的? Redis内部有自己的内存管理器,为了提高内存使用的效率,来对内存的申请和释放进行管理。

    2024年02月11日
    浏览(47)
  • 如何保证Redis缓存和数据库的一致性问题

    熟练掌握Redis缓存技术? 那么请问Redis缓存中有几种读写策略,又是如何保证与数据库的一致性问题 今天来聊一聊常用的三种缓存读写策略 首先我们来思考一个问题 写 先更新缓存 再更新数据库 首先如果缓存更新成功但数据库更新失败,会导致数据不一致的问题 其次当请求

    2024年02月14日
    浏览(48)
  • Redis如何保障缓存与数据库的数据一致性问题?

    目录 一.最经典的数据库加缓存的双写双删模式 二. 高并发场景下的缓存+数据库双写不一致问题分析与解决方案设计 三、上面高并发的场景下,该解决方案要注意的问题 1.1 Cache Aside Pattern概念以及读写逻辑 (1)读的时候,先读缓存,缓存没有的话,那么就读数据库,然后取

    2023年04月21日
    浏览(47)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包