【Postopia】设计文档

1. 概念设计

1.1 ER图

下图为论坛系统数据库设计的ER图,为了简洁期间我们从ER图中移除了实体共有的属性(id,创建日期等),仅在后续的数据字典中体现。

image-20241114213022716

1.2 数据字典

1.2.1 用户

属性数据类型约束描述
idBIGINTPRIMARY KEY, AUTO_INCREMENT用户的唯一标识符。
createdAtTIMESTAMPNOT NULL, AUDIT-CREATED用户创建时的时间戳。
usernameVARCHAR(255)NOT NULL, UNIQUE用户的唯一用户名。
nicknameVARCHAR(255)用户的可选昵称。
passwordVARCHAR(255)NOT NULL用户的加密密码。
emailVARCHAR(255)用户的电子邮件地址。
showEmailBOOLEAN是否向其他人显示电子邮件地址。
avatarVARCHAR(255)用户头像图片的 URL 或路径。
bindedWeChatVARCHAR(255)用户绑定的微信账号(可选字段)。
bindedAliPayVARCHAR(255)用户绑定的支付宝账号(可选字段)。

1.2.2 空间

属性数据类型约束描述
idBIGINTPRIMARY KEY, AUTO_INCREMENT空间的唯一标识符。
nameVARCHAR(255)NOT NULL, UNIQUE空间的唯一名称,用于标识每个空间。
descriptionVARCHAR(255)NOT NULL空间的描述信息,通常用于介绍空间的目的和内容。
createdAtTIMESTAMPNOT NULL, AUDIT-CREATED空间创建时的时间戳。
isArchivedBOOLEAN标识空间是否被归档(归档后可能不再活跃)。
avatarVARCHAR(255)空间头像的 URL 或路径。

1.2.3 帖子

属性数据类型约束描述
idBIGINTPRIMARY KEY, AUTO_INCREMENT, 不可更新帖子的唯一标识符。
subjectVARCHAR(255)NOT NULL帖子的主题,用于标题或概述。
contentTEXTNOT NULL帖子的内容,通常为正文。
createdAtTIMESTAMPNOT NULL, AUDIT-CREATED帖子创建时的时间戳。
statusTINYINTNOT NULL, ENUM(ORDINAL)帖子的状态(例如草稿、已发布、被删除等)。
positiveCountBIGINT帖子的正向反馈数量(如点赞数)。
negativeCountBIGINT帖子的负向反馈数量(如点踩数)。
commentCountBIGINT帖子的评论总数量。

1.2.4 评论

字段名称数据类型约束描述
idBIGINTPRIMARY KEY, AUTO_INCREMENT评论的唯一标识符。
contentTEXTNOT NULL评论的正文内容。
createdAtTIMESTAMPNOT NULL, AUDIT-CREATED评论创建时的时间戳。
positiveCountBIGINT评论的正向反馈数量(如点赞数)。
negativeCountBIGINT评论的负向反馈数量(如点踩数)。

1.2.5 观点

字段名称数据类型约束描述
idBIGINTPRIMARY KEY, AUTO_INCREMENT意见的唯一标识符。
createdAtTIMESTAMPNOT NULL, AUDIT-CREATED意见创建的时间戳。
isPositiveBOOLEANNOT NULL标识该意见是否为正向意见(例如点赞)。
opinion_typeVARCHAR(31)NOT NULL用于区分意见的类型(例如评论意见或帖子意见等)。

1.2.6 通知

字段名称数据类型约束描述
idBIGINTPRIMARY KEY, AUTO_INCREMENT空间用户信息的唯一标识符。
createdAtTIMESTAMPNOT NULL, AUDIT-CREATED记录用户加入空间的时间。
contentTEXTNOT NULL通知。

1.2.7 关系

关联实体1关联实体2类型解释
用户空间多对多用户可以加入多个空间,空间可以拥有多个用户
用户帖子一对多用户可以创建多个帖子
用户评论一对多用户可以发表多个评论
用户观点一对多用户可以发表多个观点
用户通知一对多用户可以收到多个通知
空间帖子一对多空间中可以有多个帖子
帖子评论一对多帖子可以有多个评论
帖子观点一对多帖子可以收到多个赞同或反对
评论评论一对多评论可以有多个子评论
评论观点一对多评论可以收到多个赞同或反对

1.3 用例图

image-20241115193639427

2. 逻辑设计

2.1 全局逻辑设计

2.1.1 表结构设计

