相互关注互粉mysql实现

2024-05-05 23:48

本文主要是介绍相互关注互粉mysql实现,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!


关注关系产生的四种关系状态

  • 关注
  • 粉丝
  • 双向关注(互粉)
  • 无关系

用词follower表示粉丝 -- 追随者
用词following表示关注 -- 追随

设计的结构必须能满足以下功能:

  • 查询关注列表
  • 查询粉丝列表
  • 查询双向关注列表
  • 判断两个用户的关系
  • 查询带关系状态的任一列表

第一种方案

用一行纪录表示关注和粉丝,字段u2的值表示粉丝,u1表示被关注者。

Table: user(用户表)
img

Table: follower(u2表示粉丝, u1表示被关注的人)
img

查询用户id = 1的关注列表

SELECT * FROM follower WHERE u2 = 1

查询用户id = 1的粉丝列表

SELECT * FROM follower WHERE u1 = 1

查询用户id = 1的双向关注列表

SELECT t1.* FROM (SELECT * FROM follower WHERE u2 = 1)  AS t1 INNER JOIN follower t2 ON t1.u1 = t2.u2 LIMIT 10

判断两个用户的关系(id = 1 --> id = 5)

SELECT * FROM follower WHERE (u2 = 1 or u1 = 1) AND (u2 = 5 or u1 = 5) LIMIT 3

id = 1的用户查询所有 id < 5的用户,并显示关系

img

如上图所示,要查询的用户的那个圈,被分成了四个部分(上面讲的四种状态):

  • 关注了我的用户
  • 和我互粉的用户
  • 我关注了的用户
  • 我未关注的用户

以上复杂的集合关系,通过单一SQL根本无法实现。

要查询的用户与粉丝集合的交集:

SELECT * FROM (SELECT * FROM user WHERE id < 5) AS t1
INNER JOIN(SELECT * FROM follower WHERE u1 = 1) AS t2ON t1.id = t2.u2

要查询的用户与关注集合的交集:

SELECT * FROM (SELECT * FROM user WHERE id < 5) AS t1
INNER JOIN(SELECT * FROM follower WHERE u2 = 1) AS t3ON t1.id = t3.u1

其他的部分可以通过以上两步查询出来的数据,在内存中作计算得出。

因为关注关系是互相的,用一行纪录即可表示。以上的设计其实是把关注和粉丝的概念用一行纪录表达。这样会引来一个缺点,当follower非常大的时候,对follower表进行分片,如果按u1或者u2分片,假设按u1分片,那么将导致关注列表,即下面的查询要做聚合。

SELECT * FROM follower WHERE u2 = 1

选择u1分片后,u2 = 1的数据行将会落到不同的分片上。

SELECT * FROM follower_0 WHERE u2 = 1
UNION 
SELECT * FROM follower_1 WHERE u2 = 1

而粉丝列表的查询不会受影响,同一个用户的所有粉丝分在一个片上。

SELECT * FROM follower_1 WHERE u1 = 1

如果按u2分片,同样也会导致粉丝列表会落在不同的分片上。两个查询不可能同时满足分片。

如果分片是跨数据库或者是跨主机的方案,问题会变得更复杂。

针对方片的优化方案

可以用冗余数据的办法来解决数据分片带来的问题,即将关注和粉丝分2个表存放。 用follower表存放粉丝
用following表存放关注

当用户Ub关注Ua,分别往follower, following写入一行纪录。 (Ua -> Ub) 只是他们表示的含义不同。

follower表示Ua的粉丝是Ub
following表示Ub关注Ua

分片的时候,同时对follower和following进行分片。同时上面分析的所有查询方法也要相应改变,思路还是一样,只是单个表的自联接变成2个表的联接。

以上方案缺点就是数据量会增加一倍,进行关注或者取消关注的写操作会多一次,要同时维护2个表的数据。

以上优化虽然解决了一些问题,但同时也带来一些问题。可见关系型数据库在处理用户关系的时候,表现得很吃力。我们不得不承认,虽然叫“关系”型数据库却不太懂得处理集合关系。

另一种方案

