You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

多币种余额用户的MySQL数据库设计方案咨询

最优多币种余额存储方案:兼顾效率与扩展性

嘿,这个问题我之前帮好几个游戏项目团队梳理过,先拆解下你提到的两个初始方案的核心问题,再给你落地性极强的最优思路。

先说说你两个方案的弊端

1. 宽表(105列)的致命问题

  • 扩展性完全锁死:哪天游戏新增第101种币种,你就得修改用户表结构加列——生产环境里DDL操作会锁表,用户量大的话直接影响服务可用性,这绝对是大忌。
  • 存储浪费+查询麻烦:绝大多数用户持有的币种肯定远少于100种,大量空值会白白占存储空间;而且查询特定币种、统计用户持有币种数的时候,SQL写起来会非常繁琐,比如WHERE currency_72 > 0这种,后续维护极易出错。

2. 单币种余额表的“扩展顾虑”是可解的

你担心的扩展问题其实是对未优化的单表的顾虑,只要做好结构设计和索引优化,这个方案才是符合数据库设计范式、兼顾效率与扩展性的正确选择。


最优方案:用户表 + 币种余额表的关联设计

这是游戏行业处理多币种/多道具余额的标准设计,核心是把用户基础信息和余额数据拆分存储,具体表结构如下:

1. 用户基础表(users

只存用户核心基础信息,别掺和币种相关字段:

CREATE TABLE users (
    userid INT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(50) NOT NULL,
    email VARCHAR(100) UNIQUE NOT NULL,
    pw VARCHAR(255) NOT NULL,
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
    updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

2. 币种余额表(user_currency_balances

每一行对应一个用户的一种币种余额,是这个方案的核心:

CREATE TABLE user_currency_balances (
    userid INT NOT NULL,
    currency_id INT NOT NULL,
    balance DECIMAL(18,4) NOT NULL DEFAULT 0.0000, -- 用DECIMAL避免浮点精度问题
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
    updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (userid, currency_id), -- 复合主键:保证一个用户一种币种唯一一条记录
    FOREIGN KEY (userid) REFERENCES users(userid) ON DELETE CASCADE, -- 用户删除时自动删余额
    INDEX idx_currency_id (currency_id) -- 方便按币种做全服统计
);

为什么这个方案能兼顾效率和扩展性?

  • 扩展性拉满:新增币种完全不用改表,直接往余额表里插入数据即可,不管以后是100种还是1000种币种,都能轻松应对。
  • 存储高效:只存用户实际持有的币种,没有空值浪费空间,数据量按需增长,不会出现宽表那种“大而空”的情况。
  • 业务逻辑清晰:查询、更新、统计的SQL都非常简洁:
    • 查询用户所有余额:SELECT currency_id, balance FROM user_currency_balances WHERE userid = ?;
    • 更新某币种余额:UPDATE user_currency_balances SET balance = balance + ? WHERE userid = ? AND currency_id = ?;
  • 性能有保障:复合主键(userid, currency_id)让查询和更新直接命中索引,速度极快;就算数据量增长,只要索引维护好,单表支撑百万级用户完全没问题。

针对“扩展问题”的额外优化手段

如果你的游戏用户量未来会快速增长,可以提前做这些优化:

  • 缓存层加持:把用户的余额数据缓存到Redis(用Hash结构,比如user:{userid}:balances),查询优先走缓存,更新时先更数据库再更缓存,能极大减轻数据库压力。
  • 批量操作优化:批量更新/查询时用IN语句或MySQL批量语法,避免循环单条操作,提升效率。
  • 分表分库(极端场景):当用户量到千万级以上时,可以按userid取模分表,或者用分布式数据库,但大部分游戏初期根本不需要这一步,先做好前面的优化足够支撑业务。

最后补个小建议:可以再建一个currencies表存储币种的基础配置(比如币种名称、图标、汇率等),用currency_id关联余额表,避免业务代码硬编码币种ID,维护更方便。

内容的提问来源于stack exchange,提问作者Rand

火山引擎 最新活动