2.1.1.1 users
字段名称数据类型约束描述
idBIGINTPRIMARY KEY, AUTO_INCREMENT用户的唯一标识符。
createdAtTIMESTAMPNOT NULL, AUDIT-CREATED用户创建时的时间戳。
usernameVARCHAR(255)NOT NULL, UNIQUE用户的唯一用户名。
nicknameVARCHAR(255)用户的可选昵称。
passwordVARCHAR(255)NOT NULL用户的加密密码。
emailVARCHAR(255)用户的电子邮件地址。
showEmailBOOLEAN是否向其他人显示电子邮件地址。
avatarVARCHAR(255)用户头像图片的 URL 或路径。
bindedWeChatVARCHAR(255)用户绑定的微信账号(可选字段)。
bindedAliPayVARCHAR(255)用户绑定的支付宝账号(可选字段)。
2.1.1.2 spaces
字段名称数据类型约束描述
idBIGINTPRIMARY KEY, AUTO_INCREMENT空间的唯一标识符。
nameVARCHAR(255)NOT NULL, UNIQUE空间的唯一名称,用于标识每个空间。
descriptionVARCHAR(255)NOT NULL空间的描述信息,通常用于介绍空间的目的和内容。
createdAtTIMESTAMPNOT NULL, AUDIT-CREATED空间创建时的时间戳。
isArchivedBOOLEAN标识空间是否被归档(归档后可能不再活跃)。
avatarVARCHAR(255)空间头像的 URL 或路径。
2.1.1.3 posts
字段名称数据类型约束描述
idBIGINTPRIMARY KEY, AUTO_INCREMENT, 不可更新帖子的唯一标识符。
subjectVARCHAR(255)NOT NULL帖子的主题,用于标题或概述。
contentTEXTNOT NULL帖子的内容,通常为正文。
user_idBIGINTNOT NULL, 不可更新, FOREIGN KEY发布帖子的用户的唯一标识符,关联到 users 表。
space_idBIGINTNOT NULL, 不可更新, FOREIGN KEY帖子所属空间的唯一标识符,关联到 spaces 表。
createdAtTIMESTAMPNOT NULL, AUDIT-CREATED帖子创建时的时间戳。
statusTINYINTNOT NULL, ENUM(ORDINAL)帖子的状态(例如草稿、已发布、被删除等)。
positiveCountBIGINT帖子的正向反馈数量(如点赞数)。
negativeCountBIGINT帖子的负向反馈数量(如点踩数)。
commentCountBIGINT帖子的评论总数量。
2.1.1.4 comments
字段名称数据类型约束描述
idBIGINTPRIMARY KEY, AUTO_INCREMENT评论的唯一标识符。
contentTEXTNOT NULL评论的正文内容。
user_idBIGINTFOREIGN KEY发布评论的用户的唯一标识符,关联到 users 表。
post_idBIGINTFOREIGN KEY评论所属帖子的唯一标识符,关联到 posts 表。
space_idBIGINTNOT NULL, 不可更新, FOREIGN KEY评论所属空间的唯一标识符,关联到 spaces 表。
parent_idBIGINTFOREIGN KEY父级评论的唯一标识符,用于支持评论的嵌套结构。
createdAtTIMESTAMPNOT NULL, AUDIT-CREATED评论创建时的时间戳。
positiveCountBIGINT评论的正向反馈数量(如点赞数)。
negativeCountBIGINT评论的负向反馈数量(如点踩数)。
2.1.1.5 opinions
字段名称数据类型约束描述
idBIGINTPRIMARY KEY, AUTO_INCREMENT意见的唯一标识符。
createdAtTIMESTAMPNOT NULL, AUDIT-CREATED意见创建的时间戳。
isPositiveBOOLEANNOT NULL标识该意见是否为正向意见(例如点赞)。
user_idBIGINTFOREIGN KEY提交该意见的用户的唯一标识符,关联到 users 表。
opinion_typeVARCHAR(31)NOT NULL用于区分意见的类型(例如评论意见或帖子意见等)。
post_idBIGINTFOREIGN KEY该意见相关的帖子的唯一标识符,关联到 posts 表,仅在意见为帖子意见时有意义
comment_idBIGINTFOREIGN KEY该意见相关的评论的唯一标识符,关联到 comments 表,仅在意见为评论意见时有意义
2.1.1.6 notifications
字段名称数据类型约束描述
idBIGINTPRIMARY KEY, AUTO_INCREMENT空间用户信息的唯一标识符。
user_idBIGINTFOREIGN KEY, NOT NULL关联的用户的唯一标识符,关联到 users 表。
createdAtTIMESTAMPNOT NULL, AUDIT-CREATED记录用户加入空间的时间。
contentTEXTNOT NULL通知。
2.1.1.7 space_user_infos
字段名称数据类型约束描述
idBIGINTPRIMARY KEY, AUTO_INCREMENT空间用户信息的唯一标识符。
space_idBIGINTFOREIGN KEY, NOT NULL关联的空间的唯一标识符,关联到 spaces 表。
user_idBIGINTFOREIGN KEY, NOT NULL关联的用户的唯一标识符,关联到 users 表。
createdAtTIMESTAMPNOT NULL, AUDIT-CREATED记录用户加入空间的时间。
lastActiveAtDATE用户在空间中最近活跃的日期。

