我是 Redis, 當程式設計師用指令 ./redis-server /path/to/redis.conf
把我啟動的時候,第一個引數必須是redis.conf
檔案的路徑。
這個檔案很重要,就好像是你們的 DNA,它能控制我的執行情況,不同的設定會有不同的特性和人生,它掌握我的人生命運,控制著我如何完成高可用、高效能。合理的設定能讓我更快、更省記憶體,並行揮我最大的優勢讓我更安全執行。
以下這些設定大家必知必會,需要大家掌握每個設定背後的技術原理,學會融合貫通並在生產中正確設定,解決問題。避免出現技術懸浮,原理說的叭叭叭,設定像個大傻瓜。
本文組態檔版本是 Redis 7.0。
這些是我的常規設定,每個 Redis 啟動必備引數,你一定要掌握,涉及到網路、模組外掛、執行模式、紀錄檔等。
這個設定可以載入模組外掛增強我的功能,常見的模組有 RedisSearch、RedisBloom 等。關於模組載入可以參考【5.6 布隆過濾器原理與實戰】章節整合布隆過濾器便是通過以下設定實現載入布隆過濾器外掛。
loadmodule /opt/app/RedisBloom-2.2.14/redisbloom.so
這部分都是與網路相關的設定,很重要滴,設定不當將會有安全和效能問題。
bind
用於繫結本機的網路介面(網路卡),注意是本機。
每臺機器可能有多個網路卡,每個網路卡都有一個 IP 地址。設定了 bind,則表示我只允許來自本機指定網路卡的 Redis 請求。
MySQL:「bind 是用於限制存取你的機器 IP 麼?」
非也,注意,這個設定指的並不是只有 bind 指定的 IP 地址的計算機才能存取我。如果想限制指定的主機連線我,只能通過防火牆來控制,bind 引數不也能起到這個作用。
舉個例子:如果我所在的伺服器有兩個網路卡,每個網路卡有一個 IP 地址, IP1,IP2。
設定 bind IP1
,則表示只能通過這個網路卡地址來的網路請求存取我,也可以使用空格分割繫結多個網路卡 IP。
我的預設設定是bind 127.0.0.1 -::1
表示繫結本地迴環地址 IPv4 和 Ipv6。- 表示當 ip 不存在也能啟動成功。
MySQL:網路世界很危險滴,你如何保證安全?
預設開啟保護模式,如果沒有設定密碼或者沒有 bind 設定,我只允許在本機連線我,其它機器無法連線。
如果想讓其它機器連線我,有以下三種方式。
protected-mode no
(不建議,地球很危險滴,防人之心不可無)。protected-mode yes
,設定 bind 繫結本機的 IP。protected-mode yes
,除了設定 bind
以外,還可以通過 requirepass magebyte
設定密碼為 magebyte
, 讓其他機器的使用者端能使用密碼存取我。bind、protected-mode、requirepass 之間的關係
如果引數設定為bind 127.0.0.1 -::1
,不管 protected-mode
是否開啟,只能本機用 127.0.0.1 連線,其他外機無法連線。
在生產環境中,為了安全,不要關閉 protected-mode,並設定 requirepass
引數設定密碼和 bind 繫結機器的網路卡 IP。
用於指定我監聽的使用者端 socket 埠號,預設 6379。設定為 0 則不會監聽 TCP 連線,我想沒人設定為 0 吧。
用於在 Linux
系統中控制 TCP 三次握手已完成連線佇列(完成三次握手後)的長度,如果已完成連線佇列已經滿則無法放入,使用者端會報read timeout
或者connection reset by peer
的錯。
MySQL:「在高並行系統中這玩意需要調大些吧?」
是的,我的預設設定是 511,這個設定的值不能大於 Linux 系統定義的 /proc/sys/net/core/somaxconn 值,Linux 預設的是 128。
所以我在啟動的時候你會看到這樣的警告:WARNING: The TCP backlog setting of 511 cannot be enforced because kern.ipc.somaxconn is set to the lower value of 128.
當系統並行量大並且使用者端速度緩慢的時候,在高並行系統中,需要設定一個較高的值來避免使用者端連線速度慢的問題。
需要分別調整 Linux 和 Redis 的設定。
建議修改為 2048 或者更大,Linux 則在 /etc/sysctl.conf
中新增net.core.somaxconn = 2048
設定,並且在終端執行 sysctl -p
即可。
碼哥使用 macOS 系統,使用 sudo sysctl -w kern.ipc.somaxconn=2048
即可。
timeout 60
單位是秒,如果在 timout 時間內使用者端跟我沒有資料互動(使用者端不再向我傳送任何資料),我將關閉該使用者端連線。
注意事項
server.maxidletime
tcp-keepalive 300
單位是秒,官方建議值是 300。這是一個很有用的設定,實現 TCP 連線複用。
用途
用於使用者端與伺服器端的長連線,如果設定為非 0,則使用 SO_KEEPALIVE
週期性傳送 ACK 給使用者端,俗話就是用來定時向用戶端傳送 tcp_ack 包來探測使用者端是否存活,並保持該連線。不用每次請求都建立 TCP 連線,畢竟建立連線是比較慢的。
這些都是我的常規設定,比較通用,你必須瞭解。你可以把這些設定寫到一個特有檔案中,其他節點可以使用 include /path/to/other.conf
設定來載入並複用該組態檔的設定。
設定daemonize yes
表示使用守護行程的模式執行,預設情況下我是以非守護執行緒的模式執行(daemonize no),開啟守護行程模式,會生成一個 .pid
檔案儲存程序號。
你也可以設定 pidfile /var/run/redis_6379.pid
引數來指定檔案的生成目錄,當關閉服務的時候我會自動刪除該檔案。
指定我在執行時的紀錄檔記錄級別。預設是 loglevel notice
。有以下幾個選項可以設定。
指定紀錄檔檔案目錄,預設是 logfile ""
,表示只在標準控制檯輸出。
需要注意的是,如果使用標準控制檯輸出,並且使用守護行程的模式執行,紀錄檔會傳送到 /dev/null。
設定資料庫數量,我的預設設定是 databases 16
。預設的資料庫是 DB 0
,使用叢集模式的時候, database 只有一個,就是 DB 0
。
MySQL:「要怎麼開啟 RDB 記憶體快照檔案實現持久化呢?」
RDB 快照持久化相關的設定,必須掌握,合理設定能我實現宕機快速恢復實現高可用。
使用 save <seconds> <changes>
開啟持久化,比如 save 60 100
表示 60 秒內,至少執行了 100 個寫操作,則執行 RDB 記憶體快照儲存。
不關心是否丟失資料,你也可以通過設定 save ""
來禁用 RDB 快照儲存,讓我效能起飛,衝出三界外。
預設情況的我會按照如下規則來儲存 RDB 記憶體快照。
也可以通過 save 3600 1 300 100 60 10000
設定來顯示設定。
MySQL:「bgsave 失敗的話,停止接收寫請求要怎麼設定?」
預設設定為 stop-writes-on-bgsave-error yes
,它的作用是如果 RDB 記憶體快照持久化開啟並且最後一次 bgsave
失敗的話就停止接收寫請求。
我通過這種強硬的方式來告知程式設計師資料持久化不正常了,否則可能沒人知道 RDB 快照持久化出問題了。
當 bgsave
後臺程序能正常工作,我會自動允許寫請求。如果你對此已經有相關的監控,即使磁碟出問題(磁碟空間不足、沒有許可權等)的情況下依舊處理寫請求,那麼設定成 no
即可。
MySQL:「RDB 記憶體快照檔案比較大,可以壓縮麼?」
我的預設設定是 rdbcompression yes
,意味著對 RDB 記憶體快照檔案中的 String 物件使用 LZF 演演算法做壓縮。這個非常有用,能大大減少檔案大小,受益匪淺呀,建議你開啟。
如果你不想損失因為壓縮 RDB 記憶體快照檔案的 CPU 資源,那就設定成 no
,帶來的後果就是檔案比較大,傳輸佔用更大的頻寬(要三思啊,老夥計)。
預設設定是 rdbchecksum yes
,從 5.0 版本開始,RDB 檔案末尾會寫入一個 CRC64 檢驗碼,能起到一定的糾錯作用,但是要丟失大約 10% 的效能損失,你可以設定成功 no
關閉這個功能來獲得更快的效能。
關閉了這個功能, RDB 記憶體快照檔案的校驗就是 0 ,程式碼會自動跳過檢查。
推薦你關閉,讓我快到令人髮指。
你還可以通過 dbfilename
引數來指定 RDB 記憶體快照檔名,預設是 dbfilename dump.rdb
。
預設設定是 rdb-del-sync-files no
,主從進行全量同步時,通過傳輸 RDB 記憶體快照檔案實現,沒有開啟 RDB 持久化的範例在同步完成後會刪除該檔案,通常情況下保持預設即可。
我的工作目錄,注意這是目錄而不是檔案, 預設設定是dir ./
。比如存放 RDB 記憶體快照檔案、AOF 檔案。
這部分設定很重要,涉及到主從複製的方方面面,是高可用的基石,重點對待啊夥計們。
主從複製,使用replicaof <masterip> <masterport>
設定將當前範例成為其他 Redis 服務的從節點。
有以下幾點需要注意。
backlog size
,保證這個快取區能完整儲存斷連期間 Master 接受寫請求的資料,防止出現全量複製,具體設定後面會細說。如果當前節點是 slave,且 master 節點設定了 requirepass
引數設定了密碼,那麼 slave 節點必須使用該引數設定為 master 的密碼,否則 master 節點將拒絕該 slave 節點的請求。
設定方式為 masterauth <master-password>
。
在 6.0 以上版本,如果使用了我的 ACL 安全功能,只設定 masterauth
還不夠。因為預設使用者不能執行 PSYNC
命令或者主從複製所需要的其他命令。
這時候,最好設定一個專門用於主從複製的特殊使用者,設定方式為 masteruser <username>
。
MySQL:「當 slave 節點與 master 失去連線,導致主從同步失敗的時候,還能處理使用者端請求麼?」
slave 節點可以有以下兩種行為來決定是否處理使用者端請求。
yes
,slave 節點可以繼續處理使用者端請求,但是資料可能是舊的,因為新的沒同步過來。也可能是空的,如果是第一次同步的話。no
,slave 節點將返回錯誤 MASTERDOWN Link with MASTER is down and replica-serve-stale-data is set to no
給使用者端。但是以下的指令還是可以執行:INFO, REPLICAOF, AUTH, SHUTDOWN, REPLCONF, ROLE, CONFIG, SUBSCRIBE,UNSUBSCRIBE, PSUBSCRIBE, PUNSUBSCRIBE, PUBLISH, PUBSUB, COMMAND, POST,HOST and LATENCY
。我的預設設定是 replica-serve-stale-data yes
。
這個設定用於控制 slave 範例能否接收寫指令,在 2.6 版本後預設設定為 yes
,表示 slave 節點只處理讀請求,如果為 no
則可讀可寫。
我建議保持預設設定,讓 slave 節點只作為副本實現高可用。想要提高寫效能,使用叢集模式橫向拓展更好。
主從複製過程中,新加入的 slave 節點和 slave 節點重連後無法進行增量同步,需要進行一次全量同步,master 節點會生成 RDB 記憶體快照檔案傳輸給 slave 節點。
所以這個設定是用於控制傳輸方式的,傳輸方式有兩種。
使用磁碟備份的方式,master 儲存在磁碟的 RDB 記憶體快照檔案可以讓多個 slave 複用。
使用無盤備份的話,當 RDB 記憶體快照檔案傳輸開始,如果當前有多個slave
節點與 master 建立連線,我會使用並行傳輸的方式將 RDB 內容傳輸給多個節點。
預設的設定是 repl-diskless-sync yes
,表示使用無盤備份。在磁碟速度很慢,而網路超快的情況下,無盤備份會更給力。如果網路很慢,有可能會出現資料丟失,推薦你改成 no。
使用無盤複製的話,如果此刻有新的 slave 發起全量同步,需要等待之前的傳輸完畢才能開啟傳輸。
所以可以使用設定 repl-diskless-sync-delay 5
引數指定一個延遲時間,這個單位是秒,讓 master 節點等待一會,讓更多 slave 節點連線再執行傳輸。
因為一旦開始傳輸,master 節點無法響應新的 slave 節點的全量複製請求,只能在佇列中等待下一次 RDB 記憶體快照傳輸。
想要關閉這個功能,設定為 0 即可。
mastar 節點有兩種方式傳輸 RDB,slave 節點也有兩種方式載入 master 傳輸過來的 RDB 資料。
一共有三個取值可設定。
需要注意的是,diskless-load 目前在實驗階段,因為 RDB 記憶體快照資料並沒有持久化到磁碟,因此有可能造成資料丟失;
另外,該模式會佔用更多記憶體,可能會導致 OOM。
預設設定repl-ping-replica-period 10
表示 slave 每 10 秒 PING 一次 master。
很重要的一個引數,slave 與 master 之間的複製超時時間,預設設定是repl-timeout 60
,表示在 60 秒內 ping 不通,則判定超時。
超時包含以下三種情況。
當檢測到超時,將會關閉 master 與 slave 之間的連線,slave 會發起重新建立主從連線的請求,對於記憶體資料比較大的系統,可以增大 repl-timeout
的值。
你需要注意的是,這個設定一定要大於 repl-ping-replica-period
的值,否則每次心跳監測都超時。
當 slave 與 master 全量同步(slave 傳送 psync/sync 指令給 master)完成後,後續的增量同步是否設定成 TCP_NODELAY
。
如果設定成 yes
,master 將合併小的 TCP 包從而節省頻寬,但是會增加同步延遲(40 ms),造成 master 與 slave 資料不一致;設定成 no
,則 master 會立即傳送資料給 slave,沒有延遲。
預設設定 repl-disable-tcp-nodelay no
。
設定主從複製積壓緩衝區(backlog) 容量大小,這是一個環形陣列,正常主從同步不涉及到 repl-backlog。當主從斷開重連,repl-backlog 的作用就出來了。
緩衝區用於存放斷連期間 master 接受的寫請求資料,當主從斷開重連,通常不需要執行全量同步,只需要將斷連期間的部分資料傳遞到 slave 即可。
主從複製積壓緩衝區越大,slave 可以承受的斷連時間越長。
預設設定是 repl-backlog-size 1mb
,建議根據每秒流量大小和斷開重連時間長,設定大一點,比如 128 mb。
用於設定當 master 與 slave 斷連多少秒之後,master 清空主從複製積壓緩衝區(repl-backlog)。設定成 0 ,表示永遠不清空。預設設定repl-backlog-ttl 3600
。
slave 優先順序,這個設定是給哨兵使用的,當 master 節點掛掉,哨兵會選擇一個 priority 最小的 slave 節點作為新的 master,這個值越小沒接越優先選中。
如果是 0,那意味著這個 slave 將不能選中成為 master,預設設定是 replica-priority 100
。
這兩個設定要一起設定才有意義,如果有一個設定成 0,表示關閉該特性。
先看預設設定含義。
min-replicas-to-write 3
min-replicas-max-lag 10
如果 master 發現超過 3 個 slave 節點連線 master 延遲大於 10 秒,那麼 master 就停止接收使用者端寫請求。這麼做的目的是為了儘可能保證主從資料一致性。
master 會記錄每個 slave 最近一次發來 ping 的時間,掌握每個 slave 的執行情況。
我在 Redis 6.0 版本,實現了伺服器端輔助實現使用者端快取的特性,需要追蹤使用者端有哪些 key。當某個 key 被修改,我需要把這個失效資訊傳送到對應的使用者端將本地快取失效,這個設定就是用於指定追蹤表儲存的最大 key 數量,一旦超過這個數量,即使這個 key 沒有被修改,為了回收記憶體我也會強制這個 key 所在的使用者端快取值失效。
設定 0 表示不限制,需要注意的是,如果使用廣播模式實現鍵追蹤,則不需要額外記憶體,忽略這個設定。
使用廣播模式的不足就是與這個 key 無關的使用者端也會收到失效訊息。
正是由於我快的一塌糊塗,攻擊者一秒鐘可以嘗試 100 萬個密碼,所以你應該使用非常健壯的密碼。
ACL 紀錄檔的最大長度,預設設定 acllog-max-len 128
表示最大 128 mb。
另外,使用 aclfile /etc/redis/users.acl
設定 ACL 檔案所在位置。
當前 Redis 伺服器的存取密碼,預設是不需要密碼存取,網路危險,必須設定,如 requirepass magebyte660
設定密碼為 「magebyte666」。
設定使用者端同時連線的最大數量,預設設定是 maxclients 10000
。達到最大值,我將關閉使用者端新的連線,並行送一個 max number of clients reached
錯誤給使用者端。
作為用記憶體儲存資料的我,這部分的設定也相當重要。
設定使用記憶體最大位元組,當記憶體達到限制,我將嘗試根據設定的記憶體淘汰策略(參見 maxmemory-policy)刪除一些 key。建議你不要設定太大的記憶體,防止執行 RDB 記憶體快照檔案或者 AOF 重寫的時候因資料太大而阻塞過長時間。
推薦最大設定為 maxmemory 6GB
。
如果淘汰策略是 noeviction
,當收到寫請求,我將回復錯誤給使用者端,讀請求依然可以執行。
如果你把我當做一個 LRU 或 LFU 快取系統的時候,那請用心關注以下設定。
設定記憶體淘汰策略,定義當記憶體滿時如何淘汰 key,預設設定是 noeviction
。
LRU, LFU and minimal TTL algorithms 不是精確的演演算法,是一個近似的演演算法(主要為了節省記憶體)。
所以需要你自己權衡速度和精確度。預設會抽取 5 個 key,選擇一個最近最少使用的 key 淘汰,你可以改變這個數量。
預設的 5 可以提供不錯的結果。設定 10 會非常接近真實的 LRU 但是會耗費更多的 CPU,設定 3 會更快,但是就不那麼精確了。
從 Redis 5.0 開始,預設情況下 slave 節點會忽略 maxmemory
設定,除非在故障轉移後或手動將其提升為 master。這意味著只有 master 才會執行記憶體淘汰策略,當 master 刪除 key 後會傳送 DEL
指令給 slave。
預設設定replica-ignore-maxmemory yes
。
我有兩種方式刪除過期資料。
這個設定用於指定過期 key 滯留在記憶體中的比例,預設值是 1,表示最多隻能有 10 % 的過期 key 駐留在記憶體中,值設定的越小,那麼一次淘汰週期內需需要消耗的 CPU 將會更多,因為需要刪除更多的過期資料。
MySQL:「 可以使用非阻塞的方式刪除 bigkey 麼?」
我提供了兩種刪除 key 的基本命令用於刪除資料。
DEL
指令:這是一個阻塞的刪除,執行該指令會停止處理寫請求,使用同步的方式去回收 DEL 刪除的物件的記憶體。如果這個 key 對應的 value 是一個非常小的物件, DEL
執行的時間非常短,時間複雜度為 O(1) 或者 O(log n)。如果 key 對應的 value 非常大,比如集合物件的資料包含百萬個元素,伺服器將阻塞很長時間(幾秒鐘)才能完成操作。UNLINK(非阻塞刪除)、(非同步刪除) FLUSHALL ASYNC/FLUSHDB ASYNC
:後臺回收記憶體,這些指令在常數級別時間內執行,會使用一個新的執行緒在後臺漸進的刪除並釋放記憶體(Lazy Free 機制)。由於 maxmemory 和 maxmemory-policy 策略設定,我會刪除一些資料,防止記憶體爆掉。使用lazyfree-lazy-eviction yes
表示使用 lazy free 機制,該場景開啟 lazy free 可能會導致淘汰資料的記憶體釋放不及時,出現記憶體超限。
對於設定了 TTL 的鍵,過期後刪除。如果想啟用 lazy free 機制刪除,則設定 lazyfree-lazy-eviction yes
。
針對有些指令在處理已存在的鍵時,會帶有一個隱式的 DEL 鍵的操作。
如 rename
命令,當目標鍵已存在,我會先刪除目標鍵,如果這些目標鍵是一個 big key,那可能會出現阻塞刪除的效能問題。 此引數設定就是解決這類問題,建議設定 lazyfree-lazy-server-del yes
開啟。
該設定針對 slave 進行全量資料同步,在載入 master 的 RDB 記憶體快照檔案之前,會先執行 flashall
清理資料的時候是否採用非同步 flush 機制。
推薦你使用 replica-lazy-flush yes
設定,可減少全量同步耗時,從而減少 master 因輸出緩衝區暴漲引起的記憶體增長。
意思是是否將 DEL
指令的預設行為替換成 lazy free 機制刪除,效果就跟 UNLINK
一樣,只要設定成 lazyfree-lazy-user-del yes
。
FLUSHDB, FLUSHALL, SCRIPT FLUSH, FUNCTION FLUSH
可以使用額外引數 ASYNC|SYNC
決定使用同步還是非同步操作,當沒有指定這個可選項,可以通過 lazyfree-lazy-user-flush yes
表示使用非同步刪除。
大家知道我是單執行緒模型處理讀寫請求,但是有一些操作可以使用其他執行緒處理,比如 UNLINK
,I/O 讀寫操作。
在 6.0 版本,我提供了 I/O 多執行緒處理 Socket 讀寫,利用 I/O 多執行緒可以提高使用者端 Socket 讀寫效能。
預設設定是關閉的,我只建議當你的機器至少是 4 核 CPU 或者更多的情況啟用,並且設定的執行緒數少於機器總 CPU 核數,設定超過 8 個執行緒對提升沒什麼幫助。
當你的機器是四核 CPU,那可以嘗試設定使用 2~3 個 I/O 執行緒,如果是 8 核 CPU,一般只需要設定 6 個執行緒。
如下設定表示開啟 I/O 執行緒組,執行緒組的 I/O 執行緒數量為 3。
io-threads-do-reads yes
io-threads 3
除了 RDB 記憶體快照檔案作為持久化手段以外,還能使用 AOF(Append only file) 實現持久化,AOF 是一種可選的持久化策略提供更好資料安全性。
預設設定下,我最多隻會丟失一秒的資料,你甚至可以設定更高階別,最多隻丟失一次 write 操作,但這樣會對損耗效能。
appendonly yes
表示開啟 AOF 持久化,可以同時開啟 AOF 和 RDB 記憶體快照持久化,如果開啟了 AOF ,我會先載入 AOF 用於恢復記憶體資料。
指定 AOF 檔名稱,預設名字是 appendonly.aof
。為了方便,你可以設定 appenddirname
指定 AOF 檔案儲存目錄。
呼叫作業系統的 fsync()
函數告訴作業系統把輸出緩衝區的資料持久化到磁碟, AOF 檔案刷寫的頻率有三種。
預設設定是 appendfsync everysec
,推薦大家這麼設定,兼顧了速度和資料安全。
當 appendfsync 的設定設定成 always
或者 everysec
,現在有一個後臺 save 程序(可能是生成 RDB 記憶體快照的 bgsave 程序,也有可能是 AOF rewrite 程序)正在進行大量的磁碟 I/O 操作,會造成呼叫 fsync()
執行太長,後續其他想要呼叫 fsync()
的程序就會阻塞。
為了緩解這個問題,可以使用以下設定 no-appendfsync-on-rewrite yes
表示當已經有 bgsave
和bgrewriteaof
後臺程序在呼叫 fsync()
時,不再開啟新程序執行 AOF 檔案寫入。
這樣的話,就會出現當前有子程序在做 bgsave 或者其他的磁碟操作時,我就無法繼續寫 AOF 檔案,這意味著可能會丟失更多資料。
如果有延遲問題,請將此選項改為 yes
。否則將其保留為 no
。從持久化的角度來看,no
是最安全的選擇。
為了防止 AOF 檔案過大,antirez 大佬給我搞了個 AOF 重寫機制。
auto-aof-rewrite-percentage 100
表示當前 AOF 檔案大小超過上一次重寫的 AOF 檔案大小的百分之多少(如果沒有執行過 AOF 重寫,那就參照原始 AOF 檔案大小),則執行 AOF 檔案重寫操作。
除了這個設定,你還要設定 auto-aof-rewrite-min-size 64mb
用於指定觸發 AOF 重寫操作的檔案大小。
如果該 AOF 檔案大小小於該值,即使檔案增長比例達到 100%,我也不會觸發 AOF 重寫操作,這是為了防止 AOF 檔案其實很小,但是滿足增長百分比時的多餘 AOF 重寫操作。
如果設定為auto-aof-rewrite-percentage 0
,表示禁用 AOF 重寫功能,建議大家開啟 AOF 重寫,防止檔案過大。
MySQL:如果 AOF 檔案是損壞的,你還載入資料還原到記憶體中麼?
載入 AOF 檔案把資料還原到記憶體中,檔案可能是損壞的,比如檔案末尾是錯誤的。這種情況一般是由於宕機導致,尤其是使用 ext4 檔案系統掛載時沒設定 data=ordered
選項。
在這種情況下,我可以直接報錯,或者儘可能的讀取可讀的 AOF 內容。
如果設定成 aof-load-truncated yes
,我依然會載入並讀取這個損壞的 AOF 檔案,並記錄一個錯誤紀錄檔通知程式設計師。
設定成 aof-load-truncated no
,我就會報錯並拒絕啟動服務,你需要使用 redis-check-aof 工具修復 AOF 檔案,再啟動 Redis。如果修復後還是錯誤,我依然報錯並拒絕啟動。
這就是大名鼎鼎的 RDB-AOF 混合持久化功能,設定成 aof-use-rdb-preamble yes
(必須先開啟 AOF),AOF 重寫生成的檔案將同時包含 RDB 格式的內容和 AOF 格式內容。
混合持久化是在 AOF 重寫完成的,開啟混合持久化後,fork 出的子程序先將記憶體資料以 RDB 的方式寫入 AOF 檔案,接著把 RDB 格式資料寫入 AOF 檔案期間收到的增量命令從重寫緩衝區以 AOF 格式寫到檔案中。
寫入完成後通知主程序更新統計資訊,並把含有 RDB 格式和 AOF 格式的 AOF 檔案替換舊的 AOF 檔案。
這樣的好處是可以結合 RDB 和 AOF 的優點,實現快速載入同時避免丟失過多資料,缺點是 AOF 檔案的 RDB 部分內容不是 AOF 格式,可讀性差(都是程式解析讀取,哪個傻瓜程式設計師去讀這個呀),強烈推薦你使用這個來保證持久化。
我在 7.0 版本新增的特性,大體就是講 AOF 現在支援時間戳了,你可以做到基於時間點來恢復資料。
預設是是 aof-timestamp-enabled no
表示關閉該特性,你可以按照實際需求選擇開啟。
Redis Cluster 叢集相關設定,使用叢集方式的你必須重視和知曉。彆嘴上原理說的頭頭是道,而叢集有哪些設定?如何設定讓叢集快到飛起,實現真正的高可用卻一頭霧水,通過下面這些設定詳解也讓你對叢集原理更加深刻。
普通的 Redis 範例是不能成為叢集的一員,想要將該節點加入 Redis Cluster,需要設定 cluster-enabled yes
。
cluster-config-file nodes-6379.conf
指定叢集中的每個節點檔案。
叢集中的每個節點都有一個組態檔,這個檔案並不是讓程式設計師編輯的,是我自己建立和更新的,每個節點都要使用不同的組態檔,一定要確保同一個叢集中的不同節點使用的是不同的檔案。
設定叢集節點不可用的最大超時時間,節點失效檢測。叢集中當一個節點向另一個節點傳送 PING 命令,但是目標節點未在給定的時限內返回 PING 命令的回覆時,那麼傳送命令的節點會將目標節點標記為 PFAIL(possible failuer,可能已失效);
如果 master 節點超過這個時間還是無響應,則用它的從節點將啟動故障遷移,升級成主節點。
預設設定是 cluster-node-timeout 15000
,單位是毫秒數。
該埠是叢集匯流排監聽 TCP 連線的埠,預設設定為 cluster-port 0
,我就會把埠繫結為使用者端命令埠 + 10000(使用者端埠預設 6379,所以繫結為 16379 作為叢集匯流排埠)。每個 Redis Cluster 節點都需要開放兩個埠:
該設定用於決定當 Redis Cluster 叢集中,一個 master 宕機後,如何選擇一個 slave 節點完成故障轉移自動恢復(failover)。如果設定為 0 ,則不管 slave 與 master 之間斷開多久,都有資格成為 master。
下面提供了兩種方式來評估 slave 的資料是否太舊。
ping
操作、master 節點傳輸過來的寫指令、上次與 master 斷開的時間等。如果上次互動的時間過去很久,那麼這個節點就不會發起 failover。針對第二點,互動時間可以通過設定定義,如果 slave 與 master 上次互動的時間大於 (node-timeout * cluster-replica-validity-factor) + repl-ping-replica-period
,該 slave 就不會發生 failover。
例如,``node-timeout = 30秒,
cluster-replica-validity-factor=10,
repl-ping-slave-period=10`秒, 表示 slave 節點與 master 節點上次互動時間已經過去了 310 秒,那麼 slave 節點就不會做 failover。
調大 cluster-replica-validity-factor
則允許儲存過舊資料的 slave 節點提升為 master,調小的話可能會導致沒有 slave 節點可以升為 master 節點。
考慮高可用,建議大家設定為 cluster-replica-validity-factor 0
。
沒有 slave 節點的 master 節點稱為孤兒 master 節點,這個設定就是用於防止出現孤兒 master。
當某個 master 的 slave 節點宕機後,叢集會從其他 master 中選出一個富餘的 slave 節點遷移過來,確保每個 master 節點至少有一個 slave 節點,防止當孤立 master 節點宕機時,沒有 slave 節點可以升為 master 導致叢集不可用。
預設設定為 cluster-migration-barrier 1
,是一個遷移臨界值。
含義是:被遷移的 master 節點至少還有 1 個 slave 節點才能做遷移操作。比如 master A 節點有 2 個以上 slave 節點 ,當叢集出現孤兒 master B 節點時,A 節點富餘的 slave 節點可以遷移到 master B 節點上。
生產環境建議維持預設值,最大可能保證高可用,設定為非常大的值或者設定 cluster-allow-replica-migration no
禁用自動遷移功能。
cluster-allow-replica-migration
預設設定為 yes,表示允許自動遷移。
預設設定是 yes
,表示為當 redis cluster 發現還有雜湊槽沒有被分配時禁止查詢操作。
這就會導致叢集部分宕機,整個叢集就不可用了,當所有雜湊槽都有分配,叢集會自動變為可用狀態。
如果你希望 cluster 的子集依然可用,設定成 cluster-require-full-coverage no
。
當設定成 yes
,在 master 宕機時,slave 不會做故障轉移升為 master。
這個設定在多資料中心的情況下會很有用,你可能希望某個資料中心永遠不要升級為 master 節點,否則 master 節點就漂移到其他資料中心了,正常情況設定成 no。
預設是 no
,表示當叢集因主節點數量達不到最小值或者雜湊槽沒有完全分配而被標記為失效時,節點將停止所有使用者端請求。
設定成 yes
,則允許叢集失效的情況下依然可從節點中讀取資料,保證了高可用。
設定成 yes
,表示當叢集因主節點數量達不到最小值或者雜湊槽沒有完全分配而被標記為失效時,pub/sub 依然可以正常執行。
設定每個叢集匯流排連線的傳送位元組緩衝區的記憶體使用限制,超過限制緩衝區將被清空(主要為了防止傳送緩衝區傳送給慢速連線時無限延長時間的問題)。
預設禁用,建議最小設定 1gb,這樣預設情況下叢集連線緩衝區可以容納至少一條 pubsub 訊息(client-query-buffer-limit 預設是 1gb);
慢查詢(Slow Log)紀錄檔是我用於記錄慢查詢執行時間的紀錄檔系統,只要查詢超過設定的時間,都會記錄。slowlog 只儲存在記憶體中,因此效率很高,大家不用擔心會影響到 Redis 的效能。
執行時間不包括 I/O 操作的時間,比如與使用者端建立連線、傳送回復等,只記錄執行命令執行階段所需要的時間。
你可以使用兩個引數設定慢查詢紀錄檔系統。
slowlog-log-slower-than
:指定對執行時間大於多少微秒(microsecond,1 秒 = 1,000,000 微秒)的查詢進行記錄,預設是 10000 微妙,推薦你先執行基線測試得到一個基準時間,通常這個值可以設定為基線效能最大延遲的 3 倍。slowlog-max-len
:設定最多儲存多少條慢查詢的紀錄檔,slowlog 本身是一個 FIFO 佇列,當超過設定的最大值後,我會把最舊的一條紀錄檔刪除。預設設定 128,如果設定太大會佔用多大記憶體。延遲監控(LATENCY MONITOR)系統會在執行時抽樣部分命令來幫助你分析 Redis 卡頓的原因。
通過 LATENCY
命令,可以列印一些檢視和報告,系統只會記錄大於等於指定值的命令。
預設設定 latency-monitor-threshold 0
,設定 0 表示關閉這個功能。沒有延遲問題,沒必要開啟開啟監控,因為會對效能造成很大影響。
在執行過程中你懷疑有延遲效能問題,想要監控的話可以使用 CONFIG SET latency-monitor-threshold <milliseconds>
開啟,單位是毫秒。
這部分設定主要圍繞以下幾個方面。
在 Redis 7.0 版本雜湊表資料型別有兩種資料結構儲存資料,分別為雜湊表和 listpack。當資料量很小時,可以使用更高效的資料結構儲存,從而達到在不影響效能的情況下節省記憶體。
hash-max-listpack-entries 512
:指定使用 listpack 儲存的最大條目數。hash-max-listpack-value 64
:listpack 中,條目 value 值最大位元組數,建議設定成 1024。在 7.0 版本以前,使用的是 ziplist 資料結構,設定如下。
hash-max-ziplist-entries 512
hash-max-ziplist-value 64
Lists 也可以使用一種特殊方式進行編碼來節省大量記憶體空間。在 Redis 7.0 之後,Lits 底層的資料結構使用 linkedlist 或者 listpack 。
Redis 3.2 版本,List 內部是通過 linkedlist 和 quicklist 實現,quicklist 是一個雙向連結串列, quicklist 的每個節點都是一個 ziplist,從而實現節省記憶體。
元素少時用 quicklist,元素多時用 linkedlist。listpack 的目的就是用於替代 ziplist 和 quicklist。listpack 也叫緊湊列表,它的特點就是用一塊連續的記憶體空間來緊湊地儲存資料,同時為了節省記憶體空間
list-max-ziplist-size
7.0 版本之前list-max-ziplist-size
用於設定 quicklist 中的每個節點的 ziplist 的大小。 當這個值設定為正數時表示 quicklist 每個節點的 ziplist 最多可儲存元素數量,超過該值就會使用 linkedlist 儲存。
當 list-max-ziplist-size
為負數時表示限制每個 quicklistNode 的 ziplist 的記憶體大小,超過這個大小就會使用 linkedlist 儲存資料,每個值有以下含義:
預設值為 -2,也是官方最推薦的值,當然你可以根據自己的實際情況進行修改。
list-max-listpack-size
7.0 之後,設定修改為list-max-listpack-size -2
則表示限制每個 listpack 大小,不再贅述。
list-compress-depth
壓縮深度設定,用來設定壓縮 Lists 的,當 Lists 底層使用 linkedlist 也是可以壓縮的,預設是 list-compress-depth 0
表示不壓縮。一般情況下,Lists 的兩端存取的頻率高一些,所以你可以考慮把中間的資料進行壓縮。
不同引數值的含義如下。
需要注意的是,head 和 tail 節點永遠都不會被壓縮。
Sets 底層的資料結構可以是 intset(整形陣列)和 Hashtable(雜湊表),intset 你可以理解成陣列,Hashtable 就是普通的雜湊表(key 存的是 Sets 的值,value 為 null)。有沒有覺得 Sets 使用雜湊表儲存是意想不到的事情?
set-max-intset-entries
當集合的元素都是 64 位以內的十進位制整數時且長度不超過 set-max-intset-entries
設定的值(預設 512),Sets 的底層會使用 intset 儲存節省記憶體。新增的元素大於 set-max-intset-entries
設定的值,底層實現由 intset 轉成雜湊表儲存。
在 Redis 7.0 版本之前,有序集合底層的資料結構有 ziplist 和 skipist,之後使用 listpack 代替了 ziplist。
7.0 版本之前,當集合元素個數小於 zset-max-ziplist-entries
設定,同時且每個元素的值大小都小於zset-max-ziplist-value
設定(預設 64 位元組,推薦調大到 128)時,我將使用 ziplist 資料結構儲存資料,有效減少記憶體使用。與此類似,7.0 版本之後我將使用 listpack 儲存。
## 7.0 之前的設定
zset-max-ziplist-entries 128
zset-max-ziplist-value 64
## 7.0 之後的設定
zset-max-listpack-entries 128
zset-max-listpack-value 64
HyperLogLog 是一種高階資料結構,統計基數的利器。HyperLogLog 的儲存結構分為密集儲存結構和稀疏儲存結構兩種,預設為稀疏儲存結構,而我們常說的佔用 12K 記憶體的則是密集儲存結構,稀疏結構佔用的記憶體會更小。
hll-sparse-max-bytes
預設設定是 hll-sparse-max-bytes 3000
,單位是 Byte,這個設定用於決定儲存資料使用稀疏資料結構(sparse)還是稠密資料結構(dense)。
如果 HyperLogLog 儲存內容大小大於 hll-sparse-max-bytes 設定的值將會轉換成稠密的資料結構(dense)。
推薦的值是 0~3000,這樣PFADD
命令的並不會慢多少,還能節省空間。如果記憶體空間相對 cpu 資源更缺乏,可以將這個值提升到 10000。
Stream 是 Redis 5.0 版本新增的資料型別。Redis Streams 是一些由基數樹(Radix Tree)連線在一起的節點經過 delta 壓縮後構成的,這些節點與 Stream 中的訊息條目(Stream Entry)並非一一對應,而是每個節點中都儲存著若干 Stream 條目,因此這些節點也被稱為宏節點或大節點。
stream-node-max-bytes 4096
單位為 Byte,預設值 4096,用於設定每個宏節點佔用的記憶體上限為 4096,0 表示無限制。
stream-node-max-entries 100
用於設定每個宏節點儲存元素個數。 預設值 100,0 表示無限制。當一個宏節點儲存的 Stream 條目到達上限,新新增的條目會儲存到新的宏節點中。
我採用的是漸進式 rehash,這是一個惰性策略,不會一次性把所有資料遷移完,而是分散到每次請求中,這樣做的目的是防止資料太多要遷移阻塞主執行緒。
在漸進式 rehash 的同時,推薦你使用 activerehashing yes
開啟定時輔助執行 rehash,預設情況下每一秒執行 10 次 rehash 加快遷移速度,儘可能釋放記憶體。
關閉該功能的話,如果這些 key 不再活躍不被被存取到,rehash 操作可能不再有機會完成,會導致雜湊表佔用更多記憶體。
這三個設定是用來強制斷開使用者端連線的,當用戶端沒有及時把緩衝區的資料讀取完畢,我會認為這個使用者端可能完蛋了(一個常見的原因是 Pub/Sub 使用者端處理髮布者的訊息不夠快),於是斷開連線。
一共分為三種不同型別的使用者端,分別設定不同的限制。
client-output-buffer-limit
的語法如下。
client-output-buffer-limit <class> <hard limit> <soft limit> <soft seconds>
預設情況下,普通使用者端不會限制,只有後非同步的使用者端才可能傳送傳送請求的速度比讀取響應速度快的問題。比如 pubsub 和 replica 使用者端會有預設的限制。
soft limit 或者 hard limit 設定為 0,表示不啟用此限制。預設設定如下。
client-output-buffer-limit normal 0 0 0
client-output-buffer-limit replica 256mb 64mb 60
client-output-buffer-limit pubsub 32mb 8mb 60
每個使用者端都有一個 query buffer(查詢緩衝區或輸入緩衝區),用於儲存使用者端傳送命令,Redis Server 從 query buffer 獲取命令並執行。
如果程式的 Key 設計不合理,使用者端使用大量的 query buffer,導致 Redis 很容易達到 maxmeory 限制。最好限制在一個固定的大小來避免佔用過大記憶體的問題。
如果你需要傳送巨大的 multi/exec 請求的時候,那可以適當修改這個值以滿足你的特殊需求。
預設設定為 client-query-buffer-limit 1gb
。
這是 7.0 版本特性,每個與伺服器端建立連線的使用者端都會佔用記憶體(查詢緩衝區、輸出緩衝區和其他緩衝區),大量的使用者端可能會佔用過大記憶體導致 OOM,為了避免這個情況,我提供了一種叫做(Client Eviction)使用者端驅逐機制用於限制記憶體佔用。
設定方式有兩種。
maxmemory-clients 1g
來限制所有使用者端佔用記憶體總和。maxmemory-clients 5%
表示使用者端總和記憶體佔用最多為 Redis 最大記憶體設定的 5%。預設設定是 maxmemory-clients 0
表示無限制。
MySQL:「達到最大記憶體限制,你會把所有使用者端連線都釋放麼?」
不是的,一旦達到限制,我會優先嚐試斷開使用記憶體最多的使用者端。
批次請求(單個字串的元素)記憶體大小限制,預設是 proto-max-bulk-len 512mb
,你可以修改限制,但必須大於等於 1mb。
我會在後臺呼叫一些函數來執行很多後臺任務,比如關閉超時連線,清理不再被請求的過期的 key,rehash、執行 RDB 記憶體快照和 AOF 持久化等。
並不是所有的後臺任務都需要使用相同的頻率來執行,你可以使用 hz 引數來決定執行這些任務的頻率。
預設設定是 hz 10
,表示每秒執行 10 次,更大的值會消耗更多的 CPU 來處理後臺任務,帶來的效果就是更快的清理過期 key,清理的超時連線更精確。
這個值的範圍是 1~500,不過並不推薦設定大於 100 的值。大家使用預設值就好,或者最多調高到 100。
預設設定是 dynamic-hz yes
,啟用 dynamic-hz 後,將啟用自適應 HZ 值的能力。hz 的設定值將會作為基線,Redis 服務中的實際 hz 值會在基線值的基礎上根據已連線到 Redis 的使用者端數量自動調整,連線的使用者端越多,實際 hz 值越高,Redis 執行定期任務的頻率就越高。
aof-rewrite-incremental-fsync
當子程序進行 AOF 重寫時,如果設定成 aof-rewrite-incremental-fsync yes
,每生成 4 MB 資料就執行一次 fsync
操作,分批提交到硬碟來避免高延遲峰值,推薦開啟。
當我在儲存 RDB 記憶體快照檔案時,如果設定成 db-save-incremental-fsync yes
,每生成 4MB 檔案就執行一次 fsync
操作,分批提交到硬碟來避免高延遲峰值,推薦開啟。
這個設定生效的前提是記憶體淘汰策略設定的是 volatile-lfu
或allkeys-lfu
。
lfu-log-factor 用於調整 Logistic Counter 的增長速度,lfu-log-factor 值越大,Logistic Counter 增長越慢。預設設定 10。
以下是表格是官方不同 factor 設定下,計數器的改變頻率。注意:表格是通過如下命令獲得的: redis-benchmark -n 1000000 incr foo redis-cli object freq foo
。
factor | 100 hits | 1000 hits | 100K hits | 1M hits | 10M hits |
---|---|---|---|---|---|
0 | 104 | 255 | 255 | 255 | 255 |
1 | 18 | 49 | 255 | 255 | 255 |
10 | 10 | 18 | 142 | 255 | 255 |
100 | 8 | 11 | 49 | 143 | 255 |
lfu-decay-time 用於調整 Logistic Counter 的衰減速度,它是一個以分鐘為單位的數值,預設值為 1;lfu-decay-time 值越大,衰減越慢。
MySQL:「什麼是線上記憶體碎片整理?」
Active (online) defragmentation 線上記憶體碎片整理指的是自動壓縮記憶體分配器分配和 Redis 頻繁做更新操作、大量過期資料刪除,釋放的空間(不夠連續)無法得到複用的記憶體空間。
通常來說當碎片化達到一定程度(檢視下面的設定)Redis 會使用 Jemalloc 的特性建立連續的記憶體空間, 並在此記憶體空間對現有的值進行拷貝,拷貝完成後會釋放掉舊的資料。 這個過程會對所有的導致碎片化的 key 以增量的形式進行。
需要注意的是
CONFIG SET activefrag yes
來啟用。清理的條件
activefrag yes
:記憶體碎片整理總開關,預設為禁用狀態 no。
active-defrag-ignore-bytes 200mb
:記憶體碎片佔用的記憶體達到 200MB。
active-defrag-threshold-lower 20
:記憶體碎片的空間佔比超過系統分配給 Redis 空間的 20% 。
在同時滿足上面三項設定時,記憶體碎片自動整理功能才會啟用。
CPU 資源佔用
MySQL:如何避免自動記憶體碎片整理對效能造成影響?
清理的條件有了,還需要分配清理碎片佔用的 CPU 資源,保證既能正常清理碎片,又能避免對 Redis 處理請求的效能影響。
active-defrag-cycle-min 5
:自動清理過程中,佔用 CPU 時間的比例不低於 5%,從而保證能正常展開清理任務。
active-defrag-cycle-max 20
:自動清理過程佔用的 CPU 時間比例不能高於 20%,超過的話就立刻停止清理,避免對 Redis 的阻塞,造成高延遲。
整理力度
active-defrag-max-scan-fields 1000
:碎片整理掃描到set/hash/zset/list
時,僅當 set/hash/zset/list
的長度小於此閥值時,才會將此鍵值對加入碎片整理,大於這個值的鍵值對會放在一個列表中延遲處理。
active-defrag-threshold-upper 100
:記憶體碎片空間佔作業系統分配給 Redis 的總空間比例達此閾值(預設 100%),我會盡最大努力整理碎片。建議你調整為 80。
預設設定為 jemalloc-bg-thread yes
,表示啟用清除髒頁後臺執行緒。
你可以將 Redis 的不同執行緒和程序繫結到特定的 CPU,減少上下文切換,提高 CPU L1、L2 Cache 命中率,實現最大化的效能。
你可以通過修改組態檔或者taskset
命令繫結。
可分為三個模組。
Redis 支援分別設定上述模組的 CPU 親合度,預設情況是關閉的。
server_cpulist 0-7:2
,I/O 執行緒(包含主執行緒)相關操作繫結到 CPU 0、2、4、6。bio_cpulist 1,3
,bio 執行緒相關的操作繫結到 CPU 1、3。aof_rewrite_cpulist
,aof rewrite 後臺程序繫結到 CPU 8、9、10、11。bgsave_cpulist 1,10-11
,bgsave 後臺程序繫結到 CPU 1、10、11。注意事項