4. redis排名系统之C++实战操作对比MySQL

一、MySQL实现方法

假设我们要设计一款排名系统,那必然要涉及到两大类数据:武器数据和非武器的通用数据,它他通常有一个共用的属性:那就是主键唯一的,例如玩家的数字编号,通常在MySQL中是自增的无符号整数字段。

非武器的通用数据可以理解为跟武器没有任何关联的数据,例如玩家ID,昵称,签名,注册时间,登录时间等等,在MySQL中就类似如下:

武器数据这里每一个武器对应的种类就相当于MYSQL中的一个字段,大致类似如下:

这个表单数据通常会只会增加并不会出现减少的现象,如果用MYSQL来做排名系统的话,当玩家数量越来越多成几何增长的时候,它会暴露出两个问题:

1、效率过低:大量的select与update语句就可能会显得非常臃肿;

2、不易扩展:如果需要实时增加新的武器数据类型(字段),可能会不方面

这时候如果用redis来做数据存储就会显示格外合适,仿佛天生就是用来干这个事情的!

二、用Redis实现方法

1、存储前字符串编码转换

    通常不建议在REDIS中直接存储明文字符串,建议采用网页编码来存储,转换函数源码如下:

// 十六进制字符表
const UCHAR			g_szHexTable[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };//
// 获取网页编码
//
int getUrlCode(const UCHAR *pszBuff, const int nSize, char *pszOutput, const int nMaxSize)
{int		i = 0;char	*pszTemp = NULL;if (pszBuff == NULL || nSize == 0)return 0;if (pszOutput == NULL || nMaxSize == 0)return 0;memset(pszOutput, 0x00, nMaxSize);pszTemp = pszOutput;for (int i = 0; i < nSize; i++){pszTemp[0] = '%';pszTemp[1] = '%';pszTemp[2] = g_szHexTable[(pszBuff[i] >> 4) & 0x0F];pszTemp[3] = g_szHexTable[pszBuff[i] & 0x0F];pszTemp += 4;}return nSize * 4;
}// 测试函数
void test_url_code()
{UCHAR	szInput[] = "我要打10个!";char	szOutput[128] = { 0x00 };getUrlCode(szInput, strlen((char *)szInput), szOutput, 128);printf("%s\n", szOutput);
}

2、用户表操作

    我们可以采用Redis Hset 命令用于为哈希表中的字段赋值;如果哈希表不存在,一个新的哈希表被创建并进行 HSET 操作;如果字段已经存在于哈希表中,旧值将被覆;

HSET KEY_NAME FIELD VALUE KEY_NAME: 键值
FIELD:    字段
VALUE:    值(字符传、数字、浮点)

我们在KEY_NAME这里键值的时候,用一个组合方法来区分:

// KEY_NAME = type : id
#define STATS_ALL		"a"		// 总榜 a:1
#define STATS_YEAR		"y"		// 年榜 y2022:1
#define STATS_MONTH		"m"		// 月榜 m202205:1
#define STATS_DAY		"d"		// 日榜 d20220501:1
#define STATS_SESSION	"s"		// 季榜 s1_2022:1
#define STATS_USER		"user"	// 用户信息 user:1

    那么SQL中修改的语句修改用户名的时候,像这样的:

UPDATE dbuser SET name = 'aa' WHERE index = 1;
UPDATE dbuser SET signature = '我要打10个!' WHERE index = 1;
UPDATE dbuser SET regdate = '2023/10/1' WHERE index = 1;

    而在Redis中应该像这样:

// 注意这里的KEY是组合出来的// 设置玩家ID=1的游戏昵称为"aa"
HSET user:1 name %%61%%61// 设置玩家ID=1的游戏昵称为"我要打10个!"
HSET user:1 signature %%CE%%D2%%D2%%AA%%B4%%F2%%31%%30%%B8%%F6%%A3%%A1// 设置玩家ID=1的注册时间为"2023/10/1"
HSET user:1 regdate %%32%%30%%32%%33%%2F%%31%%30%%2F%%31// 设置玩家ID=1的经验值时间为100
HSET user:1 exp 100// 上面redis命令也可以优化成下面的一句执行完
HMSET user:1 name %%61%%61 signature %%CE%%D2%%D2%%AA%%B4%%F2%%31%%30%%B8%%F6%%A3%%A1 regdate %%32%%30%%32%%33%%2F%%31%%30%%2F%%31 exp 100

C++代码片段如下:

	wsprintf(szCommand, /*HSET %s:%u %s %s*/XorStr<0xAB, 17, 0x72ACBFF2>("\xE3\xFF\xE8\xFA\x8F\x95\xC2\x88\x96\xC1\x95\x93\xC4\x98\x9C\xC9" + 0x72ACBFF2).s, STATS_USER, pInfo->m_nDbUid, g_szUserInfo[nPos], szValue);reply = (redisReply *)redisCommand(pRedis, szCommand);if (NULL == reply) goto STEP_END;if (REDIS_REPLY_ERROR == reply->type)goto STEP_END;bRet = TRUE;STEP_END:if (NULL != reply){freeReplyObject(reply);reply = NULL;}

3、武器数据操作

    在这方面数据操作就比较容易实现了,而且能随时扩充字段个数,不像SQL那样固定的个数;另外武器的字段的数值操作都是自加来累计,而不会产生添加或删除操作,比如当网关服务器提交一条玩家ID=1,武器=2,累计杀敌总数自加3的命令时,通常对应的SQL语句如下:

// 修改总榜数据
UPDATE dbweapon_all SET kill = kill + 3 where index = 1 AND wid = 2;// 修改年榜数据
UPDATE dbweapon_2023 SET kill = kill + 3 where index = 1 AND wid = 2;// 修改赛季榜数据
UPDATE dbweapon_s1_2023 SET kill = kill + 3 where index = 1 AND wid = 2;

    而在Redis中应该像这样:

// HINCRBY命令原型
// 参考地址:https://www.runoob.com/redis/hashes-hincrby.html
HINCRBY KEY_NAME FIELD_NAME INCR_BY_NUMBER// redis执行3条命令 
HINCRBY a:1 1:kill 3
HINCRBY y2023:1 1:kill 3
HINCRBY s1_2023:1 1:kill 3键值KEY_NAME注释:
a:1       代表总榜
y2023:1   外表年榜
s1_2023:1 代表2023第一赛季榜字段FIELD_NAME注释:
1:kill    武器编号为1,类型为kill字段INCR_BY_NUMBER代表自加或自减数值

4、MySQL修改积分查询排名

    通常来说需要写一个复杂的MySQL语句来计算玩家的积分,大致如下:

// 修改积分
UPDATE SET db_user exp = exp + 3.0 WHERE index = 1;// 查询积分
SELECT exp FROM db_user WHERE index = 1;// 查询排名
SELECT COUNT(*) + 1 AS rank 
FROM db_user 
WHERE exp >= (SELECT exp FROM db_user WHERE index = 1);

而在redis中本身就有了现成的,通常在网关中处理武器数据时,会同时修改该榜单的积分值

5、Redis Zincrby 修改积分

    Redis Zincrby 命令对有序集合中指定成员的分数加上增量 increment,可以通过传递一个负数值 increment ,让分数减去相应的值,比如 ZINCRBY key -5 member ,就是让 member 的 score 值减去 5 ;当 key 不存在,或分数不是 key 的成员时, ZINCRBY key increment member 等同于 ZADD key increment member;当 key 不是有序集类型时,返回一个错误;分数值可以是整数值或双精度浮点数。

命令原型如下:

ZINCRBY key increment memberkey        同上面的武器榜单
increment  自增或自减的积分
member     玩家id// 假设1号武器杀敌一次累计3分,那对应命令如下:
ZINCRBY a:1 3.0 1
ZINCRBY y2023:1 3.0 1
ZINCRBY s1_2023:1 3.0 1

C++代码片段如下:

	// 总榜记录wsprintf(szCommand, /*ZINCRBY %s %s %u*/XorStr<0x09, 17, 0x5A9256F5>("\x53\x43\x45\x4F\x5F\x4C\x56\x30\x34\x61\x33\x31\x66\x36\x32\x6D" + 0x5A9256F5).s, STATS_ALL, szScore, nDbUid);bRet = redisGetReply(m_pRedis, (void **)&reply);if (bRet != REDIS_OK)goto STEP_END;if (reply){freeReplyObject(reply);reply = NULL;}

6、Redis Zscore 查询积分

    Redis 有序集合(sorted set),Redis Zscore 命令返回有序集中,成员的分数值。 如果成员元素不是有序集 key 的成员,或 key 不存在,返回 nil 。

命令原型如下:

ZSCORE key memberkey        同上面的武器榜单
member     玩家id// 查询不同榜单的积分命令:
ZSCORE a:1 1
ZSCORE y2023:1 1
ZSCORE s1_2023:1 1

C++操作代码片段如下:

	// 获取玩家积分(插件中只获取总榜记录)wsprintf(szCommand, /*ZSCORE %s %u*/XorStr<0x40, 13, 0xF87B9E21>("\x1A\x12\x01\x0C\x16\x00\x66\x62\x3B\x69\x6F\x3E" + 0xF87B9E21).s, STATS_ALL, nDbUid);reply = (redisReply *)redisCommand(pRedis, szCommand);if (NULL == reply) goto STEP_END;if (REDIS_REPLY_ERROR == reply->type)goto STEP_END;if (REDIS_REPLY_STRING == reply->type){fScore = strtod(reply->str, NULL);}if (NULL != reply){freeReplyObject(reply);reply = NULL;}

7、Redis Zrevrank 查询排名

    Redis 有序集合(sorted set),Redis Zrevrank 命令返回有序集中成员的排名。其中有序集成员按分数值递减(从大到小)排序;排名以 0 为底,也就是说, 分数值最大的成员排名为 0 ;使用 ZRANK 命令可以获得成员按分数值递增(从小到大)排列的排名。

命令原型如下:

Zrevrank key memberkey        同上面的武器榜单
member     玩家id// 查询不同榜单的排名命令:
ZREVRANK a:1 1
ZREVRANK y2023:1 1
ZREVRANK s1_2023:1 1

C++操作代码片段如下:

	// 获取在玩家排名(插件中只获取总榜记录)wsprintf(szCommand, /*ZREVRANK %s %u*/XorStr<0x01, 15, 0x9CCD219A>("\x5B\x50\x46\x52\x57\x47\x49\x43\x29\x2F\x78\x2C\x28\x7B" + 0x9CCD219A).s, STATS_ALL, nDbUid);reply = (redisReply *)redisCommand(pRedis, szCommand);if (NULL == reply) goto STEP_END;if (REDIS_REPLY_ERROR == reply->type)goto STEP_END;if (REDIS_REPLY_NIL == reply->type){nRank = pServer->m_nMaxPlayers;bRet = TRUE;}if (REDIS_REPLY_INTEGER == reply->type){nRank = (reply->integer & 0xFFFFFFFF) + 1;bRet = TRUE;}if (NULL != reply){freeReplyObject(reply);reply = NULL;}

三、总结避坑

1、尽量不要使用固定字符串做KEY

在编写排名的时候,一般武器数据的KEY中不会用到像"kill"这种字符类型,通常用一个数字代替方面扩展时,只需要修改enum宏即可,武器 + 数据类型 来拼接 %u:%u 的KEY_NAME,类似下面这种:

