redis 事務,持久化,日誌,主從,VM

奮程式序猿發表於2017-01-18

 

redis目前對事務的支援比較簡單,只能保證一個客戶端連線發起事務中的命令可以連續執行,而中間不會插入其他客戶端的命令。

1.事務

一般情況下,redis接收到一個客戶端傳送的命令,立刻執行並返回結果。但是當連線發出multi命令時,此連線便進入事務上下文,redis把此連線傳送的命令儲存到一個列隊當中,當此連線傳送exec命令時,redis便開始按順序執行列隊中的所有命令,並將所有命令執行的結果大包一起返回給客戶端連線,然後此連線便結束事務。

例如:

例子中可以看出set 命令發出後並沒有立即執行,而是存放在訊息列隊中。呼叫exec命令,set命令才開始連續執行,最後返回執行結果。

如果取消事務,可以使用discard 命令取消一個事務例如:

 

discard命令的作用是清空事務列隊中的命令,並退出事務。

另外,redis只能保證事務的每個命令能夠連續執行,但是事務中如果有命令執行失敗,redis並不會回滾操作。

 2.持久化

redis 是基於記憶體的資料庫,記憶體資料庫有個嚴重的弊端:突然當機或者斷電時,內容的資料不會儲存。為了解決這個問題,redis提供兩種持久化方式:記憶體快照(snapshotting)和日誌追加(append-only file).

記憶體快照方式是將記憶體中的資料以快照的方式寫入二進位制檔案中,預設檔名為dump.rdb

redis每隔一段時間進行一次記憶體快照操作,客戶端使用save或者bgsave命令告訴redis需要做一次快照操作。save命令在主執行緒中儲存記憶體快照,redis由單執行緒處理所有請求,執行save命令肯可能阻塞其他客戶端請求,從而不能快速的相應請求,所以建議不要使用save命令。另外要注意,記憶體快照每次都把資料完整的寫入到硬碟,而不是隻寫入增量資料。所以如果資料量大,寫入操作比較頻繁,從而嚴重影響效能。

記憶體快照相關配置:

save  <seconds>  <changes>

上面的配置表示經過seconds秒或資料更改changes次進行一次快照操作。例如下面的配置

save 1000 1

表示 1000秒或者資料更改1次就進行快照。也可以設定多個方案

save 1000 1

save 300 39

save 19 1111

這樣,其中一個成立,redis就會進行快照。

如果使用 bgsave 進行快照   只需要直接傳送  bgsave 命令就可以了 

如果成功會返回

 

bgsave命令執行之後立即返回 OK ,然後 Redis fork 出一個新子程式,原來的 Redis 程式(父程式)繼續處理客戶端請求,而子程式則負責將資料儲存到磁碟,然後退出。

而save是直接呼叫主程式所以會引起阻塞.

日誌追加(aof)方式是把增加,修改資料的命令通過write函式追加到檔案尾部(預設是appendoly.aof)。redis重啟時讀取appendonly.aof檔案中的所有命令並且執行,從而把資料寫入內容中。

另外,作業系統核心的I/O介面可能存在快取,所以日誌追加方式不可能立即寫入檔案中,這樣就有可能丟失部分資料。幸運的是redis提供瞭解決的方法,通過修改配置檔案告訴redis應該在什麼時候使用fsync函式強制作業系統把快取寫入磁碟。有一下三種方法:

appendonly yes #啟動日誌追加持久化方式(yes|no)

#appendfsync always #每次收到增加或者修改命令就立即強制寫入磁碟

appendfsync everysec #每秒強制寫入磁碟一次

#appendfync no#是否寫入磁碟完全依賴作業系統

日誌追加方式有效降低資料丟失的風險,同時也帶來了另外一些問題,即持久化檔案不斷變大。例如呼叫 incr nums 命令100次,檔案就會儲存100條incr nums 命令,其中99條是多餘的,因為回覆資料只需要set nums 100

為了壓縮日誌檔案,redis 提供bgrewriteaof命令。當redis收到此命令,就是用類似記憶體快照方式將記憶體的資料以命令的方式儲存到臨時檔案中,最後替換原來的日誌檔案。

3.主從同步

主從同步可以防止主機壞掉導致網站不能正常執行,這種方法即把從機設定為主機即可。redis支援主從同步,而且配置很簡單。redis主從同步的有點如下:

master(主)可以有多個slave(從)

多個slave連線到同一個master,slave還可以連線其他slave形成圖形結構。

不會阻塞master。當一個或者多個slave與master初次同步資料時,master可以繼續處理客戶請求。相反,slave在初次同步資料時會阻塞而不能處理客戶端的請求(2.2以後不會阻塞)。

主從同步用來提高系統的伸縮性,比如多個slave專門使用者客戶端的讀請求。

在master伺服器上禁止資料持久化,只在slave伺服器上進行資料持久化。

 redis主從同步非常簡單,設定好slave(從)伺服器後,slave自動和master建立連線,傳送SYNC命令。無論是第一次同步建立的連線還是斷開後重新建立的連線,master都啟動一個後臺程式,將記憶體資料以快照的形式寫入檔案中,同時master主程式開始收集新的寫命令並且快取起來。master後臺程式完成記憶體快照後,把資料檔案發給slave,slave將檔案儲存到磁碟上,然後把資料載入到記憶體中。接著master把快取命令發給slave,後續master收到的寫命令都通過開始建立的連線傳送給slave,當master與slave斷開連線,slave自動重新建立連線。如果master同時收到多個slave發來的請求,其只啟動一個程式寫資料庫映象,然後傳送給所有slave。

redis主從同步過程分為兩個階段,第一階段如下:

1.slave伺服器主動連線到master伺服器

2.slave伺服器傳送sycn命令到master伺服器請求同步·

3.master伺服器備份資料庫到rdb檔案

4.master伺服器把rdb檔案傳輸給slave伺服器

5。slave伺服器清空資料庫資料,把rdb檔案資料匯入資料庫

完成第一個階段,接下來master伺服器把使用者所有更改資料的操作,通過命令的形似轉發給slave伺服器,slave伺服器只需要執行master伺服器傳送過來的命令就可以達到同步的效果。

相對於mysql來說,redis主從複製的配置很簡答,只需要在slave伺服器配置檔案下新增

slaveof   master伺服器ip:埠

例如:

slaveof 192.168.1.123:6379

4.虛擬記憶體

redis的資料儲存在記憶體中,可能出現實體記憶體不足的情況。實體記憶體不足時,redis會使用虛擬記憶體(VM)。

VM是redis2.0新加的功能,之前redis把資料庫中的所有資料放在記憶體中,隨著redis的不斷執行,使用記憶體會越來越大,最終導致記憶體不住。redis的VM與作業系統的VM相似,把很少訪問的vlaue儲存到磁碟中。與此同時,redis把value對應的key放在記憶體中,為了能夠讓redis快速定位到被被換出的value所在的磁碟位置,從而將其匯入的記憶體中。

作業系統也有虛擬記憶體功能,為什麼redis要重複‘製造輪子‘呢?主要原因有兩點:

1.作業系統的VM是基於頁的概念,比如linux系統中內個頁是4kb,而redis大多數物件遠小於4kb,一頁行可能有多個redis物件。另外redis的集合物件型別與list,set可能存放在多個頁上面。故redis自己實現可能達到控制換入的粒度。

2.redis將交換到磁碟的物件壓縮,儲存到磁碟的物件可以取出指標和物件元素資訊。一般壓縮後的物件比記憶體中物件小10倍,這樣redis的vm比作業系統的VM少做很多I/O操作。

配置redis的VM

#開啟VM

vm-enabled yes

#交換出來的value檔案儲存路徑

vm-swap-file /tmp/redis.swap

#redis使用最大記憶體上線,超過後開始使用交換空間  單位位元組

vm-max-memory  268435456

#設定每個頁面的大小為32位元組

vm-page-size 32

#最多在檔案使用多少頁面,swap檔案的大小等於vm-page-size * vm-page

vm-pages 134217728

#用於執行value物件換出換入的工作執行緒數。0表示不使用工作執行緒

vm-max-threads 4

redis的vm只把value交換到磁碟中,而key依然儲存在記憶體中,目的是讓開啟VM的redis和完全使用記憶體的redis效能基本保持一致。如果由於太多key而造成記憶體不住問題,redis的VM並不能解決。

和炒作系統一樣,redis也按照頁交換物件。一頁只能儲存一個物件,但是一個物件可以儲存在多頁中。

當redis使用的記憶體沒有超過設定的vm-max-memory之前,不把任何value交換到磁碟中;當超過最大記憶體限制後,redis根據一下演算法尋找一個物件交換到磁碟中:

swappability=age*log(size_in_memory);

其中age代表這個物件距離上一次被訪問的時間,size_in_memry是這個物件在記憶體中佔用的空間大小。redis採取的策略是把那些很少訪問,而且佔用記憶體又比較大的物件交換的齒盤空間中,但是第二個因素所佔的權重更低,所以在公式中取log值。因為交換大物件時,需要佔用更多的I/O和cpu資源。

應該更具自己的應用設定vm-page-size,設定太大浪費磁碟空間,設定太小照成swap檔案出現過多碎片。

vm-max-threads 表示用於交換任務的工作執行緒數量,建議不要將vm-max-threads設定為0,設定為0時交換過程在主執行緒進行,從而阻塞其他使用者。但也不是設定越大越好,因為太多的工作執行緒導致作業系統使用更多的時間來切換執行緒,從而降低效率。推薦吧vm-max-threads設定為伺服器的cpu核心數。

為了理解VM子系統如何工作,需要了解物件在swap檔案中如何儲存。

swap檔案中採用rdb檔案的儲存格式。swap檔案被分割成固定數量的頁,每頁佔用指定的數量的位元組空間。在redis.conf中更具自己業務需求配置一下兩個引數:

vm-page-size 設定每頁的大小,預設值為32.

vm-pages設定能夠使用的頁數,預設值為134217728

redis在記憶體中儲存一個bitmap以對映這些頁是否被佔用,每bit代表一個對應磁碟空間的頁是否被佔用。記憶體中儲存這樣一份對映表極大的增強了redis效能,同時,對記憶體的使用又非常少。

在持久化備份的時候,也會備份swap 中的資料只不過在備份時swap變為只讀了,父程式和後臺主程式只能訪問swap。而父程式不能將value換入。

當一個value 交換到磁碟時,value對應的redis object 將被VM Pointer,VM pointer儲存value在磁碟的資訊。

交換過程:

從記憶體交換到swap中:

第一步。計算儲存這個物件需要佔用swap檔案中多少頁

第二步:在swap中尋找一段連續的空白頁空間儲存這個物件

第三步:把物件寫入swap

從swap中交換到記憶體中

從swap檔案交換到記憶體比較簡單,因為在VM Pointer 中已記錄物件在swap檔案的頁起始地址和佔用頁數,只要更具VM Pointer 把磁碟的物件換入記憶體即可。

 

相關文章