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

DynamoDB单表架构设计咨询:个人健身训练系统场景

健身训练系统DynamoDB单表设计咨询

我正在深入学习NoSQL及DynamoDB相关知识,阅读资料和视频后仍对单表架构设计思路感到困惑,因此希望通过个人项目实践探索,我熟悉关系型数据库,现寻求方向指导。

我计划构建个人健身训练系统:用户注册企业后创建拥有该企业的用户账号(所有权可变更),企业可包含门店、客户、训练计划等。

实体定义

  • Business:由id唯一标识,包含设置信息,可关联多门店、教练、客户、动作、训练、课程
  • Location:由id唯一标识,包含设置信息,属于单个企业,可关联多用户、客户
  • Trainer:由email唯一标识,可关联多动作、训练、课程、客户
  • Client:由email唯一标识,可关联多动作、训练、课程、教练
  • Exercise:由id唯一标识,可关联多训练、课程、客户、教练、门店
  • Workout:由id唯一标识,可关联多课程、客户、教练、门店
  • Program:由id唯一标识,可关联多客户、教练、门店

访问模式

  • 获取企业(按id)
  • 获取企业设置(按id)
  • 获取门店列表(按企业id)
  • 获取单个门店(按门店id)
  • 获取门店设置(按门店id)
  • 获取教练列表(按企业id/企业+门店id)
  • 获取单个教练(按教练id)
  • 获取客户列表(按企业id/企业+门店id/企业+教练id)
  • 获取动作列表(按企业id/企业+门店id/企业+教练id/动作id)
  • 获取训练列表(按企业id/企业+门店id/企业+教练id/训练id)
  • 获取课程列表(按企业id/企业+门店id/企业+教练id/课程id)

已设计的实体表

EntityPKSKGS1PKGS1SK
BUSINESSBUSINESS#idBUSINESS#id
SETTINGSBUSINESS#id#SETTINGS
LOCATIONBUSINESS#idLOCATION#id
SETTINGSLOCATION#id#SETTINGS
EXERCISEEXERCISE#idBUSINESS#id
WORKOUTWORKOUT#idBUSINESS#id
PROGRAMPROGRAM#idBUSINESS#id
USERUSER#idUSER#idUSER#emailUSER#email
TRAINERTRAINER#idTRAINER#emailBUSINESS#idTRAINER#id
CLIENTCLIENT#idCLIENT#emailBUSINESS#idCLIENT#id

困惑点

  1. 企业包含多门店,LOCATION应作为独立实体还是归属于BUSINESS(如BUSINESS##LOCATION#)?
  2. 门店有独立设置,SETTINGS应作为独立实体还是归属于LOCATION(如LOCATION##SETTINGS)?
  3. 用户模块将使用NextAuth(Auth.js)处理认证,其DynamoDB适配器要求遵循默认USER schema,是否应将TRAINER和CLIENT合并到USER实体,通过账号类型属性区分?
  4. 如何判断何时需要使用GSI或LSI?

不确定当前设计方向是否正确,期待专业建议。


针对困惑点的解答

1. LOCATION的归属与实体设计

在DynamoDB单表架构中,不需要纠结"归属"的关系定义,核心是围绕访问模式设计主键:

  • 现有设计用BUSINESS#id作为PK、LOCATION#id作为SK,能直接通过Query操作拉取指定企业下的所有门店,完全匹配「按企业id获取门店列表」的需求,这部分是合理的。
  • 但现有设计无法直接通过门店id定位单个门店(因为门店的PK是所属企业id,可能存在不知道门店所属企业的场景),所以需要给LOCATION添加GSI:将GS1PK设为LOCATION#id,GS1SK设为LOCATION#id,这样就能通过GSI的Get操作直接获取单个门店。
  • BUSINESS##LOCATION#这类嵌套写法没必要,用#分隔主键语义已足够清晰,保持PK/SK的语义明确即可。

2. SETTINGS的实体设计

同样围绕访问模式判断:

  • 现有设计中,企业设置用BUSINESS#id作为PK、#SETTINGS作为SK;门店设置用LOCATION#id作为PK、#SETTINGS作为SK,完全满足「按企业id获取设置」「按门店id获取设置」的需求,不需要将SETTINGS归属于LOCATION的主键结构中。
  • 这种设计的优势是操作语义清晰:获取企业设置直接执行Query PK=BUSINESS#XXX AND SK=#SETTINGS,获取门店设置执行Query PK=LOCATION#XXX AND SK=#SETTINGS,高效且易维护。

3. USER、TRAINER、CLIENT的合并问题

必须合并到USER实体,原因如下:

  • NextAuth的DynamoDB适配器依赖固定的USER schema,如果单独维护TRAINER/CLIENT表,会导致认证系统与业务系统的用户数据割裂,后续同步、维护成本极高。
  • 解决方案:在USER实体中新增account_type属性(枚举值:OWNER/TRAINER/CLIENT),同时添加业务关联字段,比如business_id(关联所属企业)、location_id(关联所属门店)等。
  • 这种设计既能符合NextAuth的要求,又能通过GSI实现各类列表查询:比如要按企业id查询教练列表,可创建GSI将GS1PK设为BUSINESS#id,GS1SK设为TRAINER#user_id,直接Query即可。

4. GSI与LSI的使用判断

LSI(本地二级索引)使用场景

  • 必须与基表PK相同,仅能在创建表时定义,最多支持5个。
  • 适合同一主实体下的多维度排序/查询,比如企业下有门店、教练、客户等实体,想通过同一个PK(BUSINESS#id)查询不同类型的实体,可通过LSI的SK区分,但LSI灵活性较低,目前更推荐使用GSI。

GSI(全局二级索引)使用场景

  • 可独立定义PK和SK,支持随时添加,最多支持20个。
  • 当访问模式需要以非基表PK的字段作为查询条件时,就需要GSI:
    • 比如按门店id查询单个门店,基表中门店的PK是BUSINESS#id,无法直接定位,需用GSI将LOCATION#id设为PK。
    • 比如按企业+门店id查询教练列表,可创建GSI将GS1PK设为BUSINESS#id#LOCATION#id,GS1SK设为TRAINER#user_id,直接Query即可获取目标数据。
  • 简单判断原则:如果基表的PK/SK组合无法满足访问模式,就使用GSI。

整体设计优化建议

  1. 统一实体主键格式:所有实体的PK采用ENTITY_TYPE#ID格式,比如LOCATION的PK设为LOCATION#id,SK设为BUSINESS#id,这样既可以通过GSI按BUSINESS#id拉取门店列表,也能直接通过PK获取单个门店。
  2. 优化EXERCISE/WORKOUT/PROGRAM的设计:现有设计中它们的PK是自身id、SK是企业id,但访问模式需要按门店/教练查询这些实体,因此需添加GSI:比如给EXERCISE添加GSI,将GS1PK设为BUSINESS#id#LOCATION#id,GS1SK设为EXERCISE#id,支持按企业+门店查询动作列表。
  3. 利用SK的排序能力:比如查询教练列表时,将SK设为TRAINER#created_at#user_id,可直接按创建时间排序返回结果,无需额外处理。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 23:47:01