// push数据类别				// 命令     字符	积分
enum EnumPushType{					EPT_UNKNOW = '@',		// 未知		64 @	+0EPT_KILL,				// 杀敌		65 A	+2EPT_SHOT,				// 射击		66 B	+0EPT_HEADSHOT,			// 爆头		67 C	+1EPT_HIT,				// 击中		68 D	+0EPT_DAMAGE,				// 伤害		69 E	+0EPT_DEATH,				// 死亡		70 F	-2EPT_FIRSTKILL,			// 首杀		71 G	+1EPT_FIRSTDEATH,			// 首死		72 H	+0EPT_BOMB_DEFUSION,		// 拆除C4	73 I	+2EPT_BOMB_PLANTING,		// 安装C4	74 J	+2EPT_TIME_ONLINE,		// 在线		75 K	+0  每秒+0.002分(每小时7.2分)EPT_KILL_WORLD,			// 摔死		76 L	+0EPT_KILL_SELF,			// 自杀次数	77 M	+0EPT_MAX_PLAYER,			// 最大玩家 78 N	+0EPT_RANK,				// 当前排名 79 O	+0EPT_SCORE,				// 当前积分 80 P	+0// 身体伤害EPT_DMAGE_NONE,			// 击中空枪 81 Q	+0EPT_DMAGE_HEAD,			// 击中头部 82 R	+0EPT_DMAGE_CHEST,		// 击中胸部 83 S	+0EPT_DMAGE_STOMACH,		// 击中胃部 84 T	+0EPT_DMAGE_LEFTARM,		// 击中左臂 85 U	+0EPT_DMAGE_RIGHTARM,		// 击中右臂 86 V	+0EPT_DMAGE_LEFTEG,		// 击中左脚 87 W	+0EPT_DMAGE_RIGHTEG,		// 击中右脚 88 X	+0EPT_DMAGE_SHIELD,		// 击中盾牌 89 Y	+0// 武器+BKILLEPT_BKILL,				// 被击杀	90 Z	+0EPT_BHEAD,				// 被爆头	91 [	+0// 击中次数EPT_HIT_NONE,			// 击中空枪 92 \	+0EPT_HIT_HEAD,			// 击中头部 93 ]	+0EPT_HIT_CHEST,			// 击中胸部 94 ^	+0EPT_HIT_STOMACH,		// 击中胃部 95 _	+0EPT_HIT_LEFTARM,		// 击中左臂 96 `	+0EPT_HIT_RIGHTARM,		// 击中右臂 97 a	+0EPT_HIT_LEFTEG,			// 击中左脚 98 b	+0EPT_HIT_RIGHTEG,		// 击中右脚 99 c	+0EPT_HIT_SHIELD,			// 击中盾牌 100 d	+0// 混战参数 add by MT 2023-09-30EPT_ROUND,				// 总回合EPT_RWIN_T,				// 回合:T杀完胜利EPT_RWIN_BOOM,			// 回合:T爆炸胜利EPT_RWIN_CT,			// 回合:CT杀完胜利EPT_RWIN_DEFUSE,		// 回合:CT爆炸胜利EPT_RWIN_SAVED,			// 回合:CT时间结束胜利EPT_RWIN_RESCUE,		// 回合:CT解救人质胜利EPT_RWIN_NOT_RESCUE,	// 回合:CT未解救人质胜利EPT_RWIN_TYPE1,			// 回合:保留胜利1EPT_RWIN_TYPE2,			// 回合:保留胜利2EPT_RLOSE,				// 失败回合EPT_REVEN,				// 平局回合// 比赛参数EPT_SESSION,			// 总场次EPT_SWIN,				// 胜利场次EPT_SLOSE,				// 失败场次EPT_SEVEN,				// 平局场次EPT_MVP,				// 最佳次数EPT_RWS,				// 每局贡献评分(伤害占比)// 每回合杀敌统计EPT_KILL_0,				// 0K 酱油局EPT_KILL_1,				// 1KEPT_KILL_2,				// 2KEPT_KILL_3,				// 3KEPT_KILL_4,				// 3KEPT_KILL_5,				// 5KEPT_KILL_6,				// 6KEPT_KILL_7,				// 7KEPT_KILL_8,				// 8KEPT_KILL_9,				// 9KEPT_KILL_10,			// 10KEPT_KILL_11,			// 11KEPT_KILL_12,			// 12KEPT_KILL_13,			// 13KEPT_KILL_14,			// 14KEPT_KILL_15,			// 15KEPT_KILL_16,			// 16K// 残局统计EPT_1V1,				// 1v1EPT_1V2,				// 1v2EPT_1V3,				// 1v3EPT_1V4,				// 1v4EPT_1V5,				// 1v5EPT_1V6,				// 1v6EPT_1V7,				// 1v7EPT_1V8,				// 1v8EPT_1V9,				// 1v9EPT_1V10,				// 1v10EPT_1V11,				// 1v11EPT_1V12,				// 1v12EPT_1V13,				// 1v13EPT_1V14,				// 1v14EPT_1V15,				// 1v15EPT_1V16,				// 1v16// 残局信息EPT_1ROUND,				// 残局场次EPT_1RWIN,				// 残局胜利EPT_ASSIST,				// 助攻次数EPT_ADR,				// 场均实际伤害占比累计// 穿墙信息EPT_WALL_HIT,			// 穿墙累计命中次数EPT_WALL_DAMAGE,		// 穿墙累计射击伤害EPT_WALL_HEAD,			// 穿墙累计爆头次数EPT_WALL_KILL,			// 穿墙累计击杀次数EPT_BWALL_HIT,			// 被穿墙累计命中次数EPT_BWALL_DAMAGE,		// 被穿墙累计射击伤害EPT_BWALL_HEAD,			// 被穿墙累计爆头次数EPT_BWALL_KILL,			// 被穿墙累计击杀次数
};

 这一个宏再对应一个double数组来计算得分,如此只需要一个这样的函数作为网关总接口即可:

// 提交排名数据
UINT redis_PushCommand(REDIS_SERVER *pServer, const UINT nDataType, const UINT nWeapon, UINT nDbUid, UINT nValue);

2、尽量用事务方式读写

最后注意一下用事务处理的方法操作,可以加快读写效率,普通的10倍以上,示例代码如下:

	// 新版采用事务方式读取,加快速度for (i = 0; i < MAX_WEAPONS; i++){nWeapon = i + EPT_UNKNOW;for (j = 0; j < MAX_OPT_COUNT; j++){wsprintf(szCommand, /*HGET %s:%u %u:%u*/XorStr<0x18, 17, 0xF6DE906E>("\x50\x5E\x5F\x4F\x3C\x38\x6D\x25\x05\x54\x02\x06\x51\x1F\x03\x52" + 0xF6DE906E).s, STATS_ALL, nDbUid, , EPT_UNKNOW + j, nWeapon);redisAppendCommand(pRedis, szCommand); // 事务读取}}// 遍历所有武器数据for (i = 0; i < MAX_WEAPONS; i++){// 遍历所有参数for (j = 0; j < MAX_OPT_COUNT; j++){nRet = redisGetReply(pRedis, (void **)&reply);if (nRet != REDIS_OK)continue;if (reply->type == REDIS_REPLY_STRING){sData.m_nValue[i][j] = atoi(reply->str);}if (reply->type == REDIS_REPLY_INTEGER){sData.m_nValue[i][j] = reply->integer & 0xFFFFFFFF;}}}

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

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

相关文章

【交付高质量,用户高增长】-用户增长质量保证方法论 | 京东云技术团队

前言 俗话说&#xff0c;“测试是质量的守护者”&#xff0c;但单凭测试本身却远远不够。大多数情况下&#xff0c;测试像“一面镜子”&#xff0c;照出系统的面貌&#xff0c;给开发者提供修改代码的依据&#xff0c;这个“照镜子”的过程&#xff0c;就是质量评估的过程&…

