Php 社交网站的数据库关系或朋友表设计

Php 社交网站的数据库关系或朋友表设计,php,mysql,database,database-design,Php,Mysql,Database,Database Design,你好,我正在创建一个社交网站,我想知道我将如何创建用户之间的关系。很多网站都说我应该创建一个关系/朋友表,但我展望未来,认为这是无效的。这个想法可能会像facebook一样流行,我想为这么多的用户做好准备。Facebook拥有4亿用户,因此好友表的用户数至少是这一数字的150倍。我想,为朋友做一个查询会非常慢。因此,解决方案是为每个用户提供一个单独的表,其中包含他们的好友ID。或包含ID的关联CSV文件。任何对我的网站设计的帮助都将不胜感激。谢谢您建议的两种选择无疑都会导致悲伤——想象一下,4亿

你好,我正在创建一个社交网站,我想知道我将如何创建用户之间的关系。很多网站都说我应该创建一个关系/朋友表,但我展望未来,认为这是无效的。这个想法可能会像facebook一样流行,我想为这么多的用户做好准备。Facebook拥有4亿用户,因此好友表的用户数至少是这一数字的150倍。我想,为朋友做一个查询会非常慢。因此,解决方案是为每个用户提供一个单独的表,其中包含他们的好友ID。或包含ID的关联CSV文件。任何对我的网站设计的帮助都将不胜感激。谢谢

您建议的两种选择无疑都会导致悲伤——想象一下,4亿张表,或者管理4亿个文件


当然,最好是维护一个正确索引的关系表。

当您考虑最终支持数百万用户时,您只会看到一个特定的好友列表-这大大限制了实际数据量

为了在数据库中维护规范化的友谊关系,需要两个表:

使用者
  • 用户id(主键)
  • 用户名
朋友
  • 用户id(主键,用户外键(用户id))
  • 朋友id(主键,用户外键(用户id))

这将阻止重复(即:1,2)的发生,但不会停止反转,因为(2,1)是有效的。您需要一个触发器来强制执行关系只有一个实例…

构建您今天需要的模式,而不是您认为5年后需要的模式

你认为facebook设计的模式在第一天就能支持4亿用户吗?当然不是。这种规模的建筑是复杂的,昂贵的,老实说,如果你现在就尝试,你可能会弄错,以后无论如何都要重做

老实说,你中彩票的几率要比在短期内达到4亿用户的几率大得多。即使你这样做了,到那时,你的项目将有数百名工程师——足够的带宽用于重新设计你的模式

现在是时候建立简单的

编辑添加一些实体示例:

:

他们经历了一个共同的演变: 单个服务器,转到单个主服务器 然后使用多个读从机 对数据库进行分区,然后 决定采用分片方式

保持简单!简单让你 更快地重新构建,以便 对问题作出反应。的确 没有人真正知道什么是简单 是的,但是如果你不害怕 那是个好兆头 简单化正在发生

Livejournal也从单一服务器上的单一数据库发展到


我相信你可以在

上找到更多的例子,如果你期待Facebook取得的成功水平(我喜欢你的自信),你很快就会意识到他们实现了什么。关系数据库开始出现不足,您需要研究解决方案


也就是说,为什么要为4亿用户进行预优化?建立一个现在可以为50万用户使用的系统。如果在此之后需要重新设计,那么您必须非常成功,并且有足够的资源这样做。

您可以使用一个表来表示一个用户与另一个用户之间的“关系”。这本质上是同一个表中两个不同行之间的表。示例联接表可能包括以下列:

  • 用户1\u ID
  • 用户2\u ID

获取好友列表,这些好友执行相关用户到关系表的内部联接,并返回到用户表上的第二个实例

在代码中,将关系插入表时,遵循约定

issueSQLQuery("INSERT INTO relationships (friend1, friend2) 
    VALUES (?, ?)", min(friend_1_ID, friend_2_ID), max(friend_1_ID, friend_2_ID))

同样,对于检索也要这样做。当然,这可以在存储过程中完成。

类似这样的操作最初应该可以:


+1关于双重检查触发器的问题经常被忽略。您可能也需要对表进行分片(如果您准备5亿个条目),您将有一个算法来确定从哪个分片中选择数据。例如,拥有5亿行数据并将其拆分为500万行分区将极大地提高选择/插入的速度。我如何使用触发器来控制这一点,我以前从未使用过它们?@Dom:这值得提出自己的问题。。。您可能需要一个BEFORE INSERT触发器,因为您希望在插入数据之前对其进行验证。如果关系已经存在,它应该返回一个错误,这样您的应用程序就可以通知用户关系已经存在。避免重复问题的另一种方法就是始终在应用程序端对输入进行排序,以便将user_id检查约束可以帮助您强制执行该规则,尽管这显然只是非MySQL数据库上的一个选项。+1!!!!Frank well说:“即使你想做最好的准备,你也无法做到Facebook有两个完整的专用服务器和200多个memcached实例。”。更多的开发人员应该理解这一点。我认为您可以更进一步,说您以后需要重做模式。数据需要分片,您需要引入数据库从机等(并围绕这些点开发模式)。这并不简单,需要有资格的DBA提出一个适用于特定站点和负载的解决方案。@Kieran Allen:表分区实现各不相同,但是IIRC大多数不需要重大的数据模型更新。我不喜欢计划数据模型大修的想法——这应该是最后的选择
drop table if exists user_friends;
drop table if exists users;

create table users
(
user_id int unsigned not null auto_increment primary key,
username varchar(32) unique not null,
created_date datetime not null
)
engine=innodb;

delimiter #

create trigger users_before_ins_trig before insert on users
for each row
begin
 set new.created_date = now();
end#

delimiter ;

create table user_friends
(
user_id int unsigned not null,
friend_user_id int unsigned not null,
created_date datetime not null,
primary key (user_id, friend_user_id), -- note clustered composite PK
foreign key (user_id) references users(user_id),
foreign key (friend_user_id) references users(user_id)
)
engine=innodb;

delimiter #

create trigger user_friends_before_ins_trig before insert on user_friends
for each row
begin
 set new.created_date = now();
end#

delimiter ;


drop procedure if exists insert_user;

delimiter #

create procedure insert_user
(
in p_username varchar(32)
)
proc_main:begin

  insert into users (username) values (p_username);

end proc_main #

delimiter ;

drop procedure if exists insert_user_friend;

delimiter #

create procedure insert_user_friend
(
in p_user_id int unsigned,
in p_friend_user_id int unsigned
)
proc_main:begin

  if p_user_id = p_friend_user_id then
    leave proc_main;
  end if;

  insert into user_friends (user_id, friend_user_id) values (p_user_id, p_friend_user_id);

end proc_main #

delimiter ;

drop procedure if exists list_user_friends;

delimiter #

create procedure list_user_friends
(
in p_user_id int unsigned
)
proc_main:begin

  select
    u.*
  from
    user_friends uf
  inner join users u on uf.friend_user_id = u.user_id
  where
    uf.user_id = p_user_id
  order by
   u.username;

end proc_main #

delimiter ;

call insert_user('f00');
call insert_user('bar');
call insert_user('bish');
call insert_user('bash');
call insert_user('bosh');

select * from users;

call insert_user_friend(1,2);
call insert_user_friend(1,3);
call insert_user_friend(1,4);
call insert_user_friend(1,1); -- oops

call insert_user_friend(2,1);
call insert_user_friend(2,5);

select * from user_friends;

call list_user_friends(1);
call list_user_friends(2);

-- call these stored procs from your php !!