还有一种方案,即用一行纪录表示出两个用户之间的所有关系,此方案能节省很大的数据空占用。

字段: u1, u2, type

type=1 表示u2关注u1
type=2 表示u1,u2互相关注
type=0 表示u1,u2无关系(默认)

保证插入数据时,u1是被关注者,u2是粉丝(当然你也可以换过来,只是逻辑会变了)

每次写入数据时要检查当前的状态:

如果u1(1) -> u2(2)纪录已经存在(u2已经关注u1),这个时候u1再关注u2,只需要将type字段的值变为type = 2。

如果u1(1) -> u2(2) type(2)时,即u1和u2互相关注,如果有一个人取消关注,问题会很复杂,最坏的情况要修改整行纪录,交换u1,u2这两个字段的值,再修改type=1。

同时上面的方案查询也会变化。例如要查询id = 1的粉丝列表:

SELECT * FROM table WHERE u1 = 1 OR (u2 = 1 AND type = 2)

例如要查询id = 1的关注列表:

SELECT * FROM table WHERE u2 = 1 OR (u1 = 1 AND type = 2)

上面的方案只强调关注关系,双向关系只是在单一关系上用字段区分,关注的先后关系很明显,事务性更强。

查询id = 1的双向关注

SELECT * FROM table WHERE type = 2 AND (u1 = 1 OR u2 = 1)

这个方案虽然节省数据空间,但是不容易理解,而且写入时每次要检查判断当前的关系,逻辑上过于复杂。而且数据量大后,由于查询WHERE条件同时有u1和u2,很难进行分片。

其他一些问题

  • ua与ub的共同关注列表
  • ua与ub的共同粉丝列表
  • ua的关注列表里谁关注了ub

以上的关系计算大家可能很容易理解,但要在MySQL里实现,是非常难的。

id = 3与id = 2的共同关注列表:

SELECT  u1, COUNT(id) AS num FROM follower WHERE u2 = 3 OR u2 = 2
GROUP BY u1 HAVING num > 1

id = 3与id = 1的共同粉丝列表:

SELECT  u2, COUNT(id) AS num FROM follower WHERE u1 = 3 OR u1 = 1
GROUP BY u2 HAVING num > 1

当然你可以用集合的方法查询:

SELECT t1.u2 FROM 
(SELECT  u2 FROM follower WHERE u1 = 3) AS t1INNER JOIN
(SELECT  u2 FROM follower WHERE u1 = 1) AS t2ON t1.u2 = t2.u2

id = 1的关注列表里谁关注了id = 5

SELECT u2 FROM (SELECT u2 FROM follower WHERE u1 = 2) AS t1
INNER JOIN 
(SELECT u1 FROM follower WHERE u2 = 1) AS t2
ON t2.u1 = t1.u2
  • MySQL数据库
最新 最早 最热
  • 6条评论 
  • 1条新浪微博
  • ManKane
    ManKane

    楼主写的很强大,从这三种方式看来:我觉得第三种最繁琐,因为用一个字段来维持关系,其逻辑比较复杂。第二种的话维护的数据量比较多。目前大多数互联网公司可能采用的都是第一种和第二种。

  • wchao
    wchao

    查询id是1的双向关注列表应该是:
    SELECT t1.* FROM (SELECT * FROM follower WHERE `u2` = 1) AS t1 INNER JOIN (SELECT * FROM follower WHERE `u1` = 1) AS t2 ON t1.u1 = t2.u2;
    吧?

  • 贾朝藤
    贾朝藤

    SELECT t1.* FROM (SELECT * FROM follower WHERE u2 = 1) AS t1 INNER JOIN follower t2 ON t1.u1 = t2.u2 LIMIT 10 这个SQL有问题,t2没有条件

  • ManKane
    ManKane

    回复 wchao: 恩 应该是这样子的

  • 我曾经喜欢过你
    我曾经喜欢过你

    文章分析非常不错! 个人倾向第二种空间换效率和逻辑。楼主博客不错,持续关注中... 希望能看到更多精彩的文章。 顶!




这篇关于相互关注互粉mysql实现的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/962996