2.1.2 索引设计

OceanBase 数据库中,索引可以分为两种类型:本地索引和全局索引。两者之间的区别在于:本地索引与分区数据共用分区,全局索引为单独分区。

OceanBase推荐按照本地索引->全局分区索引->全局索引的顺序进行选择,非必要情况下不建议使用全局索引。全局索引维护代价很高,数据的增删改都需要维护全局索引, 不适宜大量使用。数据的查询要尽量使用主键或只要数据能在分区内保证唯一,则没必要使用全局索引。

因此除了全局的主键索引(OceanBase自动建立)外,仅建立本地索引,其细节将在 2.3 局部逻辑设计中详细讨论。

2.1.3 视图设计

考虑到在展示帖子列表、帖子评论时需要同时展示发帖者、评论者信息,建立视图 user_posts 和 user_comments。

user_posts:

字段名称数据类型描述
idBIGINT帖子的唯一标识符。
subjectVARCHAR(255)帖子的主题,用于标题或概述。
contentTEXT帖子的内容,通常为正文。
space_idBIGINT帖子所属空间的唯一标识符,关联到 spaces 表。
createdAtTIMESTAMP帖子创建时的时间戳。
statusTINYINT帖子的状态(例如草稿、已发布、被删除等)。
positiveCountBIGINT帖子的正向反馈数量(如点赞数)。
negativeCountBIGINT帖子的负向反馈数量(如点踩数)。
commentCountBIGINT帖子的评论总数量。
user_idBIGINT用户 id。
nicknameVARCHAR(255)用户的可选昵称。
avatarVARCHAR(255)用户头像图片的 URL 或路径。

user_comments:

字段名称数据类型描述
idBIGINT评论的唯一标识符。
contentTEXT评论的正文内容。
post_idBIGINT评论所属帖子的唯一标识符,关联到 posts 表。
createdAtTIMESTAMP评论创建时的时间戳。
positiveCountBIGINT评论的正向反馈数量(如点赞数)。
negativeCountBIGINT评论的负向反馈数量(如点踩数)。
user_idBIGINT用户 id。
nicknameVARCHAR(255)用户的可选昵称。
avatarVARCHAR(255)用户头像图片的 URL 或路径。

考虑到请求空间列表时需要根据加入空间的人数对空间进行排序,因此建立持久化视图 space_member_counts并定期刷新。

space_member_counts:

字段名称数据类型描述
idBIGINT空间的唯一标识符。
countBIGINT加入空间的用户数。

2.1.4 规范化

上述表符合BCNF范式,更高的范式虽然可以减少数据冗余,但可能会增加查询的复杂性和维护的难度,因此不再继续进行规范化。

2.2 分布设计

2.2.1 分片设计

2.2.1.1 垂直分片设计

OceanBase不支持垂直分片,因此我们在建表时就根据业务需求对 2.1.1中各表进行垂直分片设计,直接根据需要对表进行拆分。

对于users表,根据查询频率拆分为更经常查询的husers和查询频率较低的cusers:

husers:

字段名称数据类型约束描述
idBIGINTPRIMARY KEY, AUTO_INCREMENT用户的唯一标识符。
usernameVARCHAR(255)NOT NULL, UNIQUE用户的唯一用户名。
nicknameVARCHAR(255)用户的可选昵称。
emailVARCHAR(255)用户的电子邮件地址。
showEmailBOOLEAN是否向其他人显示电子邮件地址。
avatarVARCHAR(255)用户头像图片的 URL 或路径。

cusers:

字段名称数据类型约束描述
idBIGINTPRIMARY KEY, AUTO_INCREMENT用户的唯一标识符。
createdAtTIMESTAMPNOT NULL, AUDIT-CREATED用户创建时的时间戳。
passwordVARCHAR(255)NOT NULL用户的加密密码。
bindedWeChatVARCHAR(255)用户绑定的微信账号(可选字段)。
bindedAliPayVARCHAR(255)用户绑定的支付宝账号(可选字段)。
2.2.1.2 水平分片设计

