MySQL多等级游戏成就系统设计:缺陷分析与优化建议
原设计的核心弱点分析
扩展性严重不足
硬编码的3个等级字段(level_1-level_3、reward_1-reward_3)导致后续新增等级必须修改表结构,不仅涉及数据库schema变更,还需要同步修改所有依赖的业务代码,对线上服务影响极大。奖励状态记录模糊
单个reward_collected字段无法区分不同等级的奖励领取状态——比如用户完成了Level 2但未领取Level 1的奖励,这个字段只能标记“已领”或“未领”,完全无法准确反映真实情况,直接导致奖励领取逻辑混乱。等级计算效率低下
每次判断用户当前成就等级都需要执行多条件对比(progress >= level_3→ 等级3,否则progress >= level_2→ 等级2,以此类推),当用户量和成就数量较大时,这类查询会消耗大量数据库资源,拖慢响应速度。维护成本高
description字段的占位符需要手动维护,一旦某个等级的目标值修改,必须同步修改描述中的占位符内容,很容易出现描述与规则不一致的情况。存储冗余
每个成就都重复存储3组等级和奖励字段,若后续扩展等级,冗余会进一步加剧,浪费数据库存储空间。
效率与存储优化方案
一、表结构拆分(核心优化)
通过拆分表结构,实现规则与数据的解耦,解决扩展性和存储冗余问题:
1. 拆分成就基础信息与等级规则
将原achievement表拆分为两张独立表:
achievement(成就基础信息表)
| 字段名 | 类型 | 说明 |
|---|---|---|
id_achievement | INT | 成就ID(主键) |
title | VARCHAR(255) | 成就名称 |
base_description | VARCHAR(255) | 成就描述模板(例:累计击杀{target}个怪物) |
achievement_levels(成就等级规则表)
| 字段名 | 类型 | 说明 |
|---|---|---|
id_level | INT | 等级ID(主键,自增) |
id_achievement | INT | 关联成就ID(外键) |
level_order | INT | 等级顺序(1、2、3...) |
target_value | INT | 该等级所需完成数值 |
reward_details | JSON | 奖励详情(例:{"gold":100,"item":"iron_sword"}) |
2. 拆分用户进度与奖励领取记录
将原user_achievement表拆分为两张表,精准记录每个等级的领取状态:
user_achievement_progress(用户成就进度表)
| 字段名 | 类型 | 说明 |
|---|---|---|
id_user | INT | 用户ID |
id_achievement | INT | 关联成就ID |
current_progress | INT | 当前累计进度值 |
highest_completed_level | INT | 已完成的最高等级(缓存值,避免重复计算) |
| PRIMARY KEY | (id_user, id_achievement) | 复合主键,确保用户-成就唯一记录 |
user_achievement_rewards(用户成就奖励领取记录表)
| 字段名 | 类型 | 说明 |
|---|---|---|
id_user | INT | 用户ID |
id_level | INT | 关联等级ID |
is_collected | TINYINT(1) | 是否领取(0=未领,1=已领) |
collected_at | DATETIME | 领取时间(可选) |
| PRIMARY KEY | (id_user, id_level) | 复合主键,确保用户-等级唯一记录 |
二、效率优化细节
缓存等级计算结果
在user_achievement_progress中新增highest_completed_level字段,当用户进度更新时,同步计算并更新该字段——后续查询用户当前等级时直接读取该值,无需每次对比所有等级的目标值,大幅降低数据库计算开销。添加针对性索引
- 给
achievement_levels添加(id_achievement, level_order)联合索引,快速按成就查询等级顺序; - 给
user_achievement_rewards添加(id_user, is_collected)联合索引,快速筛选用户未领取的奖励。
- 给
动态生成成就描述
利用base_description模板+target_value动态渲染成就描述(比如将{target}替换为对应等级的target_value),无需在数据库中存储带占位符的固定文本,减少维护成本,避免内容不一致。
三、业务逻辑修复
- 奖励领取逻辑:每个等级的领取状态单独存储在
user_achievement_rewards中,用户完成任意等级后,无论后续是否完成更高等级,都可以单独领取该等级奖励,彻底解决原设计中奖励被覆盖或跳过的问题。 - 成就完成判断:当
highest_completed_level等于该成就的最大level_order时,即可判定成就完全完成,无需单独的achievement_completed字段。
内容的提问来源于stack exchange,提问作者Samuel Davies

