點贊作為一個高頻率的操作,如果每次操作都讀寫資料庫會增加資料庫的壓力,所以採用快取+定時任務來實現。點贊資料是在redis中快取半小時,同時定時任務是每隔5分鐘執行一次,做持久化儲存,這裡的快取時間和任務執行時間可根據專案情況而定。
優點
1.降低對資料庫的影響 2.提高點讚的效率
缺點
1.如果任務掛了,會丟失點贊資料 2.持久化儲存不是實時的
時序圖
資料庫設計
create table user_like(
id bigint(20) unsigned not null auto_increment comment 'id',
user_id bigint(20) not null default 0 comment '使用者id',
liked_id varchar(21) not null default '' comment '被點讚的id',
liked_status int(11) not null default 0 comment '點贊狀態,0未點贊,1已點贊',
liked_type int(11) not null default 0 comment '點讚的型別',
liked_time timestamp not null default '0000-00-00 00:00:00.000000' comment '點贊時間',
is_delete tinyint not null default '0' comment '是否邏輯刪除',
create_time timestamp not null default CURRENT_TIMESTAMP comment '建立時間',
update_time timestamp not null default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP comment '更新時間',
primary key (id),
unique uniq_user_id_liked_id_type(user_id,liked_id,liked_type),
key idx_liked_id (liked_id),
key idx_create_time (create_time),
key idx_update_time (update_time)
)ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 ROW_FORMAT=COMPACT COMMENT='使用者點贊表';
create table user_like_stat(
id bigint(20) unsigned not null auto_increment comment 'id',
liked_id varchar(21) not null default '' comment '被點贊id',
liked_count int(11) not null default 0 comment '點贊總數量',
is_delete tinyint not null default '0' comment '是否邏輯刪除',
create_time timestamp not null default CURRENT_TIMESTAMP comment '建立時間',
update_time timestamp not null default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP comment '更新時間',
primary key (id),
unique uniq_info_num(liked_id),
key idx_create_time (create_time),
key idx_update_time (update_time)
)ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 ROW_FORMAT=COMPACT COMMENT='點贊統計表';
複製程式碼
實現步驟
1.設計快取資料格式
整個點贊模組主要採用快取來完成,所以要選擇合適資料結構,我選擇hash資料結構來實現,應為它可以新增、獲取、移除單個鍵值對,並且可以獲取所有鍵值對。主要快取兩種資料,一種是使用者的點贊狀態,一種是被點贊id的點贊數量。這兩種資料分別用兩個key儲存,這兩個key中都是儲存的多個鍵值對。鍵值對格式如下:
使用者的點贊狀態key-value------>{"被點讚的id::使用者id" :"點贊狀態::點贊時間::點贊型別"}
被點贊id的點贊數量key-value------>{"被點贊id" : "點贊數量"}
2.大key拆分
點讚的資料量比較大的情況下,上面的設計會造成單個key儲存的value很大,由於redis是單執行緒執行,如果一次操作的value很大,會對整個redis的響應時間有影響,所以我們這裡在將上面的兩個key做拆分。固定key的數量,每次存取時都先在本地計算出落在了哪個key上,這個操作就類似於redis分割槽、分片。有利於降低單次操作的壓力,將壓力平分到多個key上。
//點贊狀態key拆分
newHashKey = hashKey +"_"+ (userId% 5);
hset (newHashKey, field, value) ;
hget(newHashKey, field)
//點贊數量key拆分
newHashKey = hashKey +"_"+ Math.abs((hash*(被點贊id)) % 5);
hset (newHashKey, field, value) ;
hget(newHashKey, field)
複製程式碼
3.程式碼實現
以下值擷取了部分程式碼,提供思路。
1.點贊狀態列舉
2.點贊型別列舉 3.使用者點贊類 4.點贊介面實現 這裡使用策略設計模式來實現,方便以後的擴充套件,對這個設計模式不瞭解的請點選 5.邏輯 取消點贊和這個介面相同,只需要替換下點贊狀態和redis增量 6.定時任務 定時任務採用Azkaban任務排程系統,每個5分種執行一次任務,把點贊資料從redis快取中取出做持久化到mysql。4.改進點
現在的讀取都是用的一個key,接下來可以優化為把key做讀寫分離。寫入和讀取分別用不同的key,這樣做可以減少資源的浪費,要不每次跑定時任務都會把已經持久化並且快取未失效的資料拿出來做一遍查詢。
以上就是點讚的一個實現思路,大家有什麼更好的方法或者改進的點,歡迎提出來。