高并发系统设计 --基于MySQL构建评论系统
如何用MySQL來實現評論系統
為什么我不用mongodb?
架構設計
使用MySQL進行存儲的話,就必須要用到Redis來做緩存,后臺admin需要接通ES來進行查詢,comment-service通過異步來進行寫Redis和MySQL評論數據,MySQL和ES通過Canal進行binlog同步。
緩存模式
首先我們需要預讀,我們讀第一頁的時候,也需要把第二頁的內容加載出來。讀第二頁的時候,我們預先讀第三頁,這樣可以避免大量的cache miss。
但是這里有一個致命的問題就是:當緩存抖動的時候,會觸發大量的cache rebuild,因為我們使用了預加載,容易造成OOM(內存溢出)。因此我們需要使用消息隊列來進行邏輯異步化,對于當前請求,只返回MySQL中的部分數據即可。
寫的邏輯
至于寫的操作,我們要穿透到存儲層,因此最好使用消息隊列異步削峰。例如我的評論發布出去了,用戶過100ms才看到評論,這是無所謂的。
存儲設計
comment_subject表
| obj_id | int | 對象id |
| obj_type | int | 對象類型 |
| member_id | int | 作者id |
| count | int | 評論總數 |
| root_count | int | 跟評總數 |
| all_count | int | 評論+回復總數 |
| state | int | 狀態 0:正常 1:隱藏 |
| attrs | int | 屬性 0:置頂 1:不置頂 |
| create_time+update_time | datatime | 創建時間,修改時間 |
obj_id+obj_type把評論系統設計成中臺。 一般是指搭建一個靈活快速應對變化的架構,快速實現前端提的需求,避免重復建設,達到提高工作效率目的。
obj_id+obj_type形成了一個業務鍵,比如微博,發帖,發視頻,你可以發視頻,你也可以發文章,評論系統設計成中臺。
comment_index
| obj_id | int | 對象id |
| obj_type | int | 對象類型 |
| member_id | int | 發表者id |
| root | int | 根評論id,不為0是回復評論 |
| parent | int | 父評論id,為0是root評論 |
| floor | int | 評論樓層 |
| count | int | 評論總數 |
| root_count | int | 根評論總數 |
| like | int | 點贊數 |
| hate | int | 點踩數 |
| state | int | 狀態,0:正常;1:隱藏 |
| attrs | int | 屬性 |
| create_time | datetime | 創建時間 |
| update_time | datetime | 修改時間 |
parent:父評論id,其實就是記錄是否是回復評論。
comment_content表:
| at_member_ids | varchar | 對象id |
| ip | int | 對象類型 |
| platform | int | 發表者id |
| device | varchar | 跟評論id,不為0是回復評論 |
| message | varchar | 評論內容 |
| meta | varchar | 評論元數據:背景,字體 |
| create_time | datetime | 創建時間 |
| update_time | datetime | 修改時間 |
index是索引表,content是內容表。
數據寫入:事務更新comment_subject,comment_index,comment_content三張表,其中content是非強制性需要一致性考慮的。因此可以先寫入content,之后事務更新其他表。即便content更新成功,后續失敗僅僅存在一條ghost數據。
數據讀取:基于obj_id + obj_type在comment_index表找到評論列表,where root=0 order by floor。之后根據comment_index的id字段撈出comment_content的評論內容。對于二級的子樓層,where parent/root in(id...)。
為什么要把index和content分成兩個表
comment_index:評論樓層的索引表,實際并不包含內容。comment_content:評論內容的表,包含評論的具體內容。其中comment_index的id字段和comment_content是1對1的關系,這里面包含了幾種設計思想。
- 表都有主鍵,comment_content沒有id,是為了減少一次二級索引查找,直接基于主鍵檢索,同時comment_id在寫入要盡可能的順序自增。
- 索引,內容分離,方便mysql_datapage緩存更多的row,如果和content耦合,會導致更大的IO。長遠來看content信息可以直接使用KV storage存儲。
緩存設計
comment_subject_cache【string】
| value | int | subject marshal string |
| expire | duration | 24h |
comment_index_cache【sorted set】
| member | int | comment_id:評論id |
| score | double | 樓層號,回復數量,排序得分 |
| expire | duration | 8h |
comment_content_cache
| value | int | content |
| expire | duration | 24h |
comment_subject_cache:對應主題的緩存,value使用protobuf序列化的方式存入,這樣調用rpc的時候速度可以更塊一點。
comment_index_cache:使用redis sorted set進行索引的緩存,索引即數據的組織順序,而非數據內容。通過預加載少量數據,通過增量加載的方式逐漸預熱填充緩存,而redis sorted set skiplist的實現可以做到O(logN) + O(M)的時間復雜度,效率很高
sorted set是要增量追加的,因此必須判定key存在,才能zadd
comment_content_cache:對應評論內容數據,使用protobuf序列化的方式存入。
增量加載(目標表僅更新源數據表中變化的內容)+lazy加載(延遲加載,種將資源標識為非阻塞(非關鍵)資源并僅在需要時加載它們的策略)
可用性設計
其實就是各種緩存問題,消息隊列消息等問題。
如果這個key是熱點的key的話可以使用本地緩存+分布式緩存。
那么如何統計是否是熱點呢?
這里,我們就移步到我們的下一篇文章了,謝謝大家。
總結
以上是生活随笔為你收集整理的高并发系统设计 --基于MySQL构建评论系统的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 〖Python 数据库开发实战 - Py
- 下一篇: kon-boot启动盘测试