图文社区用户搜索关系表设计方案:空间换时间的权衡与抉择

embedded/2024/12/21 18:58:21/

背景

我们来聊一个解决方案:我们做了一个和抖音产品类似的图文社区,社区有一个搜索栏,通过名字搜索用户,搜索出来的用户需要体现出其与当前用户的关系:1.当前用户的粉丝。2.当前用户关注的人。3.互相关注。目前总用户量在两百万左右,所以,目前搜索还是用的mysql,使用的like查询,这一块不是重点讨论。重点讨论的是,如何设计用户和关注用户关系的表,使得我们在查询时能直接查询出对应的关系并排序的逻辑。

方案一

1. 用户表(user_table)

主要用来存储社区中所有用户的基本信息,结构大致如下:

字段名类型说明
user_idint(或其他合适的整型)用户的唯一标识 ID
usernamevarchar用户的名字(用于搜索栏搜索的字段等)
other_fields...诸如性别、年龄等其他用户相关的拓展字段

2. 关注关系表(follow_relationship_table)

用于记录用户之间的关注关系,结构可以这样设计:

字段名类型说明
follower_idint关注者的用户 ID,也就是执行关注操作的那个用户的 ID
followed_idint被关注者的用户 ID,也就是被他人关注的那个用户的 ID
follow_timedatetime关注操作发生的时间,可用于后续按关注先后顺序等排序,比如新关注的排在前面等情况
逻辑解释:

通过这样的表结构,当一个用户(user A)关注另一个用户(user B)时,就在该表中插入一条记录,记录中follower_id为 user A 的user_idfollowed_id为 user B 的user_id,同时记录下关注时间。

查询逻辑

当用户A在在搜索栏,输入某个用户名,比如:“明”,返回前10条记录,包含用户A关注名字中包含“明”的用户,用户A的粉丝名字中包含“明”的用户以及用户A和名字中包含“明”的相互关注的用户。

查询的SQL。

-- 子查询1:获取用户A关注的名字中包含“明”的用户信息及对应关注时间,并标记为“已关注”关系
SELECT u.user_id,u.username,'已关注' AS relationship,fr.follow_time
FROM user_table u
JOIN follow_relationship_table fr ON u.user_id = fr.followed_id
WHERE fr.follower_id = current_user_idAND u.username LIKE '%明%'
UNION ALL
-- 子查询2:获取关注用户A且名字中包含“明”的用户(即用户A的粉丝)信息及对应关注时间,并标记为“粉丝”关系
SELECT u.user_id,u.username,'粉丝' AS relationship,fr.follow_time
FROM user_table u
JOIN follow_relationship_table fr ON u.user_id = fr.follower_id
WHERE fr.followed_id = current_user_idAND u.username LIKE '%明%'
UNION ALL
-- 子查询3:获取与用户A相互关注且名字中包含“明”的用户信息及对应关注时间,并标记为“互相关注”关系
SELECT u.user_id,u.username,'互相关注' AS relationship,fr.follow_time
FROM user_table u
JOIN follow_relationship_table fr ON (u.user_id = fr.followed_id AND fr.follower_id = current_user_id)OR (u.user_id = fr.follower_id AND fr.followed_id = current_user_id)
WHERE u.username LIKE '%明%'
ORDER BY follow_time DESC  -- 根据关注时间倒序排序,最新关注的排在前面
LIMIT 10;  -- 限制只返回前10条记录

如果是这种表的设计方案,查询的复杂度非常高,需要考虑三种查询,查询效率也不高。

方案二 

1. 用户表(user_table)

结构保持和之前类似,用于存储社区中所有用户的基本信息:

字段名类型说明
user_idint(或其他合适的整型)用户的唯一标识 ID
usernamevarchar用户的名字(用于搜索栏搜索的字段等)
other_fields...诸如性别、年龄等其他用户相关的拓展字段

2. 关注关系汇总表(follow_relation_table)

新增这张表用于提前汇总每个用户与其他用户之间的关注关系情况,结构如下:

字段名类型说明
user_idint用户的唯一标识 ID,关联到 user_table 中的 user_id
related_user_idint与之存在关注关系的其他用户的 ID
relationship_typetinyint(可根据实际情况选择合适类型)用不同的数值来表示关系类型,例如 1 表示 “已关注”(当前用户关注别人),2 表示 “互相关注”,3 表示 “粉丝”(别人关注当前用户),方便后续查询判断和筛选
follow_timedatetime关注操作发生的时间,可用于排序依据等
数据维护逻辑

情况一:用户 A 关注用户 B(单向关注)

当发生用户 A 关注用户 B 这样的操作时,需要往 follow_relation_table 中插入两条记录,来分别体现从不同角度的关注关系:

  1. 第一条记录(体现用户 A 对用户 B 的关注)

    • user_id 字段值设置为用户 A 的 user_id,这表示是从用户 A 的视角出发,记录其关注行为。
    • related_user_id 字段值设置为用户 B 的 user_id,明确其关注的对象是用户 B。
    • relationship_type 字段值设置为 1,根据预先定义的规则,1 代表 “已关注”,也就是当前用户(此处为用户 A)关注了别的用户(用户 B)。
    • follow_time 字段则记录下此次关注操作发生的具体时间,可以通过获取系统当前时间来赋值,例如在很多编程语言中结合数据库操作库可以使用类似 datetime.now()(不同语言具体函数名和用法有差异)这样的方式获取当前时间并插入到该字段。
  2. 第二条记录(体现用户 B 相对用户 A 的粉丝关系)

    • user_id 字段值设置为用户 B 的 user_id,从用户 B 的角度来看待这个关注关系,即别人(用户 A)关注了自己。
    • related_user_id 字段值设置为用户 A 的 user_id,表示与自己产生关联的这个 “别人” 是用户 A。
    • relationship_type 字段值设置为 3,按照设定,3 代表 “粉丝”,意味着用户 A 是用户 B 的粉丝(因为 A 关注了 B)。
    • 同样,follow_time 字段也记录此次关注操作发生的时间,与第一条记录的时间保持一致,因为这是同一个关注行为从不同主体角度的记录。

情况二:用户 A 关注用户 C,并且用户 C 关注用户 A(双向关注,即互相关注)

当出现这种互相关注的情况时,同样需要往 follow_relation_table 中插入两条记录来体现这种对称的关系:

  1. 第一条记录(体现用户 A 与用户 C 的互相关注关系从用户 A 角度)

    • user_id 字段值设置为用户 A 的 user_id,代表是从用户 A 这边出发来看待这个关系。
    • related_user_id 字段值设置为用户 C 的 user_id,明确与之相关的是用户 C。
    • relationship_type 字段值设置为 2,因为根据定义,2 表示 “互相关注”,这里用户 A 和用户 C 互相关注了对方。
    • follow_time 字段记录此次关注操作发生的时间,获取方式同前面情况一的时间获取逻辑。
  2. 第二条记录(体现用户 A 与用户 C 的互相关注关系从用户 C 角度)

    • user_id 字段值设置为用户 C 的 user_id,从用户 C 的视角来记录这个关系。
    • related_user_id 字段值设置为用户 A 的 user_id,表明与之相关的另一方是用户 A。
    • relationship_type 字段值同样设置为 2,代表 “互相关注”,与第一条记录保持一致,因为这是双向的互相关注关系。
    • follow_time 字段也记录此次关注操作对应的时间,和第一条记录的时间相同,毕竟是同一个互相关注行为在不同主体角度的体现。

另外,需要注意的是,在实际应用中,往往需要先判断是否已经存在相关的关注关系记录,以避免重复插入等情况导致数据异常。比如在执行用户 A 关注用户 B 的操作前,可以先查询 follow_relation_table 看是否已经存在对应的记录,如果不存在再执行上述插入逻辑;对于互相关注情况,判断会更复杂些,可能需要从两个方向去检查是否已有单向关注记录等,具体的判断逻辑也可以通过 SQL 查询语句结合业务代码来实现,这里为了聚焦数据写入逻辑暂未详细展开判断部分内容。

