游戏开发 数据库ID设计 ID生成器

对于滚服游戏开发,数据库的ID设计非常重要,关乎到合服操作的复杂性。数据库ID设计得好,合服就相当简单。合服主要是数据的合并。把两个或多个单独的服务器数据合并到一个服里面。

数据库表设计是游戏开发中必不可少的,通常每一张表,我们都会设计一个ID主键字段,关于表ID的生成方式。这里我们选择根据区服ID及玩家数量自增,其他同学喜欢用UUID那就另说。

ID结构: 自增序列+四位区服号。

这里我们以区服ID作为基数,个位到千位是区服号,万位以上是玩家ID自增ID开始。如1区服即0001, 2区服是0002.

以玩家基础数据表的ID设计为例,如果以区服1为示例,那么ID结构是这样的:玩家自增序列+0001。即1区服的第1号玩家ID是:10001;1区服的第19876号玩家ID是:198760001。2区服的第1号玩家ID是:10002;2区服的第19876号玩家ID是:198760002。

这里说明一下,ID区服段为什么是个位到千位,而不是到万位呢?通常滚服开服区服数量不会太多,除非非常爆款的游戏。一般开到上千服已经很不错了。所以这里设计区服区间为1-9999个服。即ID基数是:0001-9999。

下面是实现这种结构ID设计的实例

以JAVA代码示例。

1. ID生成管理类 IdManager.java

初始化ID和生成新ID的实现。

@Service
public class IdManager {
    private static final Logger logger = LogManager.getLogger(IdManager.class);

    @Resource
    private GameDBConfiguration gameServerConfiguration;

    private final Map<String, Long> ids = new ConcurrentHashMap<>();

    public void init() {
        logger.info("id生成器开始。");
        try {
            Field[] declaredFields = TableNameConstant.class.getDeclaredFields();
            for (Field f : declaredFields) {
                String tableName = f.get(TableNameConstant.class).toString();
                long maxId = gameServerConfiguration.getMaxId(tableName);
                if (maxId == 0) {
                    if (tableName.equals(TableNameConstant.T_PLAYER)) {
                        ids.put(tableName, IdMaxConstant.ROLE);
                    } else {
                        ids.put(tableName, IdMaxConstant.COMMON);
                    }
                } else {
                    ids.put(tableName, maxId);
                }
            }
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        }
        logger.info("id生成器结束。");
    }

    /**
     * 创建并获取新的id
     */
    public Long createNewId(String tableName) {
        synchronized (tableName) {
            Long maxId = ids.get(tableName);
            if (maxId == null) {
                logger.error("id生成规则出错,tableName={}", tableName);
                return null;
            }
            long newId = 0;
            int serverId = ServerHelper.getServerId();//获取当前区服ID。
            newId = (maxId / IdMaxConstant.SERVER_BASE_VALUE + 1) * IdMaxConstant.SERVER_BASE_VALUE + serverId;
            /*if(tableName.equalsIgnoreCase(TableNameConstant.T_PLAYER)){
                newId = (maxId / IdMaxConstant.SERVER_BASE_VALUE + 1) * IdMaxConstant.SERVER_BASE_VALUE + serverId;
            }else{
                newId = maxId + 1;
            }*/
            ids.put(tableName, newId);
            return newId;
        }
    }
}

2. 获取数据库表最大ID方法 getMaxId()

从数据库中查询当前表格最大的ID序号。

public long getMaxId(String tableName) {
        String sql = "select max(id) from " + tableName;
        Long maxId = jdbcTemplate.queryForObject(sql, Long.class);
        return maxId == null ? 0 : maxId;
    }

3. 表格常量类 TableNameConstant.java

存放需要管理ID的表格名称。

public class TableNameConstant {
    public static final String T_PLAYER = "t_player";
    public static final String T_PLAYER_GOODS = "t_player_goods";
	//...
}

4. 常量类 IdMaxConstant.java

public class IdMaxConstant {
    /**
     * role表id起始值
     */
    public static final long ROLE = 0L; 
    /**
     * 通用表id起始值
     */
    public static final long COMMON = 0L;

}

5. 使用示例

long playerId = idManager.createNewId(TableNameConstant.T_PLAYER);

        Player player = new Player();
        player.setPlayerId(playerId);
        player.setUsername(username);
        player.setGameSite(gameSite);
        player.setNickname(nickname.trim());
        player.setCreateTime(new Date());
		//...

至此,一波操作完成。
实际应用中根据具体项目可以修改 IdMaxConstant.java 起始值来改变ID的结构。

欢迎您点赞 评论,您的支持是我最大的动力~

↓ 动动您的贵手 点赞 收藏 (方便您下次查阅) 谢谢~(❁´◡`❁)✲゚