利用Python分析金融交易中的滚动Z值

大家好&#xff0c;在不断演变的证券交易领域&#xff0c;能够利用数据和统计学的力量提供重要的优势。无论是预测未来价格、分析市场趋势&#xff0c;还是简单地评估特定证券的波动性&#xff0c;数据驱动的见解已经改变了交易者对证券市场的处理方式。这就是Z值的用途&#x…

数据结构:二叉排序树

什么是二叉排序树&#xff1f; 二叉排序树要么是空二叉树&#xff0c;要么具有如下特点&#xff1a; 二叉排序树中&#xff0c;如果其根结点有左子树&#xff0c;那么左子树上所有结点的值都小于根结点的值&#xff1b;二叉排序树中&#xff0c;如果其根结点有右子树&#xf…

电脑重做系统---win10

电脑重做系统---win10 前言制作启动U盘材料方法打开网址下载启动盘制作工具参照官方说明进行制作使用U盘重做系统 常用软件官网地址 前言 记得最早学习装电脑还是04年左右&#xff0c;最为一个啥也不知道的大一傻白胖&#xff0c;花了几百大洋在电脑版把了个“电脑组装与维修”…

淘宝店铺商品评论数据采集,淘宝商品评论数据接口,淘宝API接口

采集淘宝店铺商品评论数据的方法如下&#xff1a; 进入主界面&#xff0c;选择"自定义任务"。将商品信息页的网址复制粘贴到网站输入框中&#xff0c;点击"保存设置"。将页面下拉到底部&#xff0c;点击"下一页"按钮&#xff0c;在右侧的操作提…

3. Windows下C++/MFC调用hiredis库操作redis示例

一、头文件目录 将之前下载和编译好的Redis目录拷贝到新建好的工程目录下面&#xff0c;再点击测试工程的右键/属性&#xff0c;点击C/常规&#xff0c;附加包含目录添加以下路径&#xff0c;注意如果原先有多个路径&#xff0c;在末尾处添加分号后再粘贴&#xff1a; 点击C/常…

vue引入jQuery

