您好,登錄后才能下訂單哦!
首先,并不是說redis是內存應用就完全沒性能問題,用的不好,還是會出現各種狀況,例如RDB頻繁,碎片太多等.
性能分析
info信息:
在redis-cli進入登錄界面后,輸入info all,或者redis-cli -h ${ip} -p ${post} -a "${pass}" -c info all,通常我們只輸入info就夠了,是簡介模式的意思,info all是詳細模式
之后,就會獲取所有與Redis服務相關的實時性能信息,類似于linux命令top那樣的東西.
info命令輸出的數據可分為10個類別,分別是:
server
clients
memory
persistence
stats
replication
cpu
commandstats
cluster
keyspace
下面展開解析一些重點信息.
server?部分:
redis_version?:???? Redis 服務器版本,不同版本會有些功能和命令不同
arch_bits?: ????架構(32 或 64 位),某些情況,容易被忽略的坑
tcp_port?: ????TCP/IP 監聽端口,確認你操作的對不對
uptime_in_seconds?: ????自 Redis 服務器啟動以來,經過的秒數,可以確認有沒有被重啟過
uptime_in_days?:???? 自 Redis 服務器啟動以來,經過的天數,可以確認有沒有被重啟過
clients?部分:
connected_clients?: ????已連接客戶端的數量(不包括通過從屬服務器連接的客戶端)
client_longest_output_list?: ????當前連接的客戶端當中,最長的輸出列表
client_longest_input_buf?: ????當前連接的客戶端當中,最大輸入緩存
blocked_clients?: ????正在等待阻塞命令(BLPOP、BRPOP、BRPOPLPUSH)的客戶端的數量
memory部分:
maxmemory/maxmemory_human:??? 配置文件redis.conf限制的可分配的最大內存總量,當超過之后,就會觸發LRU刪除舊數據.
used_memory/used_memory_human:??? 當前redis-server實際使用的內存總量,如果used_memory > maxmemory ,那么操作系統開始進行內存與swap空間交換,以便騰出新的物理內存給新頁或活動頁(page)使用,那是有多糟糕大家可以想得到.
used_memory_rss/used_memory_rss_human:????從操作系統上顯示已經分配的內存總量,也就是這個redis-server占用的系統物理內存實際值,比used_memory多出來的就可能是碎片.
mem_fragmentation_ratio:????內存碎片率,內存碎片率稍大于1是合理的,說明redis沒有發生內存交換,如果內存碎片率超過1.5,那就說明Redis消耗了實際需要物理內存的150%,其中50%是內存碎片率。若是內存碎片率低于1的話,說明Redis內存分配超出了物理內存,操作系統正在進行內存交換。內存交換會引起非常明顯的響應延遲.
????下面是計算公式:
????????
????當碎片率出現問題,有3種方法解決內存管理變差的問題,提高redis性能:
????????1. 重啟Redis服務器:如果內存碎片率超過1.5,重啟Redis服務器可以讓額外產生的內存碎片失效并重新作為新內存來使用,使操作系統恢復高效的內存管理。
????????2.限制內存交換:?如果內存碎片率低于1,Redis實例可能會把部分數據交換到硬盤上。內存交換會嚴重影響Redis的性能,所以應該增加可用物理內存或減少實Redis內存占用
????????3.修改內存分配器:
Redis支持glibc’s
malloc、jemalloc11、tcmalloc幾種不同的內存分配器,每個分配器在內存分配和碎片上都有不同的實現。不建議普通管理員修改Redis默認內存分配器,因為這需要完全理解這幾種內存分配器的差異,也要重新編譯Redis。
used_memory_lua:????Lua腳本引擎所使用的內存大小。redis默認允許使用lua腳本,不過太多了的話就占用了可用內存
mem_allocator:????在編譯時指定的Redis使用的內存分配器,可以是libc、jemalloc、tcmalloc.
persistence?部分:
RDB信息,RDB的操作要用到bgsave命令,是比較耗費資源的持久化操作,而且不是實時的,容易造成宕機數據消失,如果內存容量滿了,不能做bgsave操作的話,隱患會很大.
rdb_changes_since_last_save?: ????距離最近一次成功創建持久化文件之后,經過了多少秒。持久化是需要占用資源的,在高負載下需要盡量避免持久化的影響,下列參數均有參考價值.
rdb_bgsave_in_progress:????當前是否在進行bgsave操作。是為1
rdb_last_save_time?:???? 最近一次成功創建 RDB 文件的 UNIX 時間戳。
rdb_last_bgsave_time_sec?: ????記錄了最近一次創建 RDB 文件耗費的秒數。
rdb_last_bgsave_status:????上次保存的狀態
rdb_current_bgsave_time_sec?: ????如果服務器正在創建 RDB 文件,那么這個域記錄的就是當前的創建操作已經耗費的秒數。
AOF信息,AOF是持續記錄命令到持久化文件的方法,比較節省資源,但是AOF存儲文件會沒有限制存儲,時間一長,或者操作太頻繁,那就會出現AOF文件過大,撐爆硬盤的事.而且,這個方法還是會定期bgsave操作.
aof_enabled:????AOF文件是否啟用
aof_rewrite_in_progress:????表示當前是否在進行寫入AOF文件操作
aof_last_rewrite_time_sec?: ????最近一次創建 AOF 文件耗費的時長。
aof_current_rewrite_time_sec?: ????如果服務器正在創建 AOF 文件,那么這個域記錄的就是當前的創建操作已經耗費的秒數。
aof_last_bgrewrite_status:????上次寫入狀態
aof_last_write_status:????上次寫入狀態
aof_base_size?: ????服務器啟動時或者 AOF 重寫最近一次執行之后,AOF 文件的大小。
aof_pending_bio_fsync?: ????后臺 I/O 隊列里面,等待執行的?fsync?調用數量。
aof_delayed_fsync?: ????被延遲的?fsync?調用數量。
stats部分:
total_commands_processed:????顯示了Redis服務處理命令的總數,且值是遞增的.因為Redis是個單線程模型,客戶端過來的命令是按照順序執行的,如果命令隊列里等待處理的命令數量比較多,命令的響應時間就變慢,甚至于后面的命令完全被阻塞,導致Redis性能降低.所以這個時候就需要記錄這個參數的值,是不是增長過快,導致了性能降低.
instantaneous_ops_per_sec?:???? 服務器每秒鐘執行的命令數量,同上,如果增長過快就有問題。
expired_keys?:???? 因為過期而被自動刪除的數據庫鍵數量,具有參考意義。
evicted_keys:????顯示因為maxmemory限制導致key被回收刪除的數量.根據配置文件中設置maxmemory-policy值來確定Redis是使用lru策略還是過期時間策略.如果是過期回收的,不會被記錄在這里,通常這個值不為0,那就要考慮增加內存限制,不然就會造成內存交換,輕則性能變差,重則丟數據.
latest_fork_usec:???? 最近一次?fork()?操作耗費的微秒數。fork()是很耗費資源的操作,所以要留意一下.
commandstats部分:
cmdstat_XXX:????記錄了各種不同類型的命令的執行統計信息,命令包含有讀有寫,分得比較細,其中calls代表命令執行的次數,usec代表命令耗費的 CPU 時間,usec_per_call代表每個命令耗費的平均 CPU 時間,單位為微秒.對于排錯有一定用途.
---------------------------------------------------------------------------------------------------
其他問題的分析方法:
查看redis的網絡延時:
Redis的延遲數據是無法從info信息中獲取的。倘若想要查看延遲時間,可以用 Redis-cli工具加--latency參數運行
redis-cli --latency -h 10.1.2.11 -p 6379
他將會持續掃描延遲時間,直到按ctrl+C退出,以毫秒為單位測量Redis的響應延遲時間,由于服務器不同的運行情況,延遲時間可能有所誤差,通常1G網卡的延遲時間是0.2毫秒,若延時值遠高于這個參考值,那明顯是有性能問題了。這個時候我們要考慮檢查一下網絡狀況.
查看redis的慢查詢:
Slow log 是 Redis 用來記錄查詢執行時間的日志系統。Redis中的slowlog命令可以讓我們快速定位到那些超出指定執行時間的慢命令,默認情況下會記錄執行時間超過10ms的記錄到日志,由參數slowlog-log-slower-than控制.最多記錄128條,由參數slowlog-max-len控制,超過就自動刪除.
通常這個默認參數夠用,也可以在線CONFIG SET修改參數slowlog-log-slower-than和slowlog-max-len來修改這個時間和限制條數。
通常1gb帶寬的網絡延遲,預期在0.2ms左右,倘若一個命令僅執行時間就超過10ms,那比網絡延遲慢了近50倍。可以通過使用Redis-cli工具,輸入slowlog get命令查看,返回結果的第三個字段以微妙位單位顯示命令的執行時間。假如只需要查看最后3個慢命令,輸入slowlog get 10即可。
127.0.0.1:6379>?slowlog?get?10 ????. ????. ????. 4)?1)?(integer)?215 ????2)?(integer)?1489099695 ????3)?(integer)?11983 ????4)?1)?"SADD" ???????2)?"USER_TOKEN_MAP51193" ???????3)?"qIzwZKBmBJozKprQgoTEI3Qo8QO2Fi!4" ?5)?1)?(integer)?214 ????2)?(integer)?1489087112 ????3)?(integer)?18002 ????4)?1)?"SADD" ???????2)?"USER_TOKEN_MAP51192" ???????3)?"Z3Hs!iTUNfweqvLLf!ptdchSV2JAOrrH" ?6)?1)?(integer)?213 ????2)?(integer)?1489069123 ????3)?(integer)?15407 ????4)?1)?"SADD" ???????2)?"USER_TOKEN_MAP51191" ???????3)?"S3rNzOBwUlaI3QfOK9dIITB6Bk7LIGYe"
1=日志的唯一標識符
2=被記錄命令的執行時間點,以 UNIX 時間戳格式表示
3=查詢執行時間,以微秒為單位。例子中命令使用11毫秒。
4= 執行的命令,以數組的形式排列。完整命令是拼在一起.
監控客戶端的連接:
因為Redis是單線程模型(只能使用單核),來處理所有客戶端的請求, 但由于客戶端連接數的增長,處理請求的線程資源開始降低分配給單個客戶端連接的處理時間,這時每個客戶端需要花費更多的時間去等待Redis共享服務的響應。
#查看客戶端連接狀態 127.0.0.1:6379>?info?clients #?Clients connected_clients:11 client_longest_output_list:0 client_biggest_input_buf:0 blocked_clients:0
第一個字段(connected_clients)顯示當前實例客戶端連接的總數,Redis默認允許客戶端連接的最大數量是10000。若是看到連接數超過5000以上,那可能會影響Redis的性能。倘若一些或大部分客戶端發送大量的命令過來,這個數字會低的多。
查當前客戶端狀態
#查看所有正在連接的客戶端狀態, 127.0.0.1:6379>?client?list id=821882?addr=10.25.138.2:60990?fd=8?name=?age=53838?idle=24?flags=N?db=0?sub=0?psub=0?multi=-1?qbuf=0?qbuf-free=0?obl=0?oll=0?omem=0?events=r?cmd=ping
這個看起來有點繞,因為和歷史數據是混在一起的:
addr:客戶端的地址和端口,包含當前連接和歷史連接
age:這個客戶端連進來的生命周期,也就是連進來之后的持續時間,單位是秒
idle:這個客戶端的空閑時間,也就是說這個時間內,客戶端沒有操作,單位是秒
db:操作的數據庫,redis默認有db0~db15可用選擇
cmd:客戶端最后一次使用的命令
也就是說,idle越少,那就代表這個客戶端剛剛操作,反則是歷史記錄而已.age越少就代表是剛剛建立的連接,越大則是歷史連接.而有些時候個別使用者用了scan或keys命令,會對數據量大的redis造成很大的負載壓力,所以需要特別關注.
特大key的統計:
在redis的單線程處理方式下,一些數據量比較大的key的操作明顯是會影響性能,所以必要時,我們要統計出來,交給開發來優化
#統計生產上比較大的key redis-cli?-h*?-a*?-p*?--bigkeys #查看某個key的持續時間 127.0.0.1:6379>?OBJECT?IDLETIME?key名字
--bigkeys信息解析:
1.該命令使用scan方式對key進行統計,所以使用時無需擔心對redis造成阻塞。
2.輸出大概分為兩部分,summary之上的部分,只是顯示了掃描的過程。summary部分給出了每種數據結構中最大的Key,所以下面部分更重要些。
3.統計出的最大key只有string類型是以字節長度為衡量標準的。list,set,zset等都是以元素個數作為衡量標準,不能說明其占的內存就一定多,需要另外計算。
得出最大key名字后,就去看看粗略大小,
#查看某個key序列化后的長度 debug?object?key
輸出的項的說明:
Value at:key的內存地址
refcount:引用次數
encoding:編碼類型
serializedlength:經過壓縮后的序列化長度,單位是 B, 也就是 Byte(字節),因為壓縮效果要看編碼類型,不一定反應內存中的大小,只是有參考價值.
lru_seconds_idle:空閑時間
終上所述,我們要關注的大key信息正是serializedlength的長度了.
另外還有一個工具[rdbtools],可以全面分析redis里面的key信息,但是要額外安裝,對于內網用戶不可謂不麻煩,因為不是系統自帶的功能,這里不詳細說明,請等待另一篇文章另外介紹.
數據持久化引發的延遲
Redis的數據持久化工作本身就會帶來延遲,需要根據數據的安全級別和性能要求制定合理的持久化策略:
1.AOF + fsync always的設置雖然能夠絕對確保數據安全,但每個操作都會觸發一次fsync,會對Redis的性能有比較明顯的影響
2.AOF + fsync every second是比較好的折中方案,每秒fsync一次
3.AOF + fsync never會提供AOF持久化方案下的最優性能,使用RDB持久化通常會提供比使用AOF更高的性能,但需要注意RDB的策略配置
4.每一次RDB快照和AOF
Rewrite都需要Redis主進程進行fork操作。fork操作本身可能會產生較高的耗時,與CPU和Redis占用的內存大小有關。根據具體的情況合理配置RDB快照和AOF
Rewrite時機,避免過于頻繁的fork帶來的延遲.
例如:Redis在fork子進程時需要將內存分頁表拷貝至子進程,以占用了24GB內存的Redis實例為例,共需要拷貝24GB / 4kB * 8 = 48MB的數據。在使用單Xeon 2.27Ghz的物理機上,這一fork操作耗時216ms。
可以通過INFO命令返回的latest_fork_usec字段查看上一次fork操作的耗時(微秒)。
Swap引發的延遲
當Linux將Redis所用的內存分頁移至swap空間時,將會阻塞Redis進程,導致Redis出現不正常的延遲。Swap通常在物理內存不足或一些進程在進行大量I/O操作時發生,應盡可能避免上述兩種情況的出現。
在/proc/redis進程號/smaps文件中會保存進程的swap記錄,通過查看這個文件,能夠判斷Redis的延遲是否由Swap產生。如果這個文件中記錄了較大的Swap size,則說明延遲很有可能是Swap造成的。
例子如下,可以看到當前swap的狀態時0KB,也就是沒用到swap,
#/proc/pid/smaps顯示了進程運行時的內存影響,系統的運行時庫(so),堆,棧信息均可在其中看到。 cat?/proc/`ps?aux?|grep?redis?|grep?-v?grep?|awk?'{print?$2}'`/smaps 00400000-00531000?r-xp?00000000?fc:02?805438521?/usr/local/bin/redis-server Size:???????????????1220?kB Rss:?????????????????924?kB Pss:?????????????????924?kB Shared_Clean:??????????0?kB Shared_Dirty:??????????0?kB Private_Clean:???????924?kB Private_Dirty:?????????0?kB Referenced:??????????924?kB Anonymous:?????????????0?kB AnonHugePages:?????????0?kB Shared_Hugetlb:????????0?kB Private_Hugetlb:???????0?kB Swap:??????????????????0?kB SwapPss:???????????????0?kB KernelPageSize:????????4?kB MMUPageSize:???????????4?kB Locked:????????????????0?kB
內存滿了怎么辦:
redis內存滿了,那確實是很麻煩的事,但是再麻煩也得處理啊,這就得從redis架構原理說起了.
首先我們要了解,redis的內存滿,并不代表是用了系統的100%內存,為什么這么說呢?我們都知道redis做持久化是save和bgsave,而常用的bgsave(也是默認)是fork一個進程,把內存copy一份再壓縮后存到硬盤成*.rdb文件.這里就涉及一個問題,你內存必須保證有和數據一樣大的空間才能做bgsave,那嚴格來說,只要你的內存超過系統內存的50%,那就可以被稱為redis內存滿了.
redis的持久化策略,上面只說了持久化會阻塞操作導致延時,而如果內存滿了,數據量的增加會讓持久化造成的延時會更嚴重,而且持久化失敗后是默認每分鐘重試一遍.
那么問題就來了,因為內存滿了,持久化失敗,然后一分鐘后再持久化,就造成了惡性循環,redis的性能直線下降.那怎么辦好呢?
更改持久化策略是個臨時解決方案,
可以先嘗試關閉持久化失敗導致的終止所有的客戶端write請求的選項,
config?set?stop-writes-on-bgsave-error?no
但是這個方法治標不治本,我們只是忽略了問題而已,
另一種解決方案就是直接把rdb持久化關閉掉:
config?set?save?""
為什么能解決,答案也很明顯,關閉了持久化,那就不會阻塞操作,那你redis的性能還是保證到了.但是又會引入新問題,沒了持久化,內存數據如果redis-server程序重啟或關閉就沒了,還是比較危險的.而且內存滿的問題還在,如果內存用到了系統內存100%,甚至觸發了系統的OOM,那就坑大了,因為內存被徹底清空,數據也都沒有了.也就是所謂的臨時解決辦法.
所以正確的做法是,在不阻塞操作之后,刪掉可以刪除的數據,再重新拉起持久化,然后準備擴容的工作.
占用內存看哪里,上面已經說了,但是內存滿了的定義,并不一定只是實際內存占用,碎片也是要包含在內的,例如:
#這種情況,肯定就是內存滿 used_memory_human:4.2G maxmemory_human:4.00G #但是這種情況,也是內存滿 used_memory_human:792.30M used_memory_rss_human:3.97G used_memory_peak_human:4.10G maxmemory_human:4.00G
因為內存碎片沒有被釋放,那它還是會占用內存空間,對于系統來說,碎片也是redis-server占用的內存,不是空閑的內存,那剩下的內存還是不足以用來bgsave.那怎么解決碎片呢?
在redis4.0之前,沒其他更好辦法,只能重啟redis-server,之后的新版本則新加了一個碎片回收參數,杜絕了這個問題.
而碎片問題其實影響頗大,因為正常情況下,這些用不了又確實占著內存的數據,會讓我們的redis浪費空間之余,還會額外造成內存滿的風險.所以也正如上面說的那樣,如果碎片率超過1.5之后,是該想想回收一下.
那確實需要保存內存數據怎么辦?只能忍痛割愛,把不必要的數據刪除掉,讓內存降到能做bgsave之后再重啟來回收碎片.要不,就是升級到4.0之后避免同類問題.
優化建議
系統優化
1.關閉Transparent huge pages
Transparent HugePages會讓內核khugepaged線程在運行時動態分配內存。在大部分linux發行版本中默認是啟用的,缺點是可能會造成內存在運行時的延遲分配,對于大內存應用并不友好,例如:oracle,redis等會占用大量內存的應用,所以建議關閉。
#關閉Transparent?HugePages,默認狀態是[always] echo?never?>?/sys/kernel/mm/transparent_hugepage/enabled
2.在物理機部署redis,這點不用多說了,虛擬機或者是docker都會有一定的延時,沒有必要為了好管理二浪費這些性能。
3.多用連接池,而不是頻繁斷開再連接,效果我想不言而喻。
4.客戶端進行的批量數據操作,應使用Pipeline特性在一次交互中完成。
行為優化
1.假如緩存數據小于4GB,可以選擇使用32位的Redis實例。因為32位實例上的指針大小只有64位的一半,它的內存空間占用空間會更少些。Redis的dump文件在32位和64位之間是互相兼容的, 因此倘若有減少占用內存空間的需求,可以嘗試先使用32位,后面再切換到64位上。
2.盡可能的使用Hash數據結構。因為Redis在儲存小于100個字段的Hash結構上,其存儲效率是非常高的。所以在不需要集合(set)操作或list的push/pop操作的時候,盡可能的使用Hash結構。Hash結構的操作命令是HSET(key, fields, value)和HGET(key, field),使用它可以存儲或從Hash中取出指定的字段。
3.盡量設置key的過期時間。一個減少內存使用率的簡單方法就是,每當存儲對象時確保設置key的過期時間。倘若key在明確的時間周期內使用或者舊key不大可能被使用時,就可以用Redis過期時間命令(expire,expireat,
pexpire,
pexpireat)去設置過期時間,這樣Redis會在key過期時自動刪除key.用ttl命令可以查詢過期時間,單位是秒,顯示-2代表key不存在,顯示-1代表沒有設置超時時間(也就是永久的).
4.使用多參數命令:若是客戶端在很短的時間內發送大量的命令過來,會發現響應時間明顯變慢,這由于后面命令一直在等待隊列中前面大量命令執行完畢。舉例來說,循環使用LSET命令去添加1000個元素到list結構中,是性能比較差的一種方式,更好的做法是在客戶端創建一個1000元素的列表,用單個命令LPUSH或RPUSH,通過多參數構造形式一次性把1000個元素發送的Redis服務上。
5.管道命令:另一個減少多命令的方法是使用管道(pipeline),把幾個命令合并一起執行,從而減少因網絡開銷引起的延遲問題。因為10個命令單獨發送到服務端會引起10次網絡延遲開銷,使用管道會一次性把執行結果返回,僅需要一次網絡延遲開銷。Redis本身支持管道命令,大多數客戶端也支持,倘若當前實例延遲很明顯,那么使用管道去降低延遲是非常有效的。
6.避免操作大集合的慢命令:如果命令處理頻率過低導致延遲時間增加,這可能是因為使用了高時間復雜度的命令操作導致,這意味著每個命令從集合中獲取數據的時間增大。 所以減少使用高時間復雜的命令,能顯著的提高的Redis的性能。
7.限制客戶端連接數:自Redis2.6以后,允許使用者在配置文件(Redis.conf)maxclients屬性上修改客戶端連接的最大數,也可以通過在Redis-cli工具上輸入config set maxclients 去設置最大連接數。根據連接數負載的情況,這個數字應該設置為預期連接數峰值的110%到150之間,若是連接數超出這個數字后,Redis會拒絕并立刻關閉新來的連接。通過設置最大連接數來限制非預期數量的連接數增長,是非常重要的。另外,新連接嘗試失敗會返回一個錯誤消息,這可以讓客戶端知道,Redis此時有非預期數量的連接數,以便執行對應的處理措施。 上述二種做法對控制連接數的數量和持續保持Redis的性能最優是非常重要的
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。