OceanBase中的水平分片通过分区(Partition)实现。OceanBase可以将普通表的数据按照规则划分到不同的区块内,区块内数据物理上存储在一起,称为分区表,每个区块称为一个分区。OceanBase支持多种分区类型,包括Range分区、Range Columns分区、List分区、List Columns分区、Hash分区、Key分区以及二级分区。

数据表中每一行中用于计算分区归属的列的集合叫做分区键。由分区键构成的用于计算分区归属的表达式叫做分区表达式。其中Range分区根据分区表达式的值范围进行分区,List分区根据分区表达式的值进行分区,Columns后缀则代表允许使用多列作为分区键,Hash分区通过哈希函数计算的分区键哈希值进行分区,Key分区则通过哈希函数计算的分区键哈希值取模结果进行分区。

我们根据业务需求对各表进行了分区设计。

  • 对于 husers和cusers 表,以 id 为分区键进行Key分区,使得用户信息在各节点间均匀分布。
  • 对于 spaces 表,在查询时往往需要筛除已归档的空间,因此首先以 isArchived 为分区键进行List分区,再以 id 为分区键进行Key二级分区,使得空间信息在各节点间均匀分布。
  • 对于 posts 表,由于帖子查询往往以空间为单位,因此首先 space_id 为分区键进行List分区,再以 status 为分区键进行List二级分区,便于在展示帖子列表时仅展示发布状态的帖子。
  • 对于 comments 表,由于评论查询往往以空间中的帖子为单位进行查询,因此首先 space_id 为分区键进行List分区,再以 post_id 为分区键进行Range二级分区。
  • 对于 opinions 表,由于 posts,comments中有观点数记录,对于观点的查询往往以用户为单位(用户管理或展示用户对帖子、评论的观点),因此以 opinion_type 为分区键进行List分区,区分对于评论或帖子的观点,再以 user_id 为分区键进行Range二级分区。
  • 对于 notifications 表,由于通知查询往往以用户为单位,因此以 user_id 为分区键进行Range分区。
  • 对于 space_user_infos 表,由于其主要用于查询用户加入的空间,因此以 user_id 为分区键进行Range分区。

2.2.2 非冗余分配

【OceanBase 动态分区分配】

2.2.3 冗余分配

【OceanBase 副本机制】

2.3 局部逻辑设计

2.3.1 表结构设计

在OceanBase中由于主键的唯一性检查是在各个分区内部进行的,如果主键不包含全部分区键,这个检查就会失效,同时需要保证指定主键的查询能很快定位到表所在的分区,分区键必须是主键的子集,因此除了 2.2.1.1 中的husers与cusers外,表结构与 2.1.1 相比发生了如下变化:

2.3.1.1 spaces
字段名称数据类型约束描述
idBIGINTPRIMARY KEY, AUTO_INCREMENT空间的唯一标识符。
isArchivedBOOLEANPRIMARY KEY标识空间是否被归档(归档后可能不再活跃)。
nameVARCHAR(255)NOT NULL, UNIQUE空间的唯一名称,用于标识每个空间。
descriptionVARCHAR(255)NOT NULL空间的描述信息,通常用于介绍空间的目的和内容。
createdAtTIMESTAMPNOT NULL, AUDIT-CREATED空间创建时的时间戳。
avatarVARCHAR(255)空间头像的 URL 或路径。

isArchived 成为主键中的字段。

2.3.1.2 posts
字段名称数据类型约束描述
idBIGINTPRIMARY KEY, AUTO_INCREMENT, 不可更新帖子的唯一标识符。
space_idBIGINTNOT NULL, 不可更新, FOREIGN KEY,PRIMARY KEY帖子所属空间的唯一标识符,关联到 spaces 表。
statusTINYINTNOT NULL, ENUM(ORDINAL),PRIMARY KEY帖子的状态(例如草稿、已发布、被删除等)。
subjectVARCHAR(255)NOT NULL帖子的主题,用于标题或概述。
contentTEXTNOT NULL帖子的内容,通常为正文。
user_idBIGINTNOT NULL, 不可更新, FOREIGN KEY发布帖子的用户的唯一标识符,关联到 users 表。
createdAtTIMESTAMPNOT NULL, AUDIT-CREATED帖子创建时的时间戳。
positiveCountBIGINT帖子的正向反馈数量(如点赞数)。
negativeCountBIGINT帖子的负向反馈数量(如点踩数)。
commentCountBIGINT帖子的评论总数量。

space_idstatus成为主键中的字段。

