在我们项目的开发初期,大家有在讨论主数据库的选型,比较多的两个声音是MongoDB和MySQL,分别代表了文档型数据库和关系型数据库的典型。因为大家经验都不算丰富,没办法直观的比较出两者哪个更适合我们的项目。就提出我们双管齐下,针对两种数据库都设计一下表单以及对应的优化思路,来个横向对比。
这篇笔记主要会总结一下MongoDB的设计和优化想法。
表单设计
用户相关
users
| 名称 | 类型 | 属性说明 |
|---|---|---|
| user_id | ObjectId | 主键,用于唯一标识用户。 |
| username | String | 用户名。 |
| password | String | 密码(工程上应该使用哈希后的密码而不是明文)。 |
| profile | Doc | 嵌套文档,用于存储用户相关的一些其它元数据。 |
profile
| 名称 | 类型 | 属性说明 |
|---|---|---|
| follow_count | Integer | 用户关注的人数。 |
| follower_count | Integer | 用户的粉丝数量。 |
| work_count | Integer | 用户发布的视频数量。 |
| total_favorited | Integer | 用户喜欢的视频数量。 |
| favorite_count | Integer | 用户获赞数量。 |
| avatar | String | 用户头像的URL。 |
| background_image | String | 用户背景图片的URL。 |
视频流
videos
| 名称 | 类型 | 属性说明 |
|---|---|---|
| video_id | ObjectId | 主键,用于唯一标识视频。 |
| author_id | ObjectId | 视频作者的用户ID。 |
| title | String | 视频标题。 |
| play_url | String | 视频播放URL。 |
| cover_url | String | 视频封面图片URL。 |
| favorite_count | Integer | 视频的点赞数。 |
| comment_count | Integer | 视频的评论数。 |
| publish_time | Date | 视频的发布时间。 |
互动相关
favorites
| 名称 | 类型 | 属性说明 |
|---|---|---|
| user_id | ObjectId | 喜欢视频的用户ID。 |
| video_id | ObjectId | 被喜欢的视频ID。 |
| timestamp | Date | 点赞的时间。 |
comments
| 名称 | 类型 | 属性说明 |
|---|---|---|
| comment_id | ObjectId | 主键,用于唯一标识评论。 |
| user_id | ObjectId | 发表评论的用户ID。 |
| video_id | ObjectId | 被评论的视频ID。 |
| content | String | 评论的内容。 |
| timestamp | Date | 评论的创建时间。 |
关注关系
relations
| 名称 | 类型 | 属性说明 |
|---|---|---|
| from_user_id | ObjectId | 发起关注操作的用户ID。 |
| to_user_id | ObjectId | 被关注的用户ID。 |
| timestamp | Date | 关注操作的时间。 |
消息
| 名称 | 类型 | 属性说明 |
|---|---|---|
| from_user_id | ObjectId | 发送消息的用户ID。 |
| to_user_id | ObjectId | 接收消息的用户ID。 |
| content | String | 消息的内容。 |
| timestamp | Date | 消息发送的时间。 |
用数据库ERM图表示为:
优化想法
这里列举了一些优化的思路,可能有考虑不周全甚至有误,欢迎讨论和指正。
-
索引的优化:
- 针对发布列表API,因为要真是一个用户发布的所有视频,并且按照时间倒序排列,因此可以对
videos建立一个联合索引(author_id, -publish_time),这里-publish_time前面的减号代表倒序。 - 类似的,针对喜欢列表和评论列表API,我们可以在
favorites表和comments表中对user_id字段建立索引。另外,虽然API文档里面没有特别说明,但是我们可以想到不管是点赞列表还是评论列表,我们都希望是以时间倒序排列(也就是最近的在前面),那么最好是建立一个联合索引(user_id, -timestamp)。 - 对于关注列表和粉丝列表API,我们可以在
relationships表中对from_user_id和to_user_id建立索引,以便快速的找到某个用户的粉丝列表和关注对象列表。同理,按时间倒序排列,也就是应该更进一步设计成(from_user_id, -timestamp)和(to_user_id, -timestamp)这样的联合索引。 - 同样的,在
messages表中,也可以建立(from_user_id, -timestamp)和(to_user_id, -timestamp)这样的联合索引。
- 针对发布列表API,因为要真是一个用户发布的所有视频,并且按照时间倒序排列,因此可以对
-
表的优化:
对于表的存储,考虑到超大数据量的场景,我们需要对表进行水平分割/切片(honrizontal sharding)
-
缓存的优化:
a. 对于高频查询的字段,可以使用 Redis 来对近期新建的数据和近期被访问的数据进行缓存,因为这些数据在短时间内被再次访问的几率和频率都很大,将它们进行缓存可以有效地减少直接对数据库的请求,降低数据库的压力。而且因为缓存使用的是内存存储,速度远高于数据库(硬盘存储),大大提高了查询效率。