同时,随着业务的发展,可能还会涉及到取消关注等操作,那时就需要相应地去更新或者删除 follow_relation_table 中的相关记录,以保证数据的准确性和一致性,这也是后续在数据维护方面需要进一步考虑的问题。

查询逻辑

当用户A在在搜索栏,输入某个用户名,比如:“明”,返回前10条记录,包含用户A关注名字中包含“明”的用户,用户A的粉丝名字中包含“明”的用户以及用户A和名字中包含“明”的相互关注的用户。

查询的SQL。

SELECT ut.username,fr.relationship_type,fr.follow_time
FROM user_table ut
JOIN follow_relation_table fr ON ut.user_id = fr.related_user_id
WHERE fr.user_id = current_user_idAND ut.username LIKE '%明%'
ORDER BY fr.follow_time DESC
LIMIT 10;

这种方案,写入带来了极大麻烦,并且数据是双份写入。但是,查询却带来极大的便利。但是,对于读多写少的场景,我们认为这种方案是可取的。

总结

本质上第二种方案也是一种空间换时间的方案,把复杂的查询退化成简单的查询。空间换时间是一种思维方式,不能仅仅狭隘的认为用缓存换时间是空间换时间,同一个表基于不同的分表键或者分库健进行分表分库是空间换时间


http://www.ppmy.cn/embedded/147604.html

相关文章

CPU性能优化-基于源代码的CPU调优

一 在第二部分,我们将讨论如何使用CPU监控特性寻找CPU上运行的代码中可被调优的位置。对于性能敏感型应用程序,如大型分布式云服务,科学高性能计算软件,3A 级游戏等,了解底层硬件的工作原理是非常重要的。若在程序开发…

11篇--图像边缘检测

图像梯度 要学习图像边缘检测,要先了解图像梯度的概念,我们正是通过梯度值来区分边缘像素点的 处于边缘附近的像素点与周围像素点的差距很大(不然不会有边缘呈现),所以给边缘附近的的梯度之变化很快,通过…

git使用和gitlab部署

1.ci,cd,DevOps ci:持续集成:开发的代码集成到代码仓库 cd:持续交互:从代码仓库拉取代码到部署到测试环境 cd:持续部署:从代码仓库拉取代码到部署到生产环境 DevOps:开发写完的代码自动集成&#xff0c…

ResNext-50模型进行图像识别

本文为为🔗365天深度学习训练营内部文章 原作者:K同学啊 import numpy as np from keras.preprocessing.image import ImageDataGenerator from keras.utils import to_categorical from keras.models import Sequential from keras.layers import Input…

linux-----数据库

Linux下数据库概述 数据库类型: 关系型数据库(RDBMS):如MySQL、PostgreSQL、Oracle等。这些数据库以表格的形式存储数据,表格之间通过关系(如主键 - 外键关系)相互关联。关系型数据库支持复杂的…

MCU驱动使用

一、时钟的配置: AG32 通常使用 HSE 外部晶体(范围:4M~16M)。 AG32 中不需要手动设置 PLL 时钟(时钟树由系统自动配置,无须用户关注)。用户只需在配置文件中给出外部晶振频率和系统主频即可。 …

如何确保品牌色在VR虚拟展厅中保持一致性?

确保品牌色在VR虚拟展厅中的一致性对于品牌视觉传达至关重要。品牌色不仅是企业视觉识别系统的重要组成部分,而且在虚拟环境中,它们对于塑造品牌形象和提升用户体验具有决定性作用。 接下来,由专业从事VR虚拟展厅制作的圆桌3D云展厅平台为大家…

【ETCD】【实操篇(四)】etcd常见问题快问快答FAQ

原文:https://etcd.io/docs/v3.5/faq/ 目录 etcd, 一般问题配置相关部署相关操作相关性能相关其他问题 etcd, 一般问题 什么是 etcd? etcd 是一个一致性的分布式键值存储。它主要作为分布式系统中的独立协调服务,设计用于存储可以完全放入内…