如何避免相互加好友时出现的数据库竞争插入好友关系?

2018-02-02 09:52:02 +08:00
 zjsxwc

写相互加好友时碰到的问题。

数据库好友关系( friend_relation )表目前是这样的:

字段:	    id,        account_id,    friend_account_id,       status
说明:这条好友关系 id,   用户 id,          朋友 id,         状态标记是否被删除	

于是 2 个用户相互添加为好友就会出现对应的 2 条好友关系 friend_relation (这两条好友关系的 account_id 与 friend_account_id 对调)

那么现在问题来了,如果 2 个用户同时接受对方发起的交友请求,会出现对应的 2 个处理交友的进程都认为双方都没加过好友,于是最后 2 个进程共同插入了 4 条好友关系,而实际上应该插入 2 条就够了。

加锁好像不行,因为在处理之前连被加锁的数据都没有,根本不能对数据行加锁。

加唯一 index,应该怎么加,业务上要允许重复多次加删同一个人的好友关系,如果对( account_id, friend_account_id, status )作为一个唯一 index,那么只能删一次这个好友,这样也不行。

我应该怎么解决这个问题?

========== 我现在只能先把插入好友关系这一步工作都放到一个队列里去干

5993 次点击
所在节点    程序员
35 条回复
sutra
2018-02-02 13:12:28 +08:00
似乎就我没有看懂问题?

为什么会出现 4 条记录?最多就 2 条记录呀。
vjnjc
2018-02-02 13:44:09 +08:00
@sutra
比如 a 发起添加 b 为好友请求,b 也发起添加 a 请求。
a 点接受好友(插入 2 条记录好友记录),b 也点接受好友(插入 2 条记录)。

所以在接受好友的业务里先判断再添加啊!
sutra
2018-02-02 13:57:47 +08:00
@vjnjc 哦,他的意思是,只要一个单向的接受操作,就变为双向的好友呀。那我懂了。

那就数据库建立唯一键索引,只管往里插入,插入失败就抓起异常看一眼。
barbery
2018-02-02 14:07:56 +08:00
如果要从 db 上处理,unique 的方式是比较好的。如果不从 db 上处理,可以考虑引入第三方的原子性操作的服务,例如 redis 或者队列等。
eslizn
2018-02-02 14:57:58 +08:00
@Clarencep 事务调整到序列化级别性能下降太厉害了
Eternallyc
2018-02-02 15:06:33 +08:00
比如 a 发起添加 b 为好友请求,b 也发起添加 a 请求。
a 点接受好友(插入 2 条记录好友记录),b 也点接受好友(插入 2 条记录)。

所以在接受好友的业务里先判断再添加啊!


赞同!

插入的时候判断下 根据用户 id 去关系表里查询 有记录并且 status 字段不是被删除状态 才插入,否则提示 已经是好友
Clarencep
2018-02-02 15:37:57 +08:00
@eslizn 可以只改单个会话( session )的隔离级别,添加好友前改成 SERIALIZABLE,添加后再改回去就行了。
chuanwu
2018-02-02 16:40:03 +08:00
额,难道就我一个人觉得表设计不合理么...
zjsxwc
2018-02-02 16:47:02 +08:00
@chuanwu

应该怎么设计好?
vincenttone
2018-02-02 17:18:58 +08:00
唯一索引 配合上 insert into on duplicate update 即可
picasso250
2018-02-02 21:01:25 +08:00
有趣的问题.

难点之所以会出现, 是因为 每个人的 request 会新增两条数据(主动去踩不该踩的坑).

解决方案: request 的时候,只会增加一条数据. 另一条数据扔给后台的队列(这个队列本身是单线程的)去做.

完美解决.
qile1
2018-02-03 06:40:50 +08:00
@vjnjc 你这个直接 a 加 b 为好友,插一条信息,此时 b 是 a 好友,但 a 不是 b 好友,或者可以理解为 b 不把 a 当好友,当 b 在添加 a 为好友时,插入一条数据,这样就不会产生冲突。
twotiger
2018-02-03 16:40:04 +08:00
@Eternallyc 没用的,并发的话,会同时写进去
eslizn
2018-02-04 16:50:52 +08:00
@Clarencep 替捉鸡。。。只要有一个会话开启了序列化,其他会话的事务操作都得等待这个会话结束。。。
Clarencep
2018-02-05 09:15:26 +08:00
@eslizn 额,居然是这样子的吗?那倒真不如锁表了。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/427902

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX