You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Steam用户成就存储优化:转JSON列是否可行?

Steam用户成就存储方案:JSON列vs关联表分析

问题背景

当前采用三张关联表存储Steam用户与成就数据:

CREATE TABLE steam_user (
    id SERIAL PRIMARY KEY,
    username TEXT NOT NULL,
    steam_id TEXT NOT NULL,
    profile_url TEXT NOT NULL,
    avatar_url TEXT NOT NULL
);

CREATE TABLE achievement (
    id SERIAL PRIMARY KEY,
    name TEXT NOT NULL,
    display_name TEXT NOT NULL,
    description TEXT NOT NULL,
    icon_url TEXT DEFAULT '' NOT NULL,
    categories SMALLINT[] DEFAULT '{}' NOT NULL
);

CREATE TABLE achievements_on_steam_users (
    id SERIAL PRIMARY KEY,
    steam_user_id INTEGER NOT NULL,  /* 外键 */
    achievement_id INTEGER NOT NULL, /* 外键 */
    achieved BOOLEAN NOT NULL DEFAULT FALSE,
    unlock_time TIMESTAMP NOT NULL DEFAULT '1970-01-01 00:00:00'
);

业务现状与需求:

  • 每次拉取用户成就时,achievements_on_steam_users关联表会新增超1000行数据
  • 核心需求:查询单个用户所有成就并与achievement表JOIN;用户重置成就时更新数据(需保留achieved=FALSE的行)
  • 当前两类查询执行计划为顺序扫描+哈希右连接

现考虑将成就数据以JSON列形式存储在steam_user表中,JSON结构示例:

{
  // 键对应achievement表中的ID
  "333": {
    "achieved": true,
    "unlockTime": "1970-01-01 00:00:00"
  }
}

已知achievement表数据极少变更,ID永不修改,仅会新增ID。

方案合理性判断

适合采用JSON方案的场景

  • 缓解写入压力:原来每次拉取要插入上千行关联表数据,改成JSON列后只需更新单条用户记录,写入IO次数骤减,能有效降低数据库批量插入的负载。
  • 聚焦单个用户操作:如果业务几乎不需要跨用户的成就统计(比如“多少用户解锁了成就X”),仅围绕单个用户的成就查询、更新,JSON方案的聚合性更贴合业务逻辑。

需注意的潜在问题

  • 单成就更新繁琐:用户重置成就时,若要修改部分成就的状态,需先解析整个JSON、修改对应字段再重新写入,比直接更新关联表的单条记录操作更复杂,大JSON的序列化/反序列化还会额外消耗资源。
  • 存储空间膨胀:随着成就ID不断新增,每个用户的JSON列会逐渐变大,长期下来存储成本会上升。
  • 复杂查询支持差:如果后续要做跨用户的成就统计,JSON方案需要全表扫描每个用户的JSON并解析,性能会大幅下降;而关联表只要添加(achievement_id, achieved)联合索引就能快速完成统计。

查询性能对比

单个用户成就JOIN查询

  • 原方案(无索引):当前执行计划是顺序扫描+哈希右连接,性能瓶颈明显,因为要扫描整个关联表定位用户的成就行。
  • JSON方案:查询单个用户时,先取出JSON列解析,提取所有成就ID再和achievement表JOIN。千级成就的解析开销可控,相比无索引的原方案,性能反而可能提升;但如果原方案给achievements_on_steam_users添加(steam_user_id, achievement_id)联合索引,关联表的查询速度会比JSON方案更快,但JSON方案也不会出现“大幅下降”的情况。

跨用户统计查询

  • JSON方案完全不占优势,这类查询会变得极其低效,必须全表扫描+JSON解析,而关联表依靠索引就能快速得到结果。

总结

如果你的业务核心就是单个用户的成就操作,且短期内不会有跨用户统计需求,这个JSON方案是合理的,查询性能不会大幅下降,还能解决原方案的批量写入压力。但如果有跨用户统计的可能,或者需要频繁修改单个成就状态,优先给关联表添加合适的索引(比如steam_user_id单字段索引,或者(steam_user_id, achievement_id)联合索引),比切换到JSON存储更靠谱。

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 07:19:58