前言
點(diǎn)贊其實(shí)是一個(gè)很有意思的功能。基本的設(shè)計(jì)思路有大致兩種, 一種自然是用mysql等
數(shù)據(jù)庫(kù)直接落地存儲(chǔ), 另外一種就是利用點(diǎn)贊的業(yè)務(wù)特征來扔到redis(或memcache)中, 然后離線刷回mysql等。
直接寫入Mysql
直接寫入Mysql是最簡(jiǎn)單的做法。
做兩個(gè)表即可,
1、post_like
記錄文章被贊的次數(shù),已有多少人贊過這種數(shù)據(jù)就可以直接從表中查到;
2、user_like_post
記錄用戶贊過了哪些文章, 當(dāng)打開文章列表時(shí),顯示的有沒有贊過的數(shù)據(jù)就在這里面;
缺點(diǎn)
1、數(shù)據(jù)庫(kù)讀寫壓力大
熱門文章會(huì)有很多用戶點(diǎn)贊,甚至是短時(shí)間內(nèi)被大量點(diǎn)贊, 直接操作數(shù)據(jù)庫(kù)從長(zhǎng)久來看不是很理想的做法。
redis存儲(chǔ)隨后批量刷回?cái)?shù)據(jù)庫(kù)
redis主要的特點(diǎn)就是快, 畢竟主要數(shù)據(jù)都在內(nèi)存嘛;
另外為啥我選擇redis而不是memcache的主要原因在于redis支持更多的數(shù)據(jù)類型, 例如hash, set, zset等。
下面具體的會(huì)用到這幾個(gè)類型。
優(yōu)點(diǎn)
1、性能高
2、緩解數(shù)據(jù)庫(kù)讀寫壓力
其實(shí)我更多的在于緩解寫壓力, 真的讀壓力, 通過mysql主從甚至通過加入redis對(duì)熱點(diǎn)數(shù)據(jù)做緩存都可以解決,
寫壓力對(duì)于前面的方案確實(shí)是不大好使。
缺點(diǎn)
1、開發(fā)復(fù)雜
這個(gè)比直接寫mysql的方案要復(fù)雜很多, 需要考慮的地方也很多;
2、不能保證數(shù)據(jù)安全性
redis掛掉的時(shí)候會(huì)丟失數(shù)據(jù), 同時(shí)不及時(shí)同步redis中的數(shù)據(jù), 可能會(huì)在redis內(nèi)存置換的時(shí)候被淘汰掉;
不過對(duì)于我們點(diǎn)贊而已, 稍微丟失一點(diǎn)數(shù)據(jù)問題不大;
具體設(shè)計(jì)
Mysql設(shè)計(jì)
這一塊和寫入寫mysql是一樣的,畢竟是要落地存儲(chǔ)的。
所以還是同樣的需要post_like
, user_like_post
這兩表存儲(chǔ)文章被點(diǎn)贊的個(gè)數(shù)(等統(tǒng)計(jì)), 用戶對(duì)那些文章點(diǎn)了贊(取消贊)。
這兩表分別通過post_id
, user_id
進(jìn)行關(guān)聯(lián)。
redis設(shè)計(jì)部分:
post_set
在redis中弄一個(gè)set存放所有被點(diǎn)贊的文章
post_user_like_set_{$post_id}
對(duì)每個(gè)post以post_id作為key, 搞一個(gè)set存放所有對(duì)該post點(diǎn)贊的用戶;
post_user_like_{$post_id}_{$user_id}
將每個(gè)用戶對(duì)每個(gè)post的點(diǎn)贊情況放到一個(gè)hash里面去, hash的字段就
隨意跟進(jìn)需求來處理就行了。
為啥用hash
只所以用hash是因?yàn)橥耆梢杂胔ash來存儲(chǔ)一個(gè)點(diǎn)贊的對(duì)象, 對(duì)應(yīng)數(shù)據(jù)庫(kù)的一行記錄。
當(dāng)然有同學(xué)會(huì)說用key, value也可以, 將所有的數(shù)據(jù)序列化(json_encode
等)
后全部放到value里面去。 反復(fù)序列化也是一個(gè)很大的開銷不是, hash可以很
方便的修改某個(gè)字段, 而序列化和反序列化的操作。
post_{$post_id}_counter
對(duì)每個(gè)post維護(hù)一個(gè)計(jì)數(shù)器, 用來記錄當(dāng)前在redis中的點(diǎn)贊數(shù),
這里我們只用counter記錄尚未同步到mysql中的點(diǎn)贊數(shù)(可以為負(fù)), 每次
刷回mysql中時(shí)將counter中的數(shù)據(jù)和數(shù)據(jù)庫(kù)已有的贊數(shù)相加即可。
用戶點(diǎn)贊/取消贊
獲取user_id
, post_id
, 查詢?cè)撚脩羰欠褚呀?jīng)點(diǎn)過贊, 已點(diǎn)過則不允許再次點(diǎn)贊,
或者設(shè)計(jì)為前端允許用戶點(diǎn), 只是后臺(tái)不重復(fù)計(jì)算;
這里需要注意的是用戶點(diǎn)贊的記錄可能在數(shù)據(jù)庫(kù)中, 也可能在緩存中, 所以查詢的時(shí)候
緩存和數(shù)據(jù)庫(kù)都要查詢, 緩存沒有再查詢數(shù)據(jù)庫(kù)。
將用戶的點(diǎn)贊/取消贊的情況記錄在redis中, 具體為:
1、寫入post_set
將post_id
寫入post_set
2、寫入post_user_like_set_{$post_id}
將user_id
寫入post_user_like_set_{$post_id}
3、寫入post_user_like_{$post_id}_{$user_id}
將用戶點(diǎn)贊數(shù)據(jù), 例如贊狀態(tài), post_id, user_id, ctime(操作時(shí)間), mtime(修改時(shí)間)寫入post_user_like_{$post_id}_{$user_id}
中
4、更新post_{$post_id}_counter
更新post_{$post_id}_counter
, 這里的更新稍晚復(fù)雜一點(diǎn), 需要和前面一樣先獲取當(dāng)前用戶是否對(duì)這個(gè)post點(diǎn)過贊
如果點(diǎn)過, 并且本次是取消贊, counter減一, 如果沒點(diǎn)過, 本次是點(diǎn)贊, counter加一。
如果原來是取消贊的情況, 本次是點(diǎn)贊, counter加一。
同步刷回?cái)?shù)據(jù)庫(kù)
循環(huán)從post_set
中pop出來一個(gè)post_id
至到空
根據(jù){$post_id}
, 每次從post_user_like_set_{$post_id}
中pop出來一個(gè)user_id
直到空
根據(jù)post_id
, user_id
, 直接獲取對(duì)應(yīng)的hash表的內(nèi)容(post_user_like_{$post_id}_{$user_id}
將hash表中的數(shù)據(jù)寫入user_like_post
表中
將post_{$post_id}_counter
中的數(shù)據(jù)和post_like
中的數(shù)據(jù)相加, 將結(jié)果寫入到post_like
表中
頁(yè)面展示
1、查詢用戶點(diǎn)贊情況
前面已經(jīng)說過, 需要同時(shí)查詢r(jià)edis和mysql
2、查詢post點(diǎn)贊統(tǒng)計(jì)
同樣需要查詢r(jià)edis中的post_{$post_id}_counter
和mysql的post_like
表, 并將兩者相加
得到的結(jié)果才是正確的結(jié)果
總結(jié)
解決了mysql讀寫的問題
但沒有針對(duì)用戶量較大的場(chǎng)景考慮分表的設(shè)計(jì), 可以考慮針對(duì)user_id或者post_id進(jìn)行分表
好了,以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作能帶來一定的幫助,如果有疑問大家可以留言交流,謝謝大家對(duì)腳本之家的支持。
您可能感興趣的文章:- SpringBoot+Vue+Redis實(shí)現(xiàn)單點(diǎn)登錄(一處登錄另一處退出登錄)
- SpringBoot Redis配置Fastjson進(jìn)行序列化和反序列化實(shí)現(xiàn)
- SpringBoot集成Redisson實(shí)現(xiàn)分布式鎖的方法示例
- springboot整合redis集群過程解析
- springboot +redis 實(shí)現(xiàn)點(diǎn)贊、瀏覽、收藏、評(píng)論等數(shù)量的增減操作