IM 朋友圈后台存储设计

数据库表设计

存储用户发的微博 weibo

CREATE TABLE weibo ( id bigint(20) NOT NULL AUTO_INCREMENT, account_id bigint(20) NOT NULL, photos_bin blob, msg_content varchar(2048) DEFAULT NULL, srv_timestamp bigint(20) NOT NULL, msg_datetime datetime(6) NOT NULL, thumbup_times int(10) NOT NULL DEFAULT '0' COMMENT '点赞次数', PRIMARY KEY (id), KEY accountId_msgDatetime (account_id,msg_datetime) USING BTREE) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

各字段含义

  • account_id

用户 id

  • photos_bin

存放微博上的图片链接的 ( 由 protobuf 序列化后的 ) 二进制, 读取时需要用 db_struct.proto 中的 message weiboPhotos 反序列化; 写入时需要用该 protobuf 结构体序列化为二进制

message weiboPhotos

{

​ repeated string photos = 1;

}

  • msg_content

微博的文字信息, 最大长度为 2048 个英文,或者 2048 / 3 = 682 个中文

  • srv_timestamp

(发)微博(时的服务器)时间戳, 表示用户的微博 id, 属于业务逻辑字段

  • msg_datetime

srv_timestamp 的日期格式

  • thumbup_times

这条微博的点赞次数: update weibo set thumbup_times = thumbup_times + 1 where .....

 

redis 存储逻辑

用 redis list 存储用户发的微博 id( weibo_id 字段 ), 用 [ list:weibo:account_id ] 作为这个 list 的 key

用 redis zset 存储用户发的微博 id( weibo_id 字段 ), 用 [ zset:weibo:account_id ] 作为这个 zset 的 key

redis string 保存此微博的内容 ( 可以是整条数据库记录 ),用 [ string:weibo:account_id:微博id ] 作为这个 string 的 key

用户发微博后,往 [ list:weibo:account_id ] 和 [ zset:weibo:account_id ] 中插入微博 id, 用 [ string:weibo:account_id:微博id ] 保存微博内容

用户要查看自己的微博,从 [ list:weibo:account_id ] 中获取到若干微博 id, 然后根据微博 id 用 redis MGET (参数是 微博 id ) 指令从 [ string:weibo:account_id:微博id ] 中获取到微博内容

好友要查看用户的微博, 从 [ zset:weibo:account_id ] 中获取到前3天微博 id, 然后根据微博 id 用 redis MGET (参数是 微博 id ) 指令从 [ string:weibo:account_id:微博id ] 中获取到微博内容

Mysql 存储逻辑

用户发微博后,往 mysql weibo 表中插入一条记录,

 

用户删除的微博 weibo_deleted

CREATE TABLE weibo_deleted ( id bigint(20) NOT NULL AUTO_INCREMENT, account_id bigint(20) NOT NULL, photos_bin blob, msg_content varchar(2048) DEFAULT NULL, srv_timestamp bigint(20) NOT NULL, msg_datetime datetime(6) NOT NULL, thumbup_times int(10) NOT NULL DEFAULT '0' COMMENT '点赞次数', del_datetime datetime(6) NOT NULL, PRIMARY KEY (id), KEY accountId_msgDatetime_delDatetime (account_id,msg_datetime,del_datetime) USING BTREE) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

 

各字段含义

最后一个字段 del_datetime 表示删除微博时的时间, 其他字段跟 weibo 表一致

redis操作逻辑

从 [ list:weibo:account_id ] 和 [ zset:weibo:account_id ] 中删除微博 id( weibo_id 字段 ),

删除 [ string:weibo:account_id:微博id ]

mysql 存储逻辑

用户删除一条微博时, 从 weibo 表中复制相关记录插入到 weibo_deleted 表中, 然后再删除 weibo 表中的记录

 

存储微博点赞信息 weibo_like

