百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程网 > 正文

预分配法生成唯一ID 预分配内存是什么意思

yuyutoo 2024-10-18 12:11 5 浏览 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());
    }
}

相关推荐

电脑 CMD 命令大全:简单粗暴收藏版

电脑CMD命令大全包括了许多常用的命令,这些命令可以帮助用户进行各种系统管理和操作任务。以下是一些常用的CMD命令及其功能:1、系统信息和管理...

电脑维修高手必备!8个神奇DOS命令,自己动手不求人

我相信搞电脑维修或者维护的基本都会些DOS的命令。就算Windows操作系统是可视化的界面,但很多维护检查是离不开DOS命令的。掌握好这些命令,你不仅能快速诊断问题,还能解决90%的常见电脑故障。下...

一个互联网产品总监的设计技巧总结 - 技术篇

古语:工欲善其事必先利其器。往往在利其器后我们才能事半功倍。从这个角度出发成为一个合格的产品经理你需要的是“利其器”,这样你才能产品的设计过程中如鱼得水,得心应手。有些产品经理刚入职,什么都感觉自己欠...

超详解析Flutter渲染引擎|业务想创新,不了解底层原理怎么行?

作者|万红波(远湖)出品|阿里巴巴新零售淘系技术部前言Flutter作为一个跨平台的应用框架,诞生之后,就被高度关注。它通过自绘UI,解决了之前RN和weex方案难以解决的多端一致性...

瑞芯微RK3568|SDK开发之环境安装及编译操作

1.SDK简介一个通用LinuxSDK工程目录包含有buildroot、app、kernel、device、docs、external等目录。其中一些特性芯片如RK3308/RV1108/R...

且看L-MEM ECC如何守护i.MXRT1170从核CM4

大家好,我是痞子衡,是正经搞技术的痞子。今天痞子衡给大家分享的是恩智浦i.MXRT1170上Cortex-M4内核的L-MEMECC功能。本篇是《简析i.MXRT1170Cortex-M7F...

ECC给i.MXRT1170 FlexRAM带来了哪些变化?

大家好,我是痞子衡,是正经搞技术的痞子。今天痞子衡给大家分享的是恩智浦i.MXRT1170上Cortex-M7内核的FlexRAMECC功能。ECC是“ErrorCorrectingCode”...

PHP防火墙代码,防火墙,网站防火墙,WAF防火墙,PHP防火墙大全

PHP防火墙代码,防火墙,网站防火墙,WAF防火墙,PHP防火墙大全资源宝整理分享:https://www.htple.net...

从零开始移植最新版本(2023.10)主线Uboot到Orange Pi 3(全志H6)

本文将从零开始通过一步一步操作来实现将主线U-Boot最新代码移植到OrangePi3(全志H6)开发板上并正常运行起来。本文从通用移植思路的角度,展现是思考的过程,通过这种方式希望能让读者一通百...

可视化编程工具Blockly——定制工具箱

1概述本文重点讲解如何定制Blocklytoolbox上,主要包含如下几点目标:如何为toolbox不同类别添加背景色如何改变选中的类别的外观如何为toolbox类别添加定制化的css如何改变类别...

用户界面干货盘点(用户界面的基本操作方法)

DevExpressDevExpressWPF的DXSplashScreen控件在应用加载的时候显示一个启动界面。添加DXSplashScreen后,会默认生成一个XAML文件,当然,你也可...

Vue3+Bootstrap5整合:企业级后台管理系统实战

简洁而不简单,优雅而不失强大在当今快速发展的企业数字化进程中,高效、美观的后台管理系统已成为企业运营的核心支撑。作为前端开发者,我们如何选择技术栈,才能既保证开发效率,又能打造出专业级的用户体验?答案...

什么?这三款i.MXRT型号也开放了IAP API?

大家好,我是痞子衡,是正经搞技术的痞子。今天痞子衡给大家介绍的是i.MXRT1050/1020/1015系列ROM中的FlexSPI驱动API使用。今天痞子衡去4S店给爱车做保养了,...

OneCode基础组件介绍——表格组件(Grid)

在企业级应用开发中,表格组件是数据展示与交互的核心载体。OneCode平台自研的Grid表格组件,以模型驱动设计...

开源无线LoRa传感器(光照温湿度甲醛Tvoc)

本开源项目基于ShineBlinkC2M低代码单片机实现,无需复杂单片机C语言开发。即使新手也可很容易用FlexLua零门槛开发各种功能丰富稳定可靠的IoT硬件,更多学习教程可参考Flex...

取消回复欢迎 发表评论: