1. 程式人生 > >Redis部署及參數筆記

Redis部署及參數筆記

redis部署 redis參數

Redis Cluster是Redis的集群實現,內置數據自動分片機制,集群內部將所有的key映射到16384個Slot中,集群中的每個Redis Instance負責其中的一部分的Slot的讀寫。
集群客戶端連接集群中任一Redis Instance即可發送命令,當Redis Instance收到自己不負責的Slot的請求時,會將負責請求Key所在Slot的Redis Instance地址返回給客戶端,客戶端收到後自動將原請求重新發往這個地址,對外部透明。
一個Key屬於哪裏Slot由crc16(key)%16384決定。

負載均衡,集群的Redis Instance之間可以遷移數據,以Solt為單位;需要外部命令觸發。

#redis cluster安裝

yum -y install zlib ruby rubygems
gem install redis --version 3.0.0
wget https://rubygems.global.ssl.fastly.net/gems/redis-3.2.0.gem
gem install -l /root/redis-3.2.0.gem
wget http://download.redis.io/releases/redis-3.0.1.tar.gz
cd redis-3.0.1
make
cp src/redis-server /usr/local/bin/
cp src/redis-cli /usr/local/bin/
cp src/redis-trib.rb /usr/local/bin/
-----------------------------------------------------------------
vi /etc/sysctl.conf
vm.overcommit_memory=1
echo never > /sys/kernel/mm/transparent_hugepage/enabled
vi /etc/rc.local
echo never > /sys/kernel/mm/transparent_hugepage/enabled
echo 512 > /proc/sys/net/core/somaxconn
mkdir -p /etc/redis/data
--------------------------------------------------------------
vim /etc/redis/redis-commom.conf
#GENERAL
daemonize no
tcp-backlog 511
timeout 0
tcp-keepalive 0
loglevel notice
databases 16
dir /etc/redis/data
slave-serve-stale-data yes
#slave只讀
slave-read-only yes
#not use default
repl-disable-tcp-nodelay yes
slave-priority 100
#打開aof持久化
appendonly yes
#每秒一次aof寫
appendfsync everysec
#關閉在aof rewrite的時候對新的寫操作進行fsync
no-appendfsync-on-rewrite yes
auto-aof-rewrite-min-size 64mb
lua-time-limit 5000
#requirepass uuzztest
#打開redis集群
cluster-enabled yes
#節點互連超時的閥值
cluster-node-timeout 15000
cluster-migration-barrier 1
slowlog-log-slower-than 10000
slowlog-max-len 128
notify-keyspace-events ""
hash-max-ziplist-entries 512
hash-max-ziplist-value 64
list-max-ziplist-entries 512
list-max-ziplist-value 64
set-max-intset-entries 512
zset-max-ziplist-entries 128
zset-max-ziplist-value 64
activerehashing yes
client-output-buffer-limit normal 0 0 0
client-output-buffer-limit slave 256mb 64mb 60
client-output-buffer-limit pubsub 32mb 8mb 60
hz 10
aof-rewrite-incremental-fsync yes
----------------------------------------------------------
vim /etc/redis/redis-6379.conf
#包含通用配置
include /etc/redis/redis-common.conf
#監聽tcp端口
port 6379
#最大可用內存
maxmemory 1024m
#內存耗盡時采用的淘汰策略:
# volatile-lru -> remove the key with an expire set using an LRU algorithm
# allkeys-lru -> remove any key accordingly to the LRU algorithm
# volatile-random -> remove a random key with an expire set
# allkeys-random -> remove a random key, any key
# volatile-ttl -> remove the key with the nearest expire time (minor TTL)
# noeviction -> don‘t expire at all, just return an error on write operations
#maxmemory-policy allkeys-lru
maxmemory-policy noeviction
#aof存儲文件
appendfilename "appendonly-6379.aof"
#rdb文件,只用於動態添加slave過程
dbfilename dump-6379.rdb
#cluster配置文件(啟動自動生成)
cluster-config-file nodes-6379.conf
#部署在同一機器的redis實例,把<span style="font-size: 1em; line-height: 1.5;">auto-aof-rewrite搓開,防止瞬間fork所有redis進程做rewrite,占用大量內存</span>
auto-aof-rewrite-percentage 80-100
-------------------------------------------------------------------------------
redis-server /etc/redis/redis-6379.conf > /etc/redis/redis-6379.log 2>&1 &
redis-server /etc/redis/redis-6380.conf > /etc/redis/redis-6380.log 2>&1 &
redis-server /etc/redis/redis-6381.conf > /etc/redis/redis-6381.log 2>&1 &
redis-server /etc/redis/redis-6382.conf > /etc/redis/redis-6382.log 2>&1 &
redis-server /etc/redis/redis-6383.conf > /etc/redis/redis-6383.log 2>&1 &
redis-server /etc/redis/redis-6384.conf > /etc/redis/redis-6384.log 2>&1 &
#創建redis集群
redis-trib.rb create --replicas 1 10.0.1.37:6379 10.0.1.37:6380 10.0.1.37:6381 10.0.1.37:6382 10.0.1.37:6383 10.0.1.37:6384

#--replicas 1 表示每個主節點下有一個從節點
*** ERROR: Invalid configuration for cluster creation.
*** Redis Cluster requires at least 3 master nodes.
*** This is not possible with 3 nodes and 1 replicas per node.
*** At least 6 nodes are required.

#redis集群最少3個master-slave對應

#redis-info信息
以一種易於解釋(parse)且易於閱讀的格式,返回關於 Redis 服務器的各種信息和統計數值。
通過給定可選的參數 section ,可以讓命令只返回某一部分的信息:
server : 一般 Redis 服務器信息,包含以下域:
redis_version : Redis 服務器版本
redis_git_sha1 : Git SHA1
redis_git_dirty : Git dirty flag
os : Redis 服務器的宿主操作系統
arch_bits : 架構(32 或 64 位)
multiplexing_api : Redis 所使用的事件處理機制
gcc_version : 編譯 Redis 時所使用的 GCC 版本
process_id : 服務器進程的 PID
run_id : Redis 服務器的隨機標識符(用於 Sentinel 和集群)
tcp_port : TCP/IP 監聽端口
uptime_in_seconds : 自 Redis 服務器啟動以來,經過的秒數
uptime_in_days : 自 Redis 服務器啟動以來,經過的天數
lru_clock : 以分鐘為單位進行自增的時鐘,用於 LRU 管理
clients : 已連接客戶端信息,包含以下域:
connected_clients : 已連接客戶端的數量(不包括通過從屬服務器連接的客戶端)
client_longest_output_list : 當前連接的客戶端當中,最長的輸出列表
client_longest_input_buf : 當前連接的客戶端當中,最大輸入緩存
blocked_clients : 正在等待阻塞命令(BLPOP、BRPOP、BRPOPLPUSH)的客戶端的數量
memory : 內存信息,包含以下域:
used_memory : 由 Redis 分配器分配的內存總量,以字節(byte)為單位
used_memory_human : 以人類可讀的格式返回 Redis 分配的內存總量
used_memory_rss : 從操作系統的角度,返回 Redis 已分配的內存總量(俗稱常駐集大小)。這個值和 top 、 ps 等命令的輸出一致。
used_memory_peak : Redis 的內存消耗峰值(以字節為單位)
used_memory_peak_human : 以人類可讀的格式返回 Redis 的內存消耗峰值
used_memory_lua : Lua 引擎所使用的內存大小(以字節為單位)
mem_fragmentation_ratio : used_memory_rss 和 used_memory 之間的比率
mem_allocator : 在編譯時指定的, Redis 所使用的內存分配器。可以是 libc 、 jemalloc 或者 tcmalloc 。
在理想情況下, used_memory_rss 的值應該只比 used_memory 稍微高一點兒。
當 rss > used ,且兩者的值相差較大時,表示存在(內部或外部的)內存碎片。
內存碎片的比率可以通過 mem_fragmentation_ratio 的值看出。
當 used > rss 時,表示 Redis 的部分內存被操作系統換出到交換空間了,在這種情況下,操作可能會產生明顯的延遲。
Because Redis does not have control over how its allocations are mapped to memory pages, high used_memory_rss is often the result of a spike in memory usage.
當 Redis 釋放內存時,分配器可能會,也可能不會,將內存返還給操作系統。
如果 Redis 釋放了內存,卻沒有將內存返還給操作系統,那麽 used_memory 的值可能和操作系統顯示的 Redis 內存占用並不一致。
查看 used_memory_peak 的值可以驗證這種情況是否發生。
persistence : RDB 和 AOF 的相關信息
stats : 一般統計信息
replication : 主/從復制信息
cpu : CPU 計算量統計信息
commandstats : Redis 命令統計信息
cluster : Redis 集群信息
keyspace : 數據庫相關的統計信息
除上面給出的這些值以外,參數還可以是下面這兩個:
all : 返回所有信息
default : 返回默認選擇的信息
當不帶參數直接調用 INFO 命令時,使用 default 作為默認參數。
不同版本的 Redis 可能對返回的一些域進行了增加或刪減。
因此,一個健壯的客戶端程序在對 INFO 命令的輸出進行分析時,應該能夠跳過不認識的域,並且妥善地處理丟失不見的域。
性能相關的數據指標
通過Redis-cli命令行界面訪問到Redis服務器,然後使用info命令獲取所有與Redis服務相關的信息。通過這些信息來分析文章後面提到的一些性能指標。
info命令輸出的數據可分為10個類別,分別是:
server
clients
memory
persistence
stats
replication
cpu
commandstats
cluster
keyspace
這篇主要介紹比較重要的2部分性能指標memory和stats。
需要註意的是info命令返回的信息,並沒有命令響應延遲相關的數據信息,所以後面會詳細介紹怎麽獲取與延遲相關的數據指標。
倘若你覺得info輸出的信息太多並且雜亂無章,可以指定info命令的參數來獲取單個分類下的數據。比如輸入info memory命令,會只返回與內存相關的數據。
為了快速定位並解決性能問題,這裏選擇5個關鍵性的數據指標,它包含了大多數人在使用Redis上會經常碰到的性能問題。
內存使用率used_memory
上圖中used_memory 字段數據表示的是:由Redis分配器分配的內存總量,以字節(byte)為單位。 其中used_memory_human上的數據和used_memory是一樣的值,它以M為單位顯示,僅為了方便閱讀。
used_memory是Redis使用的內存總量,它包含了實際緩存占用的內存和Redis自身運行所占用的內存(如元數據、lua)。它是由Redis使用內存分配器分配的內存,所以這個數據並沒有把內存碎片浪費掉的內存給統計進去。
其他字段代表的含義,都以字節為單位:
used_memory_rss:從操作系統上顯示已經分配的內存總量。
mem_fragmentation_ratio: 內存碎片率。
used_memory_lua: Lua腳本引擎所使用的內存大小。
mem_allocator: 在編譯時指定的Redis使用的內存分配器,可以是libc、jemalloc、tcmalloc。
因內存交換引起的性能問題
內存使用率是Redis服務最關鍵的一部分。如果一個Redis實例的內存使用率超過可用最大內存 (used_memory > 可用最大內存),那麽操作系統開始進行內存與swap空間交換,把內存中舊的或不再使用的內容寫入硬盤上(硬盤上的這塊空間叫Swap分區),以便騰出新的物理內存給新頁或活動頁(page)使用。
在硬盤上進行讀寫操作要比在內存上進行讀寫操作,時間上慢了近5個數量級,內存是0.1μs單位、而硬盤是10ms。如果Redis進程上發生內存交換,那麽Redis和依賴Redis上數據的應用會受到嚴重的性能影響。 通過查看used_memory指標可知道Redis正在使用的內存情況,如果used_memory>可用最大內存,那就說明Redis實例正在進行內存交換或者已經內存交換完畢。管理員根據這個情況,執行相對應的應急措施。
跟蹤內存使用率
若是在使用Redis期間沒有開啟rdb快照或aof持久化策略,那麽緩存數據在Redis崩潰時就有丟失的危險。因為當Redis內存使用率超過可用內存的95%時,部分數據開始在內存與swap空間來回交換,這時就可能有丟失數據的危險。
當開啟並觸發快照功能時,Redis會fork一個子進程把當前內存中的數據完全復制一份寫入到硬盤上。因此若是當前使用內存超過可用內存的45%時觸發快照功能,那麽此時進行的內存交換會變的非常危險(可能會丟失數據)。 倘若在這個時候實例上有大量頻繁的更新操作,問題會變得更加嚴重。
通過減少Redis的內存占用率,來避免這樣的問題,或者使用下面的技巧來避免內存交換發生:
假如緩存數據小於4GB,就使用32位的Redis實例。因為32位實例上的指針大小只有64位的一半,它的內存空間占用空間會更少些。 這有一個壞處就是,假設物理內存超過4GB,那麽32位實例能使用的內存仍然會被限制在4GB以下。 要是實例同時也共享給其他一些應用使用的話,那可能需要更高效的64位Redis實例,這種情況下切換到32位是不可取的。 不管使用哪種方式,Redis的dump文件在32位和64位之間是互相兼容的, 因此倘若有減少占用內存空間的需求,可以嘗試先使用32位,後面再切換到64位上。
盡可能的使用Hash數據結構。因為Redis在儲存小於100個字段的Hash結構上,其存儲效率是非常高的。所以在不需要集合(set)操作或list的push/pop操作的時候,盡可能的使用Hash結構。比如,在一個web應用程序中,需要存儲一個對象表示用戶信息,使用單個key表示一個用戶,其每個屬性存儲在Hash的字段裏,這樣要比給每個屬性單獨設置一個key-value要高效的多。 通常情況下倘若有數據使用string結構,用多個key存儲時,那麽應該轉換成單key多字段的Hash結構。 如上述例子中介紹的Hash結構應包含,單個對象的屬性或者單個用戶各種各樣的資料。Hash結構的操作命令是HSET(key, fields, value)和HGET(key, field),使用它可以存儲或從Hash中取出指定的字段。
設置key的過期時間。一個減少內存使用率的簡單方法就是,每當存儲對象時確保設置key的過期時間。倘若key在明確的時間周期內使用或者舊key不大可能被使用時,就可以用Redis過期時間命令(expire,expireat, pexpire, pexpireat)去設置過期時間,這樣Redis會在key過期時自動刪除key。 假如你知道每秒鐘有多少個新key-value被創建,那可以調整key的存活時間,並指定閥值去限制Redis使用的最大內存。
回收key。在Redis配置文件中(一般叫Redis.conf),通過設置“maxmemory”屬性的值可以限制Redis最大使用的內存,修改後重啟實例生效。 也可以使用客戶端命令config set maxmemory 去修改值,這個命令是立即生效的,但會在重啟後會失效,需要使用config rewrite命令去刷新配置文件。 若是啟用了Redis快照功能,應該設置“maxmemory”值為系統可使用內存的45%,因為快照時需要一倍的內存來復制整個數據集,也就是說如果當前已使用45%,在快照期間會變成95%(45%+45%+5%),其中5%是預留給其他的開銷。 如果沒開啟快照功能,maxmemory最高能設置為系統可用內存的95%。
當內存使用達到設置的最大閥值時,需要選擇一種key的回收策略,可在Redis.conf配置文件中修改“maxmemory-policy”屬性值。 若是Redis數據集中的key都設置了過期時間,那麽“volatile-ttl”策略是比較好的選擇。但如果key在達到最大內存限制時沒能夠迅速過期,或者根本沒有設置過期時間。那麽設置為“allkeys-lru”值比較合適,它允許Redis從整個數據集中挑選最近最少使用的key進行刪除(LRU淘汰算法)。Redis還提供了一些其他淘汰策略,如下:
volatile-lru:使用LRU算法從已設置過期時間的數據集合中淘汰數據。
volatile-ttl:從已設置過期時間的數據集合中挑選即將過期的數據淘汰。
volatile-random:從已設置過期時間的數據集合中隨機挑選數據淘汰。
allkeys-lru:使用LRU算法從所有數據集合中淘汰數據。
allkeys-random:從數據集合中任意選擇數據淘汰
no-enviction:禁止淘汰數據。
通過設置maxmemory為系統可用內存的45%或95%(取決於持久化策略)和設置“maxmemory-policy”為“volatile-ttl”或“allkeys-lru”(取決於過期設置),可以比較準確的限制Redis最大內存使用率,在絕大多數場景下使用這2種方式可確保Redis不會進行內存交換。倘若你擔心由於限制了內存使用率導致丟失數據的話,可以設置noneviction值禁止淘汰數據。
命令處理數total_commands_processed
在info信息裏的total_commands_processed字段顯示了Redis服務處理命令的總數,其命令都是從一個或多個Redis客戶端請求過來的。Redis每時每刻都在處理從客戶端請求過來的命令,它可以是Redis提供的140種命令的任意一個。 total_commands_processed字段的值是遞增的,比如Redis服務分別處理了client_x請求過來的2個命令和client_y請求過來的3個命令,那麽命令處理總數(total_commands_processed)就會加上5。
分析命令處理總數,診斷響應延遲。
在Redis實例中,跟蹤命令處理總數是解決響應延遲問題最關鍵的部分,因為Redis是個單線程模型,客戶端過來的命令是按照順序執行的。比較常見的延遲是帶寬,通過千兆網卡的延遲大約有200μs。倘若明顯看到命令的響應時間變慢,延遲高於200μs,那可能是Redis命令隊列裏等待處理的命令數量比較多。 如上所述,延遲時間增加導致響應時間變慢可能是由於一個或多個慢命令引起的,這時可以看到每秒命令處理數在明顯下降,甚至於後面的命令完全被阻塞,導致Redis性能降低。要分析解決這個性能問題,需要跟蹤命令處理數的數量和延遲時間。
比如可以寫個腳本,定期記錄total_commands_processed的值。當客戶端明顯發現響應時間過慢時,可以通過記錄的total_commands_processed歷史數據值來判斷命理處理總數是上升趨勢還是下降趨勢,以便排查問題。
使用命令處理總數解決延遲時間增加。
通過與記錄的歷史數據比較得知,命令處理總數確實是處於上升或下降狀態,那麽可能是有2個原因引起的:
命令隊列裏的命令數量過多,後面命令一直在等待中。
幾個慢命令阻塞Redis。
下面有三個辦法可以解決,因上面2條原因引起的響應延遲問題。
使用多參數命令:若是客戶端在很短的時間內發送大量的命令過來,會發現響應時間明顯變慢,這由於後面命令一直在等待隊列中前面大量命令執行完畢。有個方法可以改善延遲問題,就是通過單命令多參數的形式取代多命令單參數的形式。舉例來說,循環使用LSET命令去添加1000個元素到list結構中,是性能比較差的一種方式,更好的做法是在客戶端創建一個1000元素的列表,用單個命令LPUSH或RPUSH,通過多參數構造形式一次性把1000個元素發送的Redis服務上。下面的表格是Redis的一些操作命令,有單個參數命令和支持多個參數的命令,通過這些命令可盡量減少使用多命令的次數。
管道命令:另一個減少多命令的方法是使用管道(pipeline),把幾個命令合並一起執行,從而減少因網絡開銷引起的延遲問題。因為10個命令單獨發送到服務端會引起10次網絡延遲開銷,使用管道會一次性把執行結果返回,僅需要一次網絡延遲開銷。Redis本身支持管道命令,大多數客戶端也支持,倘若當前實例延遲很明顯,那麽使用管道去降低延遲是非常有效的。
避免操作大集合的慢命令:如果命令處理頻率過低導致延遲時間增加,這可能是因為使用了高時間復雜度的命令操作導致,這意味著每個命令從集合中獲取數據的時間增大。 所以減少使用高時間復雜的命令,能顯著的提高的Redis的性能。下面的表格是高時間復雜度命令的列表,其詳細描述了命令的屬性,有這助於高效合理的、最優化的使用這些命令(如果不得不使用的話),以提高Redis性能。
延遲時間
Redis的延遲數據是無法從info信息中獲取的。倘若想要查看延遲時間,可以用 Redis-cli工具加--latency參數運行,如:
Redis-cli --latency -h 127.0.0.1 -p 6379
其host和port是Redis實例的ip及端口。由於當前服務器不同的運行情況,延遲時間可能有所誤差,通常1G網卡的延遲時間是200μs。
以毫秒為單位測量Redis的響應延遲時間,樓主本機的延遲是300μs:
跟蹤Redis延遲性能
Redis之所以這麽流行的主要原因之一就是低延遲特性帶來的高性能,所以說解決延遲問題是提高Redis性能最直接的辦法。拿1G帶寬來說,若是延遲時間遠高於200μs,那明顯是出現了性能問題。 雖然在服務器上會有一些慢的IO操作,但Redis是單核接受所有客戶端的請求,所有請求是按良好的順序排隊執行。因此若是一個客戶端發過來的命令是個慢操作,那麽其他所有請求必須等待它完成後才能繼續執行。
使用延遲命令提高性能
一旦確定延遲時間是個性能問題後,這裏有幾個辦法可以用來分析解決性能問題。
1. 使用slowlog查出引發延遲的慢命令:Redis中的slowlog命令可以讓我們快速定位到那些超出指定執行時間的慢命令,默認情況下命令若是執行時間超過10ms就會被記錄到日誌。slowlog只會記錄其命令執行的時間,不包含io往返操作,也不記錄單由網絡延遲引起的響應慢。通常1gb帶寬的網絡延遲,預期在200μs左右,倘若一個命令僅執行時間就超過10ms,那比網絡延遲慢了近50倍。 想要查看所有執行時間比較慢的命令,可以通過使用Redis-cli工具,輸入slowlog get命令查看,返回結果的第三個字段以微妙位單位顯示命令的執行時間。假如只需要查看最後10個慢命令,輸入slowlog get 10即可。 關於怎麽定位到是由慢命令引起的延遲問題,可查看total_commands_processed介紹章節。
圖中字段分別意思是:
1=日誌的唯一標識符
2=被記錄命令的執行時間點,以 UNIX 時間戳格式表示
3=查詢執行時間,以微秒為單位。例子中命令使用54毫秒。
4= 執行的命令,以數組的形式排列。完整命令是config get *。
倘若你想自定義慢命令的標準,可以調整觸發日誌記錄慢命令的閥值。若是很少或沒有命令超過10ms,想降低記錄的閥值,比如5毫秒,可在Redis-cli工具中輸入下面的命令配置:
config set slowlog-log-slower-than 5000
也可以在Redis.config配置文件中設置,以微妙位單位。
2.監控客戶端的連接:因為Redis是單線程模型(只能使用單核),來處理所有客戶端的請求, 但由於客戶端連接數的增長,處理請求的線程資源開始降低分配給單個客戶端連接的處理時間,這時每個客戶端需要花費更多的時間去等待Redis共享服務的響應。這種情況下監控客戶端連接數是非常重要的,因為客戶端創建連接數的數量可能超出預期的數量,也可能是客戶端端沒有有效的釋放連接。在Redis-cli工具中輸入info clients可以查看到當前實例的所有客戶端連接信息。如下圖,第一個字段(connected_clients)顯示當前實例客戶端連接的總數:
Redis默認允許客戶端連接的最大數量是10000。若是看到連接數超過5000以上,那可能會影響Redis的性能。倘若一些或大部分客戶端發送大量的命令過來,這個數字會低的多。
3.限制客戶端連接數:自Redis2.6以後,允許使用者在配置文件(Redis.conf)maxclients屬性上修改客戶端連接的最大數,也可以通過在Redis-cli工具上輸入config set maxclients 去設置最大連接數。根據連接數負載的情況,這個數字應該設置為預期連接數峰值的110%到150之間,若是連接數超出這個數字後,Redis會拒絕並立刻關閉新來的連接。通過設置最大連接數來限制非預期數量的連接數增長,是非常重要的。另外,新連接嘗試失敗會返回一個錯誤消息,這可以讓客戶端知道,Redis此時有非預期數量的連接數,以便執行對應的處理措施。 上述二種做法對控制連接數的數量和持續保持Redis的性能最優是非常重要的,
4.加強內存管理:較少的內存會引起Redis延遲時間增加。如果Redis占用內存超出系統可用內存,操作系統會把Redis進程的一部分數據,從物理內存交換到硬盤上,內存交換會明顯的增加延遲時間。關於怎麽監控和減少內存使用,可查看used_memory介紹章節。
5. 性能數據指標:
分析解決Redis性能問題,通常需要把延遲時間的數據變化與其他性能指標的變化相關聯起來。命令處理總數下降的發生可能是由慢命令阻塞了整個系統,但如果命令處理總數的增加,同時內存使用率也增加,那麽就可能是由於內存交換引起的性能問題。對於這種性能指標相關聯的分析,需要從歷史數據上來觀察到數據指標的重要變化,此外還可以觀察到單個性能指標相關聯的所有其他性能指標信息。這些數據可以在Redis上收集,周期性的調用內容為Redis info的腳本,然後分析輸出的信息,記錄到日誌文件中。當延遲發生變化時,用日誌文件配合其他數據指標,把數據串聯起來排查定位問題。
內存碎片率
info信息中的mem_fragmentation_ratio給出了內存碎片率的數據指標,它是由操系統分配的內存除以Redis分配的內存得出:
used_memory和used_memory_rss數字都包含的內存分配有:
用戶定義的數據:內存被用來存儲key-value值。
內部開銷: 存儲內部Redis信息用來表示不同的數據類型。
used_memory_rss的rss是Resident Set Size的縮寫,表示該進程所占物理內存的大小,是操作系統分配給Redis實例的內存大小。除了用戶定義的數據和內部開銷以外,used_memory_rss指標還包含了內存碎片的開銷,內存碎片是由操作系統低效的分配/回收物理內存導致的。
操作系統負責分配物理內存給各個應用進程,Redis使用的內存與物理內存的映射是由操作系統上虛擬內存管理分配器完成的。
舉個例子來說,Redis需要分配連續內存塊來存儲1G的數據集,這樣的話更有利,但可能物理內存上沒有超過1G的連續內存塊,那操作系統就不得不使用多個不連續的小內存塊來分配並存儲這1G數據,也就導致內存碎片的產生。
內存分配器另一個復雜的層面是,它經常會預先分配一些內存塊給引用,這樣做會使加快應用程序的運行。
理解資源性能
跟蹤內存碎片率對理解Redis實例的資源性能是非常重要的。內存碎片率稍大於1是合理的,這個值表示內存碎片率比較低,也說明redis沒有發生內存交換。但如果內存碎片率超過1.5,那就說明Redis消耗了實際需要物理內存的150%,其中50%是內存碎片率。若是內存碎片率低於1的話,說明Redis內存分配超出了物理內存,操作系統正在進行內存交換。內存交換會引起非常明顯的響應延遲,可查看used_memory介紹章節。
上圖中的0.99即99%。
用內存碎片率預測性能問題
倘若內存碎片率超過了1.5,那可能是操作系統或Redis實例中內存管理變差的表現。下面有3種方法解決內存管理變差的問題,並提高Redis性能:
1. 重啟Redis服務器:如果內存碎片率超過1.5,重啟Redis服務器可以讓額外產生的內存碎片失效並重新作為新內存來使用,使操作系統恢復高效的內存管理。額外碎片的產生是由於Redis釋放了內存塊,但內存分配器並沒有返回內存給操作系統,這個內存分配器是在編譯時指定的,可以是libc、jemalloc或者tcmalloc。 通過比較used_memory_peak, used_memory_rss和used_memory_metrics的數據指標值可以檢查額外內存碎片的占用。從名字上可以看出,used_memory_peak是過去Redis內存使用的峰值,而不是當前使用內存的值。如果used_memory_peak和used_memory_rss的值大致上相等,而且二者明顯超過了used_memory值,這說明額外的內存碎片正在產生。 在Redis-cli工具上輸入info memory可以查看上面三個指標的信息:
在重啟服務器之前,需要在Redis-cli工具上輸入shutdown save命令,意思是強制讓Redis數據庫執行保存操作並關閉Redis服務,這樣做能保證在執行Redis關閉時不丟失任何數據。 在重啟後,Redis會從硬盤上加載持久化的文件,以確保數據集持續可用。
2.限制內存交換: 如果內存碎片率低於1,Redis實例可能會把部分數據交換到硬盤上。內存交換會嚴重影響Redis的性能,所以應該增加可用物理內存或減少實Redis內存占用。 可查看used_memory章節的優化建議。
3.修改內存分配器:
Redis支持glibc’s malloc、jemalloc11、tcmalloc幾種不同的內存分配器,每個分配器在內存分配和碎片上都有不同的實現。不建議普通管理員修改Redis默認內存分配器,因為這需要完全理解這幾種內存分配器的差異,也要重新編譯Redis。這個方法更多的是讓其了解Redis內存分配器所做的工作,當然也是改善內存碎片問題的一種辦法。
回收key
info信息中的evicted_keys字段顯示的是,因為maxmemory限制導致key被回收刪除的數量。關於maxmemory的介紹見前面章節,回收key的情況只會發生在設置maxmemory值後,不設置會發生內存交換。 當Redis由於內存壓力需要回收一個key時,Redis首先考慮的不是回收最舊的數據,而是在最近最少使用的key或即將過期的key中隨機選擇一個key,從數據集中刪除。
這可以在配置文件中設置maxmemory-policy值為“volatile-lru”或“volatile-ttl”,來確定Redis是使用lru策略還是過期時間策略。 倘若所有的key都有明確的過期時間,那過期時間回收策略是比較合適的。若是沒有設置key的過期時間或者說沒有足夠的過期key,那設置lru策略是比較合理的,這可以回收key而不用考慮其過期狀態。
根據key回收定位性能問題
跟蹤key回收是非常重要的,因為通過回收key,可以保證合理分配Redis有限的內存資源。如果evicted_keys值經常超過0,那應該會看到客戶端命令響應延遲時間增加,因為Redis不但要處理客戶端過來的命令請求,還要頻繁的回收滿足條件的key。
需要註意的是,回收key對性能的影響遠沒有內存交換嚴重,若是在強制內存交換和設置回收策略做一個選擇的話,選擇設置回收策略是比較合理的,因為把內存數據交換到硬盤上對性能影響非常大(見前面章節)。
減少回收key以提升性能
減少回收key的數量是提升Redis性能的直接辦法,下面有2種方法可以減少回收key的數量:
1.增加內存限制:倘若開啟快照功能,maxmemory需要設置成物理內存的45%,這幾乎不會有引發內存交換的危險。若是沒有開啟快照功能,設置系統可用內存的95%是比較合理的,具體參考前面的快照和maxmemory限制章節。如果maxmemory的設置是低於45%或95%(視持久化策略),通過增加maxmemory的值能讓Redis在內存中存儲更多的key,這能顯著減少回收key的數量。 若是maxmemory已經設置為推薦的閥值後,增加maxmemory限制不但無法提升性能,反而會引發內存交換,導致延遲增加、性能降低。 maxmemory的值可以在Redis-cli工具上輸入config set maxmemory命令來設置。
需要註意的是,這個設置是立即生效的,但重啟後丟失,需要永久化保存的話,再輸入config rewrite命令會把內存中的新配置刷新到配置文件中。
2.對實例進行分片:分片是把數據分割成合適大小,分別存放在不同的Redis實例上,每一個實例都包含整個數據集的一部分。通過分片可以把很多服務器聯合起來存儲數據,相當於增加總的物理內存,使其在沒有內存交換和回收key的策略下也能存儲更多的key。假如有一個非常大的數據集,maxmemory已經設置,實際內存使用也已經超過了推薦設置的閥值,那通過數據分片能明顯減少key的回收,從而提高Redis的性能。 分片的實現有很多種方法,下面是Redis實現分片的幾種常見方式:
a. Hash分片:一個比較簡單的方法實現,通過Hash函數計算出key的Hash值,然後值所在範圍對應特定的Redis實例。
b. 代理分片:客戶端把請求發送到代理上,代理通過分片配置表選擇對應的Redis實例。 如Twitter的Twemproxy,豌豆莢的codis。
c. 一致性Hash分片: 參見前面博客《一致性Hash分片詳解》
d. 虛擬桶分片:參見前面博客《虛擬桶分詳解》
總結
對於開發者來說,Redis是個速度非常快的key-value內存數據庫,並提供了方便的API接口。為了最好最優的使用Redis,需要理解哪些因素能影響到Redis性能,哪些數據指標能幫助我們避免性能陷阱。 通過本篇,能理解Redis中的重要性能指標,怎麽查看,更重要的是怎麽利用這些數據排查解決Redis性能問題。
Redis的數據回寫機制
Redis的數據回寫機制分同步和異步兩種,
同步回寫即SAVE命令,主進程直接向磁盤回寫數據。在數據大的情況下會導致系統假死很長時間,所以一般不是推薦的。
異步回寫即BGSAVE命令,主進程fork後,復制自身並通過這個新的進程回寫磁盤,回寫結束後新進程自行關閉。由於這樣做不需要主進程阻塞,系統不會假死,一般默認會采用這個方法。
個人感覺方法2采用fork主進程的方式很拙劣,但似乎是唯一的方法。內存中的熱數據隨時可能修改,要在磁盤上保存某個時間的內存鏡像必須要凍結。凍結就會導致假死。 fork一個新的進程之後等於復制了當時的一個內存鏡像,這樣主進程上就不需要凍結,只要子進程上操作就可以了。
在小內存的進程上做一個fork,不需要太多資源,但當這個進程的內存空間以G為單位時,fork就成為一件很恐怖的操作。何況在16G內存的主機上fork 14G內存的進程呢?肯定會報內存無法分配的。更可氣的是,越是改動頻繁的主機上fork也越頻繁,fork操作本身的代價恐怕也不會比假死好多少。
找到原因之後,直接修改/etc/sysctl.conf內核參數vm.overcommit_memory= 1
sysctl -p
Linux內核會根據參數vm.overcommit_memory參數的設置決定是否放行。
如果 vm.overcommit_memory = 1,直接放行
vm.overcommit_memory = 0:則比較 此次請求分配的虛擬內存大小和系統當前空閑的物理內存加上swap,決定是否放行。
vm.overcommit_memory= 2:則會比較進程所有已分配的虛擬內存加上此次請求分配的虛擬內存和系統當前的空閑物理內存加上swap,決定是否放行。

本文出自 “運維筆記” 博客,請務必保留此出處http://phospherus.blog.51cto.com/7824598/1930102

Redis部署及參數筆記