前言
本文介紹 MySQL45 講中提到的一個用戶關注的案例,并記錄下可行的處理方案,
業務背景
業務上有這樣的需求,A、B兩個用戶,如果互相關注,則成為好友,存在兩個表,
關系(relation)表:
CREATE TABLE `relation` (
`id` int NOT NULL AUTO_INCREMENT,
`user_id` int NOT NULL,
`liker_id` int NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_user_id_liker_id` (`user_id`,`liker_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
好友(friend)表:
CREATE TABLE `friend` (
`id` int NOT NULL AUTO_INCREMENT,
`friend1_id` int NOT NULL,
`friend2_id` int NOT NULL,
PRIMARY KEY (`id`),
KEY `uk_friend` (`friend1_id`,`friend2_id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
以A關注B為例:
第一步,先查詢對方有沒有關注自己(B有沒有關注A)
select * from relation where user_id = B and liker_id = A;
如果有,則成為好友
insert into friend;
沒有,則只是單向關注關系
insert into like;
現在存在的問題是,如果 A 和 B 同時關注對方,在第一步判斷對方沒有關注自己,則只記錄單向關注關系,然而實際上應該記錄成好友的,
那通過對記錄上鎖可以解決么?不行的,因為記錄不存在,行鎖無法生效,
解決方案
將兩條記錄合并到一條,這樣就可以充分利用唯一索引,此外,再新增一個欄位來記錄狀態,
關系表新增一個 relation_ship 欄位,tinyint 型別,當 A 關注 B 且 A < B 時,
INSERT INTO (user_id, liker_id, relation_ship) values(A, B, 1);
當 A 關注 B 且 B < A 時,
INSERT INTO (user_id, liker_id, relation_ship) values(B, A, 2);
當 relation_ship = 3,表示 A 和 B 互相關注,
執行邏輯如下
begin;
# 1. 插入記錄或者更新 relation_ship
INSERT INTO relation(user_id, liker_id, relation_ship) values(A, B, 1) on duplicate key update relation_ship = (relation_ship | 1);
# 2. 查詢 relation_ship
SELECT relation_ship FROM relation WHERE user_id = A AND liker_id = B;
# 3. 在業務邏輯中,如果 relation_ship = 3,則創建好友關系
INSERT INTO friend(friend1_id, friend2_id) values(A, B);
commit;
當兩個用戶同時關注對方時,對先執行到達的事務會成功插入關系記錄,得到的 relation_ship 值為 1 或 2,不創建好友關系;而后到來的事務,因為重復不插入關系記錄,而更新 relation_ship = 1 | 2 或者 2 | 1,總之結果都是 3,于是它會創建好友關系,
這樣就解決了業務上存在的問題,主要借助了唯一索引來實作,
參考
- [1] 答疑文章(一):日志和索引相關問題
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/300941.html
標籤:其他