CREATE TABLE weibo_like ( id bigint(20) NOT NULL AUTO_INCREMENT, account_id bigint(20) NOT NULL, srv_timestamp bigint(20) NOT NULL, liker_id bigint(20) NOT NULL COMMENT '点赞者信息集合', thumbup_timestamp datetime(6) NOT NULL COMMENT '点赞时刻', PRIMARY KEY (id), KEY accountId_srvTimestamp_thumbupTimestamp (account_id,srv_timestamp,thumbup_timestamp) USING BTREE) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

 

各字段含义

  • account_id

被点赞用户的 account_id

  • srv_timestamp

被点赞的微博 id, 也就是 weibo 表中的 srv_timestamp 字段

  • liker_id

点赞者的 account_id

  • thumbup_timestamp

点赞时刻

 

redis 存储逻辑

"我"的每条微博有一个 redis hash , 为"我"点赞的 [ 用户 id ] - [ 点赞时的服务器时间戳 ], 作为 [ 字段 ] - [ 值 ] 写入进去, 这个 redis hash 供 "我"本人和好友查看; [ hash:weibo_like:account_id:微博id ] 作为 hash 的 key

"我"的每条微博有一个 set, 为"我"点赞的用户 id 全部写入进去, 这个 set 用来计算和其他用户的共同好友, [ set:weibo_like:account_id:微博id ] 作为 set 的 key

"我"删除某条微博后, 就把这条微博对应的 redis hash ( hash:weibo_like:account_id:微博id ) 和 redis set ( set:weibo_like:account_id:微博id ) 删除掉

 

mysql 存储逻辑:

用户 A 为"我"的某条微博点赞后, 在 weibo_like 中插入新记录, 把 "我" 的用户 ID ( account_id 字段 )和微博 ID ( srv_timestamp 字段), 用户 A 的 account_id, 当前时间戳赋值给这条记录

 

 

存储其他用户在"我"的微博下的留言 weibo_reply

CREATE TABLE weibo_reply ( id bigint(20) NOT NULL AUTO_INCREMENT, account_id bigint(20) NOT NULL COMMENT '朋友圈发布者Id', weibo_id bigint(20) NOT NULL COMMENT '当前朋友圈Id', comment_id bigint(20) NOT NULL COMMENT '此条朋友圈评论的评论Id', sender_id bigint(20) NOT NULL COMMENT '此条朋友圈评论的发布者Id', be_comment_id bigint(20) DEFAULT NULL COMMENT '此条朋友圈评论回复评论Id', receiver_id bigint(20) DEFAULT NULL COMMENT '此条朋友圈回复评论的用户Id', msg_content varchar(2048) NOT NULL, reply_datetime datetime(6) NOT NULL, deleted int(10) DEFAULT '0' COMMENT '此条评论所在是朋友圈状态,0为正常,1为删除状态', status int(10) NOT NULL DEFAULT '0' COMMENT '用户评论状态,0为正常,1为删除', PRIMARY KEY (id), KEY weiboId,commentId (weibo_id,comment_id)) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=utf8mb4;

各字段含义

  • account_id

用户 id; B 在 A 的某条微博下留言,那么 account_id = A 用户 id

  • weibo_id

微博 id, 发微博时的时间戳, 也就是 weibo 表中的 weibo_id; B 在 A 的某条微博下留言,那么 weibo_id = A 的某条微博 id

  • sender_id

在微博下留言的用户 id, B 在 A 的某条微博下留言, send_id = B 用户 id

  • comment_id ( 雪花 id )

B 在 A 的微博下评论, 假设这个评论 ID = 3( comment_id = 3 ), sender_id 就是 B 的 ID; receiver_id 和 be_comment_id 为空

  • be_comment_id ( = 被评论的 id )

接上, A ( 或者 C ) 回复 B 的评论( id = 3), 生成评论 id = 4, 此时 comment_id = 4, be_comment_id = 3, receiver_id 是 B 的用户 id, send_id = A ( 或者 C ) 的用户 id

  • receiver_id

B 在"我"的某条微博下, @ C 用户,那么 receiver_id 就是 C 的用户 id; 如果 B 没有 @ 任何人, receiver_id = 0

  • msg_content

微博留言内容

  • reply_datetime

回复时间

  • deleted

发微博的人,删除微博后,这条微博下所有的留言记录, 都要把 deleted = 1, 并不从数据库删除,当做备份

  • status

用户评论状态,0为正常,1为删除

 

redis存储逻辑

"我"的每条微博下的所有回复, 用一个 redis list 保存, 也就是说 有多少条微博,就有多少个 redis list for weibo reply

用 [ list:weibo_reply:account_id:微博id ] 来作为 list 的 key, 这个 list 供 "我" 和"我"的好友查看微博留言时使用

备注: 由于同一个用户会在微博下留言多次,所以无法使用 redis zset 来保存留言

"我"的每条微博有一个 set, 为"我"留言的用户 id 全部写入进去, 这个 set 用来计算和其他用户的共同好友, [ set:weibo_reply:account_id:微博id ] 作为 set 的 key

"我"删除某条微博后,要把 [ list:weibo_reply:account_id:微博id ] 和 [ set:weibo_reply:account_id:微博id ] 删除掉

 

mysql 存储逻辑:

用户 B 要在用户 A 的某条微博下留言时, 往 weibo_reply 表中插入一条记录, account_id = A 用户id, weibo_id = A 的微博 id, sender_id = B 用户id, receiver_id = 0, msg_content = 留言内容

如果用户 B 要在用户 A 的某条微博下 回复 C 用户, 就往 weibo_reply 表中插入一条记录, account_id = A 用户id, weibo_id = A 的微博 id, sender_id = B 用户id, receiver_id = C 用户id, msg_content = 留言内容

"我"删除某条微博后,执行: update weibo_reply set deleted = 1 where account_id = A 用户id and srv_timestamp = 微博 id

 

存储动态时间线 weibo_action_log

当"我"发了一条微博/为朋友的微博点赞/在某个朋友的微博下留言, 就会在 weibo_action_log 中插入一条记录,即生成一条动态

CREATE TABLE weibo_action_log ( id bigint(20) NOT NULL AUTO_INCREMENT, account_id bigint(20) NOT NULL, weibo_id bigint(20) NOT NULL COMMENT '用户发微博,点赞,在别人微博下留言时的时间戳', action int(6) NOT NULL DEFAULT '0' COMMENT '0 发微博, 1 点赞, 2 在某个朋友的微博下留言', friend_id bigint(20) DEFAULT '0' COMMENT '只有为别人点赞或留言时, friend_id 才有意义', friend_srv_timestamp bigint(20) DEFAULT NULL COMMENT '只有为别人点赞或留言时, friend_srv_timestamp 是对方的微博 id', action_datetime datetime(6) NOT NULL, PRIMARY KEY (id), KEY accountId_srvTimestamp (account_id,srv_timestamp) USING BTREE) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

各字段含义

  • account_id

产生微博动态的用户 id

  • srv_timestamp

微博 id, 就是 weibo 表中相关记录的 srv_timestamp 字段

  • action

行为类型, 0 发微博, 1 为好友点赞, 2 在某个朋友的微博下留言

  • friend_id

只有 action = 1 或者 2 时, 此字段才有意义; 表示好友的用户 id

  • friend_srv_timestamp

只有 action = 1 或者 2 时, 此字段才有意义; 表示好友的微博 id, 即 weibo 表中相关记录的 srv_timestamp 字段的值

  • action_datetime

当前时间

 

redis 存储逻辑

每个用户有 2 个 redis zset, zsetMine 保存个人动态, zsetFriend 保存朋友们的动态

用户发微博后,会产生一个动态,并进行两次 redis 操作

  • 插入到个人的 zsetMine 中

  • 用 redis 流水线打包多个命令到 redis, 往每个好友的 zsetFriend 插入一条动态日志

用户 B 为好友 A 点赞,在好友 A 的微博下留言时,会产生一个动态,并进行下面的 redis 操作

  • 插入到 B 的 zsetMine 中

  • 在 redis 中利用 set 查出 A, B 两个人的共同好友

  • 用流水线打包多个命令到 redis, 往好友 A 以及每个AB共同好友的 zsetFriend 插入动态日志

用户 B 在好友 A 微博下,回复其他用户 C 时( B C 必须是好友才看得见对方,这里要做合法性判断,禁止回复非好友), 会产生一个动态,并进行下面的 redis 操作

  • 插入到 B 的 zsetMine 中

  • 在 redis 中利用 set 查出 A, B, C 三个人的共同好友

  • 用流水线打包多个命令到 redis, 往好友 A, C 以及每个ABC共同好友的 zsetFriend 插入动态日志

 

用户登录后,依次从 zsetFriend 获取到一定数量的好友动态, 聚合后下发

用户从 zsetMine 中获取个人历史动态

mysql 存储逻辑

用户发微博后, 往 weibo 表( 前面已完成 ) 和 weibo_action_log 中插入记录; 为 weibo_action_log 插入记录时, srv_timestamp = weibo 表中相关记录的 srv_timestamp 字段, action字段 = 0, friend_id, friend_srv_timestamp 两个字段为空

用户为好友某个微博点赞后, 往 weibo_like 表( 前面已完成 ) 和 weibo_action_log 中插入记录; 为 weibo_action_log 插入记录时, srv_timestamp = 0, action字段 = 1, friend_id 字段 = 好友 account_id, friend_srv_timestamp 字段 = 好友的微博 id

用户在好友微博留言后, 往 weibo_reply 表( 前面已完成 ) 和 weibo_action_log 中插入记录; 为 weibo_action_log 插入记录时, srv_timestamp = 0, action字段 = 2, friend_id 字段 = 好友 account_id, friend_srv_timestamp 字段 = 好友的微博 id

特殊说明

由于动态的数据量,与好友数量呈几何级增长,所以在 mysql 中就把个人动态存储到 weibo_action_log 中,如果需要获得好友动态,可以通过 weibo_action_log 中的记录计算出来(并加载到 redis), 需要专门做一个工具来实现这个功能(交给天文做)

 

A 查看朋友圈动态

接上, 每个用户有 2 个 redis zset, zsetMine 保存个人动态, zsetFriend 保存朋友们的动态

所以从 zsetFriend 中可以获取到最近几天的朋友们的动态; 由于朋友可能是先发动态,然后两者解除朋友关系,所以服务端下发动态给用户时,需要过滤掉非好友动态

 

A 查看 B 某条微博的点赞列表

A 只能看到 A B 共同好友为 B 点赞

  • 1 设 redis 中 A 的好友集合为 setA

  • 2 设 redis 中 B 的某条微博的点赞集合为 setB ( setB 的 key = set:weibo_like:account_id:微博id )

  • 3 对 setA 和 setB 求交集,就是 A 能看见的点赞好友的 id 集合; 如果集合是空,就不用执行 4

  • 4 对 hash:weibo_like:account_id:微博id 一次性取多个字段的值 ( 3 的结果 作为 redis 指令 HMGET 的参数 ),获得若干个 [ 好友 id ] - [ 留言时间戳 ], 下发给客户端; 客户端根据留言时间戳进行排序并显示在 UI 上

  • 备注: 不确定使用 lua 是否可以一次完成 3, 4 的功能, 即一次 redis IO 访问就能计算出结果

 

A 查看 B 某条微博的留言列表

A 只能看到 A B 共同好友的留言

  • 1 设 redis 中 A 的好友集合为 setA

  • 2 设 redis 中 B 的某条微博的好友回复集合为 setB ( setB 的 key = set:weibo_reply:account_id:微博id )

  • 3 对 setA 和 setB 求交集,就是 A 能看见的留言的好友 id 集合; 如果集合是空,就不用执行 4

  • 4 从 list:weibo_reply:account_id:微博 id 中获取到所有的留言者, 和 3 的结果取交集,就是 A 能看见的留言

  • 备注: 不确定使用 lua 是否可以一次完成 3, 4 的功能, 即一次 redis IO 访问就能计算出结果

 

你可能感兴趣的:(工作中积累的开发笔记)