日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網為廣大站長提供免費收錄網站服務,提交前請做好本站友鏈:【 網站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

 

redis時延問題分析及應對

Redis的事件循環在一個線程中處理,作為一個單線程程序,重要的是要保證事件處理的時延短,這樣,事件循環中的后續任務才不會阻塞;

當redis的數據量達到一定級別后(比如20G),阻塞操作對性能的影響尤為嚴重;

下面我們總結下在redis中有哪些耗時的場景及應對方法;

耗時長的命令造成阻塞

keys、sort等命令

keys命令用于查找所有符合給定模式 pattern 的 key,時間復雜度為O(N), N 為數據庫中 key 的數量。當數據庫中的個數達到千萬時,這個命令會造成讀寫線程阻塞數秒;

類似的命令有sunion sort等操作;

如果業務需求中一定要使用keys、sort等操作怎么辦?

解決方案:

Redis時延問題分析及應對

 

在架構設計中,有“分流”一招,說的是將處理快的請求和處理慢的請求分離來開,否則,慢的影響到了快的,讓快的也快不起來;這在redis的設計中體現的非常明顯,redis的純內存操作,epoll非阻塞IO事件處理,這些快的放在一個線程中搞定,而持久化,AOF重寫、Master-slave同步數據這些耗時的操作就單開一個進程來處理,不要慢的影響到快的;

同樣,既然需要使用keys這些耗時的操作,那么我們就將它們剝離出去,比如單開一個redis slave結點,專門用于keys、sort等耗時的操作,這些查詢一般不會是線上的實時業務,查詢慢點就慢點,主要是能完成任務,而對于線上的耗時快的任務沒有影響;

smembers命令

smembers命令用于獲取集合全集,時間復雜度為O(N),N為集合中的數量;

如果一個集合中保存了千萬量級的數據,一次取回也會造成事件處理線程的長時間阻塞;

解決方案:

和sort,keys等命令不一樣,smembers可能是線上實時應用場景中使用頻率非常高的一個命令,這里分流一招并不適合,我們更多的需要從設計層面來考慮;

在設計時,我們可以控制集合的數量,將集合數一般保持在500個以內;

比如原來使用一個鍵來存儲一年的記錄,數據量大,我們可以使用12個鍵來分別保存12個月的記錄,或者365個鍵來保存每一天的記錄,將集合的規模控制在可接受的范圍;

如果不容易將集合劃分為多個子集合,而堅持用一個大集合來存儲,那么在取集合的時候可以考慮使用SRANDMEMBER key [count];隨機返回集合中的指定數量,當然,如果要遍歷集合中的所有元素,這個命令就不適合了;

save命令

save命令使用事件處理線程進行數據的持久化;當數據量大的時候,會造成線程長時間阻塞(我們的生產上,reids內存中1個G保存需要12s左右),整個redis被block;

save阻塞了事件處理的線程,我們甚至無法使用redis-cli查看當前的系統狀態,造成“何時保存結束,目前保存了多少”這樣的信息都無從得知;

解決方案:

我沒有想到需要用到save命令的場景,任何時候需要持久化的時候使用bgsave都是合理的選擇(當然,這個命令也會帶來問題,后面聊到);

fork產生的阻塞

在redis需要執行耗時的操作時,會新建一個進程來做,比如數據持久化bgsave:

開啟RDB持久化后,當達到持久化的閾值,redis會fork一個新的進程來做持久化,采用了操作系統的copy-on-wirte寫時復制策略,子進程與父進程共享Page。如果父進程的Page(每頁4K)有修改,父進程自己創建那個Page的副本,不會影響到子進程;

fork新進程時,雖然可共享的數據內容不需要復制,但會復制之前進程空間的內存頁表,如果內存空間有40G(考慮每個頁表條目消耗 8 個字節),那么頁表大小就有80M,這個復制是需要時間的,如果使用虛擬機,特別是Xen虛擬服務器,耗時會更長;

在我們有的服務器結點上測試,35G的數據bgsave瞬間會阻塞200ms以上;

類似的,以下這些操作都有進程fork;

  • Master向slave首次同步數據:當master結點收到slave結點來的syn同步請求,會生成一個新的進程,將內存數據dump到文件上,然后再同步到slave結點中;
  • AOF日志重寫:使用AOF持久化方式,做AOF文件重寫操作會創建新的進程做重寫;(重寫并不會去讀已有的文件,而是直接使用內存中的數據寫成歸檔日志);

解決方案:

為了應對大內存頁表復制時帶來的影響,有些可用的措施:

  1. 控制每個redis實例的最大內存量;
  2. 不讓fork帶來的限制太多,可以從內存量上控制fork的時延;
  3. 一般建議不超過20G,可根據自己服務器的性能來確定(內存越大,持久化的時間越長,復制頁表的時間越長,對事件循環的阻塞就延長)
  4. 新浪微博給的建議是不超過20G,而我們虛機上的測試,要想保證應用毛刺不明顯,可能得在10G以下;
  5. 使用大內存頁,默認內存頁使用4KB,這樣,當使用40G的內存時,頁表就有80M;而將每個內存頁擴大到4M,頁表就只有80K;這樣復制頁表幾乎沒有阻塞,同時也會提高快速頁表緩沖TLB(translation lookaside buffer)的命中率;但大內存頁也有問題,在寫時復制時,只要一個頁快中任何一個元素被修改,這個頁塊都需要復制一份(COW機制的粒度是頁面),這樣在寫時復制期間,會耗用更多的內存空間;
  6. 使用物理機;
  7. 如果有的選,物理機當然是最佳方案,比上面都要省事;
  8. 當然,虛擬化實現也有多種,除了Xen系統外,現代的硬件大部分都可以快速的復制頁表;
  9. 但公司的虛擬化一般是成套上線的,不會因為我們個別服務器的原因而變更,如果面對的只有Xen,只能想想如何用好它;
  10. 杜絕新進程的產生,不使用持久化,不在主結點上提供查詢;實現起來有以下方案:
  11. 1) 只用單機,不開持久化,不掛slave結點。這樣最簡單,不會有新進程的產生;但這樣的方案只適合緩存;
  12. 如何來做這個方案的高可用?
  13. 要做高可用,可以在寫redis的前端掛上一個消息隊列,在消息隊列中使用pub-sub來做分發,保證每個寫操作至少落到2個結點上;因為所有結點的數據相同,只需要用一個結點做持久化,這個結點對外不提供查詢;
Redis時延問題分析及應對

 

  1.  
  2. 2) master-slave:在主結點上開持久化,主結點不對外提供查詢,查詢由slave結點提供,從結點不提供持久化;這樣,所有的fork耗時的操作都在主結點上,而查詢請求由slave結點提供;
  3. 這個方案的問題是主結點壞了之后如何處理?
  4. 簡單的實現方案是主不具有可替代性,壞了之后,redis集群對外就只能提供讀,而無法更新;待主結點啟動后,再繼續更新操作;對于之前的更新操作,可以用MQ緩存起來,等主結點起來之后消化掉故障期間的寫請求;
Redis時延問題分析及應對

 

  1.  
  2. 如果使用官方的Sentinel將從升級為主,整體實現就相對復雜了;需要更改可用從的ip配置,將其從可查詢結點中剔除,讓前端的查詢負載不再落在新主上;然后,才能放開sentinel的切換操作,這個前后關系需要保證;

持久化造成的阻塞

執行持久化(AOF / RDB snapshot)對系統性能有較大影響,特別是服務器結點上還有其它讀寫磁盤的操作時(比如,應用服務和redis服務部署在相同結點上,應用服務實時記錄進出報日志);應盡可能避免在IO已經繁重的結點上開Redis持久化;

子進程持久化時,子進程的write和主進程的fsync沖突造成阻塞

在開啟了AOF持久化的結點上,當子進程執行AOF重寫或者RDB持久化時,出現了Redis查詢卡頓甚至長時間阻塞的問題, 此時, Redis無法提供任何讀寫操作;

原因分析:

Redis 服務設置了 Appendfsync everysec, 主進程每秒鐘便會調用 fsync(), 要求內核將數據”確實”寫到存儲硬件里. 但由于服務器正在進行大量IO操作, 導致主進程 fsync()/操作被阻塞, 最終導致 Redis 主進程阻塞.

redis.conf中是這么說的:

When the AOF fsync policy is set to always or everysec, and a background

saving process (a background save or AOF log background rewriting) is

performing a lot of I/O against the disk, in some linux configurations

Redis may block too long on the fsync() call. Note that there is no fix for

this currently, as even performing fsync in a different thread will block

our synchronous write(2) call.

當執行AOF重寫時會有大量IO,這在某些Linux配置下會造成主進程fsync阻塞;

解決方案:

設置 no-appendfsync-on-rewrite yes, 在子進程執行AOF重寫時, 主進程不調用fsync()操作;注意, 即使進程不調用 fsync(), 系統內核也會根據自己的算法在適當的時機將數據寫到硬盤(Linux 默認最長不超過 30 秒).

這個設置帶來的問題是當出現故障時,最長可能丟失超過30秒的數據,而不再是1秒;

子進程AOF重寫時,系統的sync造成主進程的write阻塞

我們來梳理下:

1) 起因:有大量IO操作write(2) 但未主動調用同步操作

2) 造成kernel buffer中有大量臟數據

3) 系統同步時,sync的同步時間過長

4) 造成redis的寫aof日志write(2)操作阻塞;

5) 造成單線程的redis的下一個事件無法處理,整個redis阻塞(redis的事件處理是在一個線程中進行,其中寫aof日志的write(2)是同步阻塞模式調用,與網絡的非阻塞write(2)要區分開來)

產生1)的原因:這是redis2.6.12之前的問題,AOF rewrite時一直埋頭的調用write(2),由系統自己去觸發sync。

另外的原因:系統IO繁忙,比如有別的應用在寫盤;

解決方案:

控制系統sync調用的時間;需要同步的數據多時,耗時就長;縮小這個耗時,控制每次同步的數據量;通過配置按比例(vm.dirty_background_ratio)或按值(vm.dirty_bytes)設置sync的調用閾值;(一般設置為32M同步一次)

2.6.12以后,AOF rewrite 32M時會主動調用fdatasync;

另外,Redis當發現當前正在寫的文件有在執行fdatasync(2)時,就先不調用write(2),只存在cache里,免得被block。但如果已經超過兩秒都還是這個樣子,則會強行執行write(2),即使redis會被block住。

AOF重寫完成后合并數據時造成的阻塞

在bgrewriteaof過程中,所有新來的寫入請求依然會被寫入舊的AOF文件,同時放到AOF buffer中,當rewrite完成后,會在主線程把這部分內容合并到臨時文件中之后才rename成新的AOF文件,所以rewrite過程中會不斷打印"Background AOF buffer size: 80 MB, Background AOF buffer size: 180 MB",要監控這部分的日志。這個合并的過程是阻塞的,如果產生了280MB的buffer,在100MB/s的傳統硬盤上,Redis就要阻塞2.8秒;

解決方案:

將硬盤設置的足夠大,將AOF重寫的閾值調高,保證高峰期間不會觸發重寫操作;在閑時使用crontab 調用AOF重寫命令;

參考:

http://www.oschina.net/translate/redis-latency-problems-troubleshooting

https://github.com/springside/springside4/wiki/redis

原文地址:https://www.cnblogs.com/me115/p/5032177.html

JAVA編程技術樂園:一個分享編程知識。跟著老司機一起學習干貨技術知識,每天進步一點點,讓小的積累,帶來大的改變!

歡迎關注!持續推送有趣有料的技術文章~

分享到:
標簽:時延 Redis
用戶無頭像

網友整理

注冊時間:

網站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網站吧!
最新入駐小程序

數獨大挑戰2018-06-03

數獨一種數學游戲,玩家需要根據9

答題星2018-06-03

您可以通過答題星輕松地創建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數有氧達人2018-06-03

記錄運動步數,積累氧氣值。還可偷

每日養生app2018-06-03

每日養生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定