配置 下载 npm install jquery --save在build的webpack.base.conf中 var webpackrequire("webpack")在module.exports中: plugins: [ //   new webpack.optimize.CommonsChunkPlugin(common.js),new webpack.ProvidePlugin({jQuery: "jquery",$: &quo…

chapter8 Dimensionality Reduction(降维)

设置 首先&#xff0c;确保代码在python2和python3中都能正常工作&#xff0c;导入一些通用模块&#xff0c;确保MatplotLib以内联方式绘制图形&#xff0c;并准备一个函数来保存这些图形: from __future__ import division,print_function,unicode_literalsimport numpy as …

微服务13-Seata的四种分布式事务模式

文章目录 XA模式实现XA模式 AT模式AT模式的脏写问题&#xff08;对同数据并发写的问题&#xff09;其他事务不获取全局锁的一个情况&#xff08;AT模式写隔离的实现&#xff09;实现AT模式 TCC模式TCC实现我们怎么样去判断是否空回滚和业务悬挂&#xff1f;业务分析 Saga模式总…

【学习笔记】项目进行过程中遇到有关composer的问题

composer.json内容详解 以项目中的composer.json为例&#xff0c;参考文档。 name&#xff1a;composer包名type&#xff1a;包的类型&#xff0c;project和library两种keywords&#xff1a;关键词&#xff0c;方便别人在安装时通过关键词检索&#xff08;没试过&#xff0c;好…

Java 中 Volatile 关键字

基本概念 补充一下 java 内存模型中的 可见性、原子性和有序性 可见性&#xff1a; 指的是线程之间的可见性&#xff0c;一个线程修改的状态对另一个线程是可见的。也就是一个线程修改的结果&#xff0c;另一个线程马上可以看到。比如 &#xff1a;用 volatile 修饰的变量&am…

Redis实现简易消息队列的三种方式

Redis实现简易消息队列的三种方式 消息队列简介 消息队列是一种用于在计算机系统中传递和处理数据的重要工具。如果你完全不了解消息队列&#xff0c;不用担心&#xff0c;我将尽力以简单明了的方式来解释它。 首先&#xff0c;想象一下你正在玩一个游戏&#xff0c;而游戏中…

Web应用防火墙的性能优化技术

Web应用防火墙&#xff08;WAF&#xff09;是企业网络安全的重要屏障&#xff0c;其性能直接影响到网络服务的质量和安全。本文详细探讨了WAF性能优化的几种技术&#xff0c;旨在为网络安全专业人员提供实用的参考。 规则优化 1.1 精简规则集 规则评估&#xff1a;定期评估规…

面试题补充

1.公司有几套环境&#xff1a;测试环境&#xff08;测试人员使用&#xff09;&#xff0c;开发环境&#xff08;开发人员使用&#xff09;&#xff0c;预生产环境&#xff08;测试人员使用&#xff09;&#xff0c;生产环境&#xff08;用户使用&#xff09; 2.作为一名测试&a…

opencv跨平台arm交叉编译之ubuntu

目录 1. 安装交叉编译工具链2. 安装依赖3. 配置工具链3.1 新建build目录3.2 安装cmake-gui3.3 工具链配置界面进行配置3.3.1 终端输入以下命令3.3.2 点击Configure&#xff0c;弹出编译方式选择对话框&#xff1a;3.3.3 点击Next3.3.4 点击Finish3.3.5 点击Configure。3.3.6 Ge…

RISC-V 特权级架构

特权级别 级别的数值越大&#xff0c;特权级越高&#xff0c;掌控硬件的能力越强&#xff0c;在CPU硬件层面&#xff0c;M模式必须存在&#xff0c;其它模式可以不存在 执行环境调用 ecall &#xff0c;这是一种很特殊的陷入类的指令&#xff0c; 相邻两特权级软件之间的接口正…

2023年9月Web3行业月度发展报告区块链篇 | 陀螺科技会员专享

9月是加密市场的活动月&#xff0c;斯坦福区块链周、Token2049等大型活动相继举办&#xff0c;后者更是创下超过1万人的历史最高纪录&#xff0c;成为了全球最大的Web3活动。在本次Token2049上&#xff0c;RWA、支付以及出入金成为了讨论度最多的活动。尽管活动如火如荼&#x…

docker入门加实战—docker数据卷

docker入门加实战—docker数据卷 容器是隔离环境&#xff0c;容器内程序的文件、配置等都在容器的内部&#xff0c;要读写容器内的文件非常不方便。 因此&#xff0c;容器提供程序的运行环境&#xff0c;但是程序运行产生的数据、程序运行依赖的配置都应该与容器进行解耦。 …

JUC第二十八讲:JUC工具类: Semaphore详解

JUC工具类: Semaphore详解 本文是JUC第二十八讲&#xff0c;JUC工具类: Semaphore详解。Semaphore底层是基于AbstractQueuedSynchronizer来实现的。Semaphore称为计数信号量&#xff0c;它允许n个任务同时访问某个资源&#xff0c;可以将信号量看做是在向外分发使用资源的许可证…

亳州市的自然风光与旅游资源:欣赏安徽省中部的壮丽景色

亳州市是中国安徽省的一个地级市&#xff0c;位于该省的中部。 亳州市辖区包括谯城区、涡阳县、蒙城县和利辛县等地。亳州市拥有悠久的历史和丰富的文化遗产&#xff0c;同时也以其独特的自然风光而闻名。 首先&#xff0c;让我们来了解一下亳州的历史和景点。亳州的历史可以…