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

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

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

public class ConfigPlayerLevelStorage implements Reloadable {private Map<Integer, ConfigPlayerLevel> levels = new HashMap<>();private Map<Integer, Long> level2Exp = new HashMap<>();@Overridepublic 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,先保证所有缓存的数据已经完整再切换引用,代码如下

@Overridepublic 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;       // 临时执行行数1this.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;}}

这种方式能完美解决所有缓存的状态一致性问题,可以说无懈可击。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/695268.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Elasticsearch:了解人工智能搜索算法

作者&#xff1a;来自 Elastic Jessica Taylor, Aditya Tripathi 人工智能工具无处不在&#xff0c;其原因并不神秘。 他们可以执行各种各样的任务并找到许多日常问题的解决方案。 但这些应用程序的好坏取决于它们的人工智能搜索算法。 简单来说&#xff0c;人工智能搜索算法是…

采用uniapp实现的银行卡卡片, 支持H5和微信小程序

采用uniapp-vue3实现的银行卡卡片 支持H5、微信小程序&#xff08;其他小程序未测试过&#xff0c;可自行尝试&#xff09; 可用于参考学习 可到插件市场下载尝试&#xff1a; https://ext.dcloud.net.cn/plugin?id16736 使用示例

《Python 语音转换简易速速上手小册》第2章 Python 编程基础(2024 最新版)

文章目录 2.1 Python 语言基础2.1.1 基础知识深入基础总结 2.1.2 主要案例&#xff1a;数据分析脚本案例介绍案例 Demo案例分析 2.1.3 扩展案例 1&#xff1a;自动化邮件发送案例介绍案例 Demo案例分析 2.1.4 扩展案例 2&#xff1a;网页数据抓取案例介绍案例 Demo案例分析 2.2…

将本地项目上传到svn服务端和git

一、SVN 1.创建svn库,下面生成了三个文件夹,branches指分支,trunk下可以放项目 2.在本地checkout,填入svn库的地址,因为是新建的,所以checkout的是空文件夹 把自己的项目复制到trunk下,在项目上 右键-TortoiseSVN-add add完之后 右键-svn commit 3.idea打开这个项目,将项目跟…

C++面试宝典第31题:有效的数独

题目 判断一个9 x 9的数独是否有效。只需要根据以下规则,验证已经填入的数字是否有效即可。 1、数字1-9在每一行只能出现一次。 2、数字1-9在每一列只能出现一次。 3、数字1-9在每一个以粗实线分隔的3 x 3宫内只能出现一次。 下图是一个部分填充的有效的数独,数独部分空格内已…

爬虫在网页抓取的过程中可能会遇到哪些问题?

在网页抓取&#xff08;爬虫&#xff09;过程中&#xff0c;开发者可能会遇到多种问题&#xff0c;以下是一些常见问题及其解决方案&#xff1a; 1. IP封锁&#xff1a; 问题&#xff1a;封IP是最常见的问题&#xff0c;抓取的目标网站会识别并封锁频繁请求的IP地址。 解决方案…

Js如何判断两个数组是否相等?

本文目录 1、通过数组自带方法比较2、通过循环判断3、toString()4、join()5、JSON.stringify() 日常开发&#xff0c;时不时会遇到需要判定2个数组是否相等的情况&#xff0c;需要实现考虑的场景有&#xff1a; 先判断长度&#xff0c;长度不等必然不等元素位置其他情况考虑 1…

Spring Boot中的@Scheduled注解:定时任务的原理与实现

1. 前言 本文将详细探讨Spring Boot中Scheduled注解的使用&#xff0c;包括其原理、实现流程、步骤和代码示例。通过本文&#xff0c;读者将能够了解如何在Spring Boot应用中轻松创建和管理定时任务。 2. Scheduled注解简介 在Spring框架中&#xff0c;Scheduled注解用于标记…

《Python 语音转换简易速速上手小册》第5章 音频数据处理(2024 最新版)

文章目录 5.1 音频数据的基本处理5.1.1 基础知识5.1.2 主要案例&#xff1a;音频剪辑工具案例介绍案例 Demo案例分析 5.1.3 扩展案例 1&#xff1a;自动音量调节器案例介绍案例 Demo案例分析 5.1.4 扩展案例 2&#xff1a;语音识别预处理案例介绍案例 Demo案例分析 5.2 使用 Py…

手把手教您安装2024最新版微信消息防撤回插件

文章目录 &#x1f4d6; 介绍 &#x1f4d6;&#x1f3e1; 环境 &#x1f3e1;&#x1f4d2; 使用方法 &#x1f4d2;&#x1f4dd; 步骤一&#x1f4dd; 步骤二 ⚓️ 相关链接 ⚓️ &#x1f4d6; 介绍 &#x1f4d6; 本文与大家分享一个大神开发的2024最新版本的微信消息防撤…

Tomcat 学习之 Servlet

目录 1 Servlet 介绍 2 创建一个 Servlet 3 web.xml 介绍&#xff08;不涉及 filter 和 listener 标签&#xff09; 3.1 display-name 3.2 welcome-file-list 3.3 servlet 3.4 session-config 3.5 error-page 4 Tomcat 如何根据 URL 定位到 Servlet 5 执行 Servlet …

不破不立,那些年错过的Python

随着OpenAI的发展&#xff0c;Python的重要性不言而喻。不知你是否和我一样&#xff0c;不知道曾经说过多少次我要学Python&#xff0c;都没有执行起来… 近期我在知识库中更新了一波Python教程&#xff0c;选取了这一篇分享给大家。 前言 很多时候我们需要让程序变成交互性的…

Linux系统——nginx服务介绍

一、Nginx——高性能的Web服务端 Nginx的高并发性能优于httpd服务 1.nginx概述 Nginx是由1994年毕业于俄罗斯国立莫斯科鲍曼科技大学的同学为俄罗斯rambler.ru公司开发的&#xff0c;开发工作最早从2002年开始&#xff0c;第一次公开发布时间是2004年10月4日&#xff0c;版本…

第二证券:美国加息对中国股市的影响?美联储加息利好还是利空股市?

美国加息是一种紧缩的钱银政策&#xff0c;会招引出资者添加银行的出资&#xff0c;导致社会上的流动钱银减少&#xff0c;然后间接地导致股市流动资金减少&#xff0c;股市跌落&#xff0c;引起商场上的出资者恐慌&#xff0c;大量卖出&#xff0c;添加商场上的空方力量&#…

电路设计(25)——4位数字频率计的multism仿真及PCB设计

1.设计要求 使用4位数码管&#xff0c;显示输入信号的频率。完成功能仿真后&#xff0c;用AD软件&#xff0c;画出原理图以及PCB。 2.电路设计 输入信号的参数为&#xff1a; 可见&#xff0c;输入为168HZ&#xff0c;测量值为170HZ&#xff0c;误差在可接受的范围内。 3.PCB设…

分享Video.js观看Web视频流

界面效果 HTML结构 <div class"homePopup" ><div class"search_box animate__animated animate__fadeInDown" style"display: none;"><div class"van-search" style"background: rgba(0, 0, 0, 0);">&…

[服务器-数据库]MongoDBv7.0.4不支持ipv6访问

文章目录 MongoDBv7.0.4不支持ipv6访问错误描述问题分析错误原因解决方式 MongoDBv7.0.4不支持ipv6访问 错误描述 报错如下描述 Cannot connect to MongoDB.No suitable servers found: serverSelectionTimeoutMS expired: [failed to resolve 2408]问题分析 首先确定其是…

Android14 InputManager-InputReader的处理

IMS启动时会调用InputReader.start()方法 InputReader.cpp status_t InputReader::start() {if (mThread) {return ALREADY_EXISTS;}mThread std::make_unique<InputThread>("InputReader", [this]() { loopOnce(); }, [this]() { mEventHub->wake(); });…

Java SE 入门到精通—基础语法【Java】

敲重点&#xff01; 本篇讲述了比较重要的基础&#xff0c;是必须要掌握的 1.程序入口 在Java中&#xff0c;main方法是程序的入口点&#xff0c;是JVM&#xff08;Java虚拟机&#xff09;执行Java应用程序的起始点。 main方法的方法签名必须遵循下面规范&#xff1a; publ…

【力扣白嫖日记】1987.上级经理已离职的公司员工

前言 练习sql语句&#xff0c;所有题目来自于力扣&#xff08;https://leetcode.cn/problemset/database/&#xff09;的免费数据库练习题。 今日题目&#xff1a; 1978.上级经理已离职的公司员工 表&#xff1a;Employees 列名类型employee_idintnamevarcharmanager_idint…