事务、事件(文件、时间、调度和执行)、复制、分片(范围、哈希)、简单的论坛系统分析
1.?事務
一個事務包含了多個命令,服務器在執行事務期間,不會改去執行其它客戶端的命令請求。
事務中的多個命令被一次性發送給服務器,而不是一條一條發送,這種方式被稱為流水線。
- 它可以減少客戶端與服務器之間的網絡通信次數從而提升性能。
Redis 最簡單的事務實現方式是使用 MULTI 和 EXEC 命令將事務操作包圍起來。
2. 事件
Redis 服務器是一個事件驅動程序。
2.1 文件事件
服務器通過套接字與客戶端或者其它服務器進行通信,文件事件就是對套接字操作的抽象。
Redis 基于 Reactor 模式開發了自己的網絡事件處理器,使用 I/O 多路復用程序來同時監聽多個套接字,并將到達的事件傳送給文件事件分派器,分派器會根據套接字產生的事件類型調用相應的事件處理器。
2.2?時間事件
服務器有一些操作需要在給定的時間點執行,時間事件是對這類定時操作的抽象。
時間事件又分為:
- 定時事件:是讓一段程序在指定的時間之內執行一次;
- 周期性事件:是讓一段程序每隔指定時間就執行一次。
Redis 將所有時間事件都放在一個無序鏈表中,通過遍歷整個鏈表查找出已到達的時間事件,并調用相應的事件處理器。
2.3 事件的調度與執行
服務器需要不斷監聽文件事件的套接字才能得到待處理的文件事件,但是不能一直監聽,否則時間事件無法在規定的時間內執行,因此監聽時間應該根據距離現在最近的時間事件來決定。
事件調度與執行由 aeProcessEvents 函數負責,偽代碼如下:
def aeProcessEvents():# 獲取到達時間離當前時間最接近的時間事件time_event = aeSearchNearestTimer()# 計算最接近的時間事件距離到達還有多少毫秒remaind_ms = time_event.when - unix_ts_now()# 如果事件已到達,那么 remaind_ms 的值可能為負數,將它設為 0if remaind_ms < 0:remaind_ms = 0# 根據 remaind_ms 的值,創建 timevaltimeval = create_timeval_with_ms(remaind_ms)# 阻塞并等待文件事件產生,最大阻塞時間由傳入的 timeval 決定aeApiPoll(timeval)# 處理所有已產生的文件事件procesFileEvents()# 處理所有已到達的時間事件processTimeEvents()將 aeProcessEvents 函數置于一個循環里面,加上初始化和清理函數,就構成了Redis 服務器的主函數,偽代碼如下:
def main():# 初始化服務器init_server()# 一直處理事件,直到服務器關閉為止while server_is_not_shutdown():aeProcessEvents()# 服務器關閉,執行清理操作clean_server()從事件處理的角度來看,服務器運行流程如下:
3.?復制
通過使用 slaveof host port 命令來讓一個服務器成為另一個服務器的從服務器。
一個從服務器只能有一個主服務器,并且不支持主主復制。
3.1 連接過程
3.2 主從鏈
隨著負載不斷上升,主服務器可能無法很快地更新所有從服務器,或者重新連接和重新同步從服務器將導致系統超載。
為了解決這個問題,可以創建一個中間層來分擔主服務器的復制工作。
中間層的服務器是最上層服務器的從服務器,又是最下層服務器的主服務器。
4. Sentinel
Sentinel(哨兵) 可以監聽主服務器,并在主服務器進入下線狀態時,自動從從服務器中選舉出新的主服務器。
5. 分片
分片是將數據劃分為多個部分的方法,可以將數據存儲到多臺機器里面,這種方法在解決某些問題時可以獲得線性級別的性能提升。
假設有 4 個 Reids 實例 R0,R1,R2,R3,還有很多表示用戶的鍵 user:1,user:2,... ,有不同的方式來選擇一個指定的鍵存儲在哪個實例中。
5.1 范圍分片
最簡單的方式是范圍分片,例如用戶 id 從 0~1000 的存儲到實例 R0 中,用戶id 從 1001~2000 的存儲到實例 R1 中,等等。
- 但是這樣需要維護一張映射范圍表,維護操作代價很高。
5.2 哈希分片
使用 CRC32 哈希函數將鍵轉換為一個數字,再對實例數量求模就能知道應該存儲的實例。
5.3 根據執行分片的位置,可以分為三種分片方式:
- 客戶端分片:客戶端使用一致性哈希等算法決定鍵應當分布到哪個節點。
- 代理分片:將客戶端請求發送到代理上,由代理轉發請求到正確的節點上。
- 服務器分片:Redis Cluster。
6. 一個簡單的論壇系統分析
該論壇系統功能如下:
- 可以發布文章;
- 可以對文章進行點贊;
- 在首頁可以按文章的發布時間或者文章的點贊數進行排序顯示。
6.1 文章信息
文章包括標題、作者、贊數等信息,在關系型數據庫中很容易構建一張表來存儲這些信息,在 Redis 中可以使用 HASH 來存儲每種信息以及其對應的值的映射。
Redis 沒有關系型數據庫中的表這一概念來將同種類型的數據存放在一起,而是使用命名空間的方式來實現這一功能。
- 鍵名的前面部分存儲命名空間,后面部分的內容存儲 ID
- 通常使用 : 來進行分隔。例如下面的 HASH 的鍵名為 article:92617,
- 其中 article 為命名空間,ID 為 92617。
6.2?點贊功能
當有用戶為一篇文章點贊時,除了要對該文章的 votes 字段進行加 1 操作,還必須記錄該用戶已經對該文章進行了點贊,防止用戶點贊次數超過 1。
可以建立文章的已投票用戶集合來進行記錄。
為了節約內存,規定一篇文章發布滿一周之后,就不能再對它進行投票,而文章的已投票集合也會被刪除,可以為文章的已投票集合設置一個一周的過期時間就能實現這個規定。
6.3?對文章進行排序
為了按發布時間和點贊數進行排序,可以建立一個文章發布時間的有序集合和一個文章點贊數的有序集合。
(下圖中的 score 就是這里所說的點贊數;下面所示的有序集合分值并不直接是時間和點贊數,而是根據時間和點贊數間接計算出來的)
總結
以上是生活随笔為你收集整理的事务、事件(文件、时间、调度和执行)、复制、分片(范围、哈希)、简单的论坛系统分析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 数据结构(字典,跳跃表)、使用场景(计数
- 下一篇: 磁盘臂调度算法