上一篇文章概括地描述了千寻使用的通讯方式和数据库,本篇文章我们将重点探讨一下千寻的数据库该如何设计。
在设计数据库前,我将千寻涉及到的主要功能列在下面。当然实际上,在明确功能点和需求上,更好的手段是编写用户故事。不过,这里还是用这样简单的方式来说明问题:
- 注册、登录,个人资料包括性别、出生日期、签名、照片、星座、学校等
- 通过通讯录、邮箱等方式添加好友
- 点对点聊天
- 发现周围人
- 利用兴趣爱好来辅助查找周围人
- 好友分组
- 设置在线状态:在线、仅好友可见、隐身
- 创建基于话题和基于好友的两种讨论组
- 发起活动,活动包括标题、地点(可选)、简介等信息
- 参与活动
- 白板讨论
结合上篇文章所述关于数据持久化的内容,我们对这些功能涉及到的数据做如下区分:
- 保存在MongoDB中、缓存在Redis中的数据
- 保存在Redis中的数据
第一类数据包括:
-
用户信息(UserProfile)
用户信息包括用户名(邮箱)、密码(MD5+salt)、昵称、性别、出生日期、星座、签名、学校、职业、手机号、上次活动时间和地理位置等基本信息。此外用户信息文档(Document)里,还保存有好友集合、好友分组集合、标签(如兴趣)集合。这些信息可以以列表的形式保存在MongoDB中。
此外,还需要对邮箱、手机号和地理位置建立索引,以便快速查找。
-
活动信息(Activity)
活动信息包括活动的标题、活动时间、活动地点(文字描述+坐标)、简介、发起人和参与者列表。
为了进一步提高效率,我们还需要使用Redis作为缓存来保存这些数据及相关结果:
-
当用户上线时,用户的信息应当被加载到Redis中,存储方式为:
-
在线列表
无序集合。key: "onlines", val: ("user_id1", "user_id2", ...)
-
用户资料
信息:哈希。key: "user:user_id", val: {"username": XXX, "gender": XXX, ...}
好友&分组:无序集合。key: "groups:user_id:group_name",val: ("user_id1", "user_id2", ...)
兴趣:无序集合。key: "interests:user_id", val: ("word1", "word2", ...)
-
-
系统启动时,活跃用户(例如最近7天有过活动的用户)的资料也按上述方式加载到Redis中。
-
系统启动时,从过去某一天(例如30天前)起的活动被加载到内存中:
基本信息:哈希。key: "activity:activity_id", val: {"title": XXX, "introduction":XXX, "place": XXX, "position": XXX, ...}
活动参与者:无序集合。key: "activity:activity_id:participants", val: ("user_id1", "user_id2", ...)
发起的活动:有序列表(按照活动日期排列)。key: "activity:user_id:own", val: ("activity_id1", "activity_id2", ...)
参与的活动:有序列表(按照活动日期排列)。key: "activity:user_id:in", val: ("activity_id1", "activity_id2", ...)
第二类数据包括:
-
点对点消息
有序列表。key: "message:from_id:to_id",val: ["time|content", "time|content", ...]
-
话题讨论组
有序列表。key: "topic:sub_id:topic_name", val: ["nickname|time|content", "nickname|time|content", ...]
-
好友讨论组
讨论组列表:无序集合。key: "chatgrp:user_id:list:group_name",val: ("user_id1", "user_id2", ...)
讨论组消息:有序列表。key: "chatgrp:sub_id:msg:group_name", val:["nickname|time|content", "nickname|time|content", ...]
-
白板讨论
使用"Pub/Sub"功能即可。