2.3.1.3 comments
字段名称数据类型约束描述
idBIGINTPRIMARY KEY, AUTO_INCREMENT评论的唯一标识符。
post_idBIGINTFOREIGN KEY,PRIMARY KEY评论所属帖子的唯一标识符,关联到 posts 表。
space_idBIGINTNOT NULL, 不可更新, FOREIGN KEY,PRIMARY KEY评论所属空间的唯一标识符,关联到 spaces 表。
contentTEXTNOT NULL评论的正文内容。
user_idBIGINTFOREIGN KEY发布评论的用户的唯一标识符,关联到 users 表。
parent_idBIGINTFOREIGN KEY父级评论的唯一标识符,用于支持评论的嵌套结构。
createdAtTIMESTAMPNOT NULL, AUDIT-CREATED评论创建时的时间戳。
positiveCountBIGINT评论的正向反馈数量(如点赞数)。
negativeCountBIGINT评论的负向反馈数量(如点踩数)。

space_idpost_id成为主键中的字段。

2.3.1.4 opinions
字段名称数据类型约束描述
idBIGINTPRIMARY KEY, AUTO_INCREMENT意见的唯一标识符。
user_idBIGINTFOREIGN KEY,PRIMARY KEY提交该意见的用户的唯一标识符,关联到 users 表。
opinion_typeVARCHAR(31)NOT NULL,PRIMARY KEY用于区分意见的类型(例如评论意见或帖子意见等)。
createdAtTIMESTAMPNOT NULL, AUDIT-CREATED意见创建的时间戳。
isPositiveBOOLEANNOT NULL标识该意见是否为正向意见(例如点赞)。
post_idBIGINTFOREIGN KEY该意见相关的帖子的唯一标识符,关联到 posts 表,仅在意见为帖子意见时有意义
comment_idBIGINTFOREIGN KEY该意见相关的评论的唯一标识符,关联到 comments 表,仅在意见为评论意见时有意义

opinion_typeuser_id 成为主键中的字段。

2.3.1.5 notifications
字段名称数据类型约束描述
idBIGINTPRIMARY KEY, AUTO_INCREMENT空间用户信息的唯一标识符。
user_idBIGINTFOREIGN KEY, NOT NULL,PRIMARY KEY关联的用户的唯一标识符,关联到 users 表。
createdAtTIMESTAMPNOT NULL, AUDIT-CREATED记录用户加入空间的时间。
contentTEXTNOT NULL通知。

user_id 成为主键中的字段。

2.3.1.6 space_user_infos
字段名称数据类型约束描述
idBIGINTPRIMARY KEY, AUTO_INCREMENT空间用户信息的唯一标识符。
user_idBIGINTFOREIGN KEY, NOT NULL,PRIMARY KEY关联的用户的唯一标识符,关联到 users 表。
space_idBIGINTFOREIGN KEY, NOT NULL关联的空间的唯一标识符,关联到 spaces 表。
createdAtTIMESTAMPNOT NULL, AUDIT-CREATED记录用户加入空间的时间。
lastActiveAtDATE用户在空间中最近活跃的日期。

user_id 成为主键中的字段。

2.3.3 索引设计

对于分区后的分区表,我们根据业务查询的需求在如下表上设计了索引。

  • 对于二级分区(List Partition By space_id + List Partition By status )后的 posts 表分区,在 createdAt 列上建立本地索引,便于按照创建时间查询帖子。
  • 对于二级分区(List Partition By space_id + Range Partition By post_id )后的 comments 表分区,在 post_idcreatedAt 列上建立本地索引,便于按照帖子及评论时间查询评论。
  • 对于二级分区(List Partition By opinion_type + Range Partition By user_id )后的 opinions 表分区,在 user_id 列上建立本地索引,便于按照用户查询观点。
  • 对于分区(Range Partition By user_id )后的 notifications 表,在 user_idcreated_at 列上建立复合本地索引,便于按照用户查询按时间排序的通知。
  • 对于分区(Range Partition By user_id )后的 space_user_infos 表,在 user_idcreated_at 列上建立复合本地索引,便于按照用户查询按加入时间排序的空间。

2.3.4 视图设计

对于持久化视图 space_member_counts,使用与 spaces 表相同的分区方式,即首先以 isArchived 为分区键进行List分区,再以 id 为分区键进行Key二级分区。

2.3.5 规范化

users 表拆分而成的 husers 与 cusers 表同样满足BCNF范式

2.4 局部物理设计

【OceanBase 节点物理设计】

Licensed under CC BY-NC-SA 4.0
Last updated on Nov 19, 2024 00:00 UTC
comments powered by Disqus
Built with Hugo
Theme Stack designed by Jimmy