相关文章

MySQL 删除数据详解(最新整理)

《MySQL删除数据详解(最新整理)》:本文主要介绍MySQL删除数据的相关知识,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录一、前言二、mysql 中的三种删除方式1.DELETE语句✅ 基本语法: 示例:2.TRUNCATE语句✅ 基本语

MySQL中查找重复值的实现

《MySQL中查找重复值的实现》查找重复值是一项常见需求,比如在数据清理、数据分析、数据质量检查等场景下,我们常常需要找出表中某列或多列的重复值,具有一定的参考价值,感兴趣的可以了解一下... 目录技术背景实现步骤方法一:使用GROUP BY和HAVING子句方法二:仅返回重复值方法三:返回完整记录方法四:

IDEA中新建/切换Git分支的实现步骤

《IDEA中新建/切换Git分支的实现步骤》本文主要介绍了IDEA中新建/切换Git分支的实现步骤,通过菜单创建新分支并选择是否切换,创建后在Git详情或右键Checkout中切换分支,感兴趣的可以了... 前提:项目已被Git托管1、点击上方栏Git->NewBrancjsh...2、输入新的分支的

从入门到精通MySQL联合查询

《从入门到精通MySQL联合查询》:本文主要介绍从入门到精通MySQL联合查询,本文通过实例代码给大家介绍的非常详细,需要的朋友可以参考下... 目录摘要1. 多表联合查询时mysql内部原理2. 内连接3. 外连接4. 自连接5. 子查询6. 合并查询7. 插入查询结果摘要前面我们学习了数据库设计时要满

Python实现对阿里云OSS对象存储的操作详解

《Python实现对阿里云OSS对象存储的操作详解》这篇文章主要为大家详细介绍了Python实现对阿里云OSS对象存储的操作相关知识,包括连接,上传,下载,列举等功能,感兴趣的小伙伴可以了解下... 目录一、直接使用代码二、详细使用1. 环境准备2. 初始化配置3. bucket配置创建4. 文件上传到os

MySQL查询JSON数组字段包含特定字符串的方法

《MySQL查询JSON数组字段包含特定字符串的方法》在MySQL数据库中,当某个字段存储的是JSON数组,需要查询数组中包含特定字符串的记录时传统的LIKE语句无法直接使用,下面小编就为大家介绍两种... 目录问题背景解决方案对比1. 精确匹配方案(推荐)2. 模糊匹配方案参数化查询示例使用场景建议性能优

关于集合与数组转换实现方法

《关于集合与数组转换实现方法》:本文主要介绍关于集合与数组转换实现方法,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1、Arrays.asList()1.1、方法作用1.2、内部实现1.3、修改元素的影响1.4、注意事项2、list.toArray()2.1、方

使用Python实现可恢复式多线程下载器

《使用Python实现可恢复式多线程下载器》在数字时代,大文件下载已成为日常操作,本文将手把手教你用Python打造专业级下载器,实现断点续传,多线程加速,速度限制等功能,感兴趣的小伙伴可以了解下... 目录一、智能续传:从崩溃边缘抢救进度二、多线程加速:榨干网络带宽三、速度控制:做网络的好邻居四、终端交互

mysql表操作与查询功能详解

《mysql表操作与查询功能详解》本文系统讲解MySQL表操作与查询,涵盖创建、修改、复制表语法,基本查询结构及WHERE、GROUPBY等子句,本文结合实例代码给大家介绍的非常详细,感兴趣的朋友跟随... 目录01.表的操作1.1表操作概览1.2创建表1.3修改表1.4复制表02.基本查询操作2.1 SE

MySQL中的锁机制详解之全局锁,表级锁,行级锁

《MySQL中的锁机制详解之全局锁,表级锁,行级锁》MySQL锁机制通过全局、表级、行级锁控制并发,保障数据一致性与隔离性,全局锁适用于全库备份,表级锁适合读多写少场景,行级锁(InnoDB)实现高并... 目录一、锁机制基础:从并发问题到锁分类1.1 并发访问的三大问题1.2 锁的核心作用1.3 锁粒度分