预分配法生成唯一ID 预分配内存是什么意思
yuyutoo 2024-10-18 12:11 3 浏览 0 评论
在项目中经常有生成唯一ID的业务需求。作为唯一标识方便后续查找和追踪。网上也有很多实现方案。比如:数据库自增主键,数据库批量生成ID,UUID,时间戳方法,zookeeper生成ID,redis生成唯一ID,snowflake算法等等。这些解决方案各有优缺点。我在游戏项目常用的解决方案是:预分配法生成唯一ID。其原理简单,利于扩展,具有较强的适用性。
预分配法生成唯一ID原理:数据库中存储每种类型最后的预分配dbUniqueId值。数据库实体对象上增加当前分配的useDbUniqueId。预分配步长DBUNIQUEID_M可根据类型和业务量自定义。当useDbUniqueId大于等于dbUniqueId时就预分配出DBUNIQUEID_M个唯一ID。
角色ID生成:要求7位连续的唯一ID。则DBUNIQUEID_M=1。1000000+dbUniqueId格式化成7位数字。
本服唯一ID:则DBUNIQUEID_M=10000。(1000000+dbUniqueId)*10000+serverId格式化而成。其中serverId游戏服ID定义为4位
全局唯一ID:则DBUNIQUEID_M=10000。((100000000000L+dbUniqueId)100+gameCode10000+workerId;格式化而成。其中gameCode游戏编码定义为2位。
具体实现请参阅代码。非常方便的移植到项目工程中。
IdWorkerEnum.java
package com.game.common.db;
import java.util.List;
import com.core.enums.LLIndexedEnum;
/**
* ID枚举类型
* @author Thinker
*/
public enum IdWorkerEnum implements LLIndexedEnum
{
/** 角色ID */
IDWORKER_HUMANID(1,"角色ID"),
/** 数据库ID */
IDWORKER_DBID(2,"数据库ID"),
/** 工会ID */
IDWORKER_CLUBID(3,"工会ID"),
/** 收集ID */
IDWORKER_COLLECTID(4,"收集ID"),;
/** 枚举的索引 */
public final int index;
/** 名称的key */
private final String nameKey;
/** 按索引顺序存放的枚举数组 */
private static final List<IdWorkerEnum> indexes=LLIndexedEnum.IndexedEnumUtil.toIndexes(IdWorkerEnum.values());
private IdWorkerEnum(int index,String nameKey)
{
this.index=index;
this.nameKey=nameKey;
}
/**
* 获取索引
*/
@Override
public int getIndex()
{
return index;
}
/**
* 取得名称key
*/
public String getNameKey()
{
return this.nameKey;
}
/**
* 根据指定的索引获取枚举的定义
*/
public static IdWorkerEnum valueOf(final int index)
{
return LLIndexedEnum.IndexedEnumUtil.valueOf(indexes,index);
}
}
DbUniqueIdEntity.java
package com.game.common.db;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import org.eclipse.persistence.annotations.HashPartitioning;
import org.eclipse.persistence.annotations.Partitioned;
import com.game.common.db.dao.BaseDbBean;
/**
* 数据库唯一KEY实体类
* @author Thinker
*/
@Entity(name="t_dbUniqueId")
@HashPartitioning(name="HashPartitionByDbUniqueCacheId",partitionColumn=@Column(name="dbUniqueCacheId"))
@Partitioned("HashPartitionByDbUniqueCacheId")
public class DbUniqueIdEntity extends BaseDbBean
{
/** 数据库唯一缓存ID **/
@Id
@Column(length=20)
private long dbUniqueCacheId;
/** 数据库唯一ID **/
@Column(length=20)
private long dbUniqueId;
/** 最后更新的时间 **/
@Column(length=20)
private long createTime;
// ////////////////内部数据//////////////////
/** 已经使用过的数据库唯一ID(唯一ID) */
@Column(length=20)
private long useDbUniqueId;
@Override
public long getId()
{
return dbUniqueCacheId;
}
@Override
public void setId(long id)
{
}
public long getDbUniqueCacheId()
{
return dbUniqueCacheId;
}
public void setDbUniqueCacheId(long dbUniqueCacheId)
{
this.dbUniqueCacheId=dbUniqueCacheId;
}
public long getDbUniqueId()
{
return dbUniqueId;
}
public void setDbUniqueId(long dbUniqueId)
{
this.dbUniqueId=dbUniqueId;
}
public long getCreateTime()
{
return createTime;
}
public void setCreateTime(long createTime)
{
this.createTime=createTime;
}
public long getUseDbUniqueId()
{
return useDbUniqueId;
}
public void setUseDbUniqueId(long useDbUniqueId)
{
this.useDbUniqueId=useDbUniqueId;
}
}
IdWorker.java
package com.game.common.db;
import com.game.common.Globals;
import com.game.common.entityProxy.EntityProxy;
import com.game.core.appConfig.AppConfigServ;
import com.game.core.utils.LoggerUtils;
/**
* 唯一ID生成器
*
* @author Thinker
*/
public class IdWorker
{
public final static IdWorker OBJ=new IdWorker(AppConfigServ.OBJ.getAppConfigBean().getServerNo());
private int DBUNIQUEID_M=2000;
private final long workerId;
/** 数据库连接池 */
private DbUniqueIdEntity[] szDbUniqueIdEntity=new DbUniqueIdEntity[5];
public IdWorker(long workerId)
{
this.workerId=workerId;
}
/**
* 生成系统唯一ID:采用隐性锁方式
*/
public synchronized long nextId()
{
IdWorkerEnum idWorkerEnum=IdWorkerEnum.IDWORKER_DBID;
int dbUniqueCacheId=idWorkerEnum.getIndex();
loadDbUniqueId(idWorkerEnum);
DbUniqueIdEntity dbUniqueIdEntity=szDbUniqueIdEntity[dbUniqueCacheId];
long dbUniqueId=0;
// 需要重新分配Id:每次分配 M个
if(dbUniqueIdEntity.getUseDbUniqueId()>=dbUniqueIdEntity.getDbUniqueId())
{
dbUniqueIdEntity.setDbUniqueId(dbUniqueIdEntity.getDbUniqueId()+DBUNIQUEID_M);
EntityProxy.OBJ.update((long) dbUniqueCacheId,dbUniqueIdEntity);
LoggerUtils.serverLogger.info("IdWorker DbId:update dbUniqueId="+dbUniqueIdEntity.getDbUniqueId());
}
dbUniqueId=dbUniqueIdEntity.getUseDbUniqueId()+1;
dbUniqueIdEntity.setUseDbUniqueId(dbUniqueId);
// 11位UUID
return (1000000+dbUniqueId)*10000+workerId;
}
/**
* 生成系统唯一HumanID:采用隐性锁方式
*/
public synchronized long nextHumanId()
{
IdWorkerEnum idWorkerEnum=IdWorkerEnum.IDWORKER_HUMANID;
int dbUniqueCacheId=idWorkerEnum.getIndex();
loadDbUniqueId(idWorkerEnum);
DbUniqueIdEntity dbUniqueIdEntity=szDbUniqueIdEntity[dbUniqueCacheId];
long dbUniqueId=0;
// 需要重新分配Id:每次分配 M个
if(dbUniqueIdEntity.getUseDbUniqueId()>=dbUniqueIdEntity.getDbUniqueId())
{
dbUniqueIdEntity.setDbUniqueId(dbUniqueIdEntity.getDbUniqueId()+1);
EntityProxy.OBJ.update((long) dbUniqueCacheId,dbUniqueIdEntity);
LoggerUtils.serverLogger.info("IdWorker HumanId:update dbUniqueId="+dbUniqueIdEntity.getDbUniqueId());
}
dbUniqueId=dbUniqueIdEntity.getUseDbUniqueId()+1;
dbUniqueIdEntity.setUseDbUniqueId(dbUniqueId);
// 7位UUID
return (1000000+dbUniqueId);
}
/**
* 生成系统唯一ClubID:采用隐性锁方式
*/
public synchronized long nextClubId()
{
IdWorkerEnum idWorkerEnum=IdWorkerEnum.IDWORKER_CLUBID;
int dbUniqueCacheId=idWorkerEnum.getIndex();
loadDbUniqueId(idWorkerEnum);
DbUniqueIdEntity dbUniqueIdEntity=szDbUniqueIdEntity[dbUniqueCacheId];
long dbUniqueId=0;
// 需要重新分配Id:每次分配 M个
if(dbUniqueIdEntity.getUseDbUniqueId()>=dbUniqueIdEntity.getDbUniqueId())
{
dbUniqueIdEntity.setDbUniqueId(dbUniqueIdEntity.getDbUniqueId()+1);
EntityProxy.OBJ.update((long) dbUniqueCacheId,dbUniqueIdEntity);
LoggerUtils.serverLogger.info("IdWorker clubId:update dbUniqueId="+dbUniqueIdEntity.getDbUniqueId());
}
dbUniqueId=dbUniqueIdEntity.getUseDbUniqueId()+1;
dbUniqueIdEntity.setUseDbUniqueId(dbUniqueId);
// 5位UUID
return (10000+dbUniqueId);
}
/**
* 生成系统唯一ID:采用隐性锁方式
*/
public synchronized long nextCollectId()
{
IdWorkerEnum idWorkerEnum=IdWorkerEnum.IDWORKER_COLLECTID;
int dbUniqueCacheId=idWorkerEnum.getIndex();
loadDbUniqueId(idWorkerEnum);
DbUniqueIdEntity dbUniqueIdEntity=szDbUniqueIdEntity[dbUniqueCacheId];
long dbUniqueId=0;
// 需要重新分配Id:每次分配 M个
if(dbUniqueIdEntity.getUseDbUniqueId()>=dbUniqueIdEntity.getDbUniqueId())
{
dbUniqueIdEntity.setDbUniqueId(dbUniqueIdEntity.getDbUniqueId()+DBUNIQUEID_M);
EntityProxy.OBJ.update((long) dbUniqueCacheId,dbUniqueIdEntity);
LoggerUtils.serverLogger.info("IdWorker DbId:update dbUniqueId="+dbUniqueIdEntity.getDbUniqueId());
}
dbUniqueId=dbUniqueIdEntity.getUseDbUniqueId()+1;
dbUniqueIdEntity.setUseDbUniqueId(dbUniqueId);
// 18位UUID:游戏ID+服务器ID。确保支持多游戏不重复的ID。便于兼容统计
return ((100000000000L+dbUniqueId)*100+AppConfigServ.OBJ.getAppConfigBean().getGameCode())*10000+workerId;
}
/**
* 加载唯一ID
*/
public void loadDbUniqueId(IdWorkerEnum idWorkerEnum)
{
int dbUniqueCacheId=idWorkerEnum.getIndex();
if(szDbUniqueIdEntity[dbUniqueCacheId]!=null) return;
DbUniqueIdEntity dbUniqueIdEntity=EntityProxy.OBJ.get((long) dbUniqueCacheId,(long) dbUniqueCacheId,DbUniqueIdEntity.class);
if(dbUniqueIdEntity==null)
{
dbUniqueIdEntity=new DbUniqueIdEntity();
dbUniqueIdEntity.setDbUniqueCacheId(dbUniqueCacheId);
dbUniqueIdEntity.setDbUniqueId(0);
dbUniqueIdEntity.setCreateTime(Globals.getTimeService().now());
dbUniqueIdEntity.setUseDbUniqueId(0);
EntityProxy.OBJ.insert((long) dbUniqueCacheId,dbUniqueIdEntity);
}
dbUniqueIdEntity.setUseDbUniqueId(dbUniqueIdEntity.getDbUniqueId());
szDbUniqueIdEntity[dbUniqueCacheId]=dbUniqueIdEntity;
LoggerUtils.serverLogger.info("IdWorker:load dbUniqueId="+dbUniqueIdEntity.getDbUniqueId());
}
}
相关推荐
- 高一高二第一次月考认真作答(高二第一次月考的重要性)
-
正在进行高一、高二第一次月考,同学们正在认真完成化学试卷,研究考纲,探究考点,夯实基础,迎战高考!
- 山清水秀,盛世今朝(山清水秀出处)
-
万千星河,神州妖娆!山清水秀,盛世今朝!龙腾虎跃,锦绣前程!千里婵娟,祝福永远!
- 我校二模成绩已新鲜出炉(二模考试成绩)
-
充电加油备战高考,积极努力再拼一搏...
- Argon Design向瑞萨电子有限公司提供Argon Streams VP9许可证
-
英国剑桥--(美国商业资讯)--领先的先进视频验证解决方案提供商ArgonDesignLtd已与日本半导体公司瑞萨电子有限公司(RenesasElectronicsCorporation)签署...
- 高考倒计时75天(高考倒计时75天励志语)
-
今天是2022年3月24日星期四,距离2022年高考还有75天时间对于十八岁的高三学子来说,有些事情的确会影响你们的一生,但是没有一件事能决定你们的一生!努力的意义,就是:以后的日子里,放眼望去,全...
- 期中考试正在进行(期中考试在即)
-
转眼即瞬,期中考试已到,紧张忙碌的两个月学习,检验的时刻到了。让我们拿出信心和勇气,来挑战自我。面对考验,我们该做的就是沉着,冷静。让知识来一次次洗礼我们的灵魂,让失败和成功迎接一次次的成长。你们可以...
- 不要浪费了你NAS上的HDMI接口!详解华硕NAS上HDMI接口的妙用
-
不要浪费了你NAS上的HDMI接口!详解华硕NAS上HDMI接口的妙用之前我在本站分享我使用的华硕(ASUS)AS6704T...
- Java通过Kafka Streams库来实现数据流处理
-
#暑期创作大赛#...
- From abandoned mines to limpid streams waters: how banks profit from EOD
-
ByZENGYanglinInthecurrentpursuitofthe“dualcarbon”target(carbonpeakingandcarbonneutra...
- SPSS与Streams的集成实现实时预测
-
SPSSModeler是一个数据挖掘工作台,提供了一个可了解数据并生成预测模型的最先进的环境。Streams提供了一个可伸缩的高性能环境,对不断变化的数据进行实时分析,这些数据中包括传统结构的数据...
- Kafka Streams, 我还会再使用它吗?
-
DeeptiMittal4分钟阅读...
- 大数据Hadoop之——Kafka Streams原理介绍与简单应用示例
-
一、KafkaStreams概述官网文档:https://kafka.apache.org/32/documentation/streams/...
- Android上的TCP今天开始向用户推出,并将在下个月向所有用户提供
-
据extends网3月15日报道,Firefox今天宣布,其保护用户免受跟踪器攻击的全面cookie保护(TCP)功能现已在Android上可用。该功能默认启动模式,这样,跟踪器将无法收集有关用户的浏...
- Linux curl命令(linux curl命令安装)
-
Linuxcurl命令是一个利用URL规则在命令行下工作的文件传输工具。它支持文件的上传和下载,所以是综合传输工具,但按传统,习惯称curl为下载工具。作为一款强力工具,curl支持包括HTTP、H...
- go语言http服务入门详解(go语言http服务器)
-
当你在浏览器中输入URL时,实际上是在发送一个对Web页面的请求。该请求被发送到服务器。服务器的工作是获取适当的页面并将其作为响应发送回浏览器。在Web的早期,服务器通常读取服务器硬盘上HTML文件的...
你 发表评论:
欢迎- 一周热门
-
-
前端面试:iframe 的优缺点? iframe有那些缺点
-
带斜线的表头制作好了,如何填充内容?这几种方法你更喜欢哪个?
-
漫学笔记之PHP.ini常用的配置信息
-
推荐7个模板代码和其他游戏源码下载的网址
-
其实模版网站在开发工作中很重要,推荐几个参考站给大家
-
[干货] JAVA - JVM - 2 内存两分 [干货]+java+-+jvm+-+2+内存两分吗
-
正在学习使用python搭建自动化测试框架?这个系统包你可能会用到
-
织梦(Dedecms)建站教程 织梦建站详细步骤
-
【开源分享】2024PHP在线客服系统源码(搭建教程+终身使用)
-
2024PHP在线客服系统源码+完全开源 带详细搭建教程
-
- 最近发表
-
- 高一高二第一次月考认真作答(高二第一次月考的重要性)
- 山清水秀,盛世今朝(山清水秀出处)
- 我校二模成绩已新鲜出炉(二模考试成绩)
- Argon Design向瑞萨电子有限公司提供Argon Streams VP9许可证
- 高考倒计时75天(高考倒计时75天励志语)
- 期中考试正在进行(期中考试在即)
- 不要浪费了你NAS上的HDMI接口!详解华硕NAS上HDMI接口的妙用
- Java通过Kafka Streams库来实现数据流处理
- From abandoned mines to limpid streams waters: how banks profit from EOD
- SPSS与Streams的集成实现实时预测
- 标签列表
-
- mybatis plus (70)
- scheduledtask (71)
- css滚动条 (60)
- java学生成绩管理系统 (59)
- 结构体数组 (69)
- databasemetadata (64)
- javastatic (68)
- jsp实用教程 (53)
- fontawesome (57)
- widget开发 (57)
- vb net教程 (62)
- hibernate 教程 (63)
- case语句 (57)
- svn连接 (74)
- directoryindex (69)
- session timeout (58)
- textbox换行 (67)
- extension_dir (64)
- linearlayout (58)
- vba高级教程 (75)
- iframe用法 (58)
- sqlparameter (59)
- trim函数 (59)
- flex布局 (63)
- contextloaderlistener (56)