日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > 数据库 >内容正文

数据库

redis scan 效率太慢_Redis 基础、高级特性与性能调优(下)

發(fā)布時間:2024/4/11 数据库 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 redis scan 效率太慢_Redis 基础、高级特性与性能调优(下) 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

數(shù)據(jù)淘汰機制

Redis提供了5種數(shù)據(jù)淘汰策略:

  • volatile-lru:使用LRU算法進行數(shù)據(jù)淘汰(淘汰上次使用時間最早的,且使用次數(shù)最少的key),只淘汰設定了有效期的key
  • allkeys-lru:使用LRU算法進行數(shù)據(jù)淘汰,所有的key都可以被淘汰
  • volatile-random:隨機淘汰數(shù)據(jù),只淘汰設定了有效期的key
  • allkeys-random:隨機淘汰數(shù)據(jù),所有的key都可以被淘汰
  • volatile-ttl:淘汰剩余有效期最短的key

最好為Redis指定一種有效的數(shù)據(jù)淘汰策略以配合maxmemory設置,避免在內存使用滿后發(fā)生寫入失敗的情況。

一般來說,推薦使用的策略是volatile-lru,并辨識Redis中保存的數(shù)據(jù)的重要性。對于那些重要的,絕對不能丟棄的數(shù)據(jù)(如配置類數(shù)據(jù)等),應不設置有效期,這樣Redis就永遠不會淘汰這些數(shù)據(jù)。對于那些相對不是那么重要的,并且能夠熱加載的數(shù)據(jù)(比如緩存最近登錄的用戶信息,當在Redis中找不到時,程序會去DB中讀取),可以設置上有效期,這樣在內存不夠時Redis就會淘汰這部分數(shù)據(jù)。

配置方法:

maxmemory-policy volatile-lru #默認是noeviction,即不進行數(shù)據(jù)淘汰

Pipelining

Pipelining

Redis提供許多批量操作的命令,如MSET/MGET/HMSET/HMGET等等,這些命令存在的意義是減少維護網絡連接和傳輸數(shù)據(jù)所消耗的資源和時間。

例如連續(xù)使用5次SET命令設置5個不同的key,比起使用一次MSET命令設置5個不同的key,效果是一樣的,但前者會消耗更多的RTT(Round Trip Time)時長,永遠應優(yōu)先使用后者。

然而,如果客戶端要連續(xù)執(zhí)行的多次操作無法通過Redis命令組合在一起,例如:

SET a "abc"INCR bHSET c name "hi"

此時便可以使用Redis提供的pipelining功能來實現(xiàn)在一次交互中執(zhí)行多條命令。

使用pipelining時,只需要從客戶端一次向Redis發(fā)送多條命令(以rn)分隔,Redis就會依次執(zhí)行這些命令,并且把每個命令的返回按順序組裝在一起一次返回,比如:

$ (printf "PINGrnPINGrnPINGrn"; sleep 1) | nc localhost 6379+PONG+PONG+PONG

大部分的Redis客戶端都對Pipelining提供支持,所以開發(fā)者通常并不需要自己手工拼裝命令列表。

Pipelining的局限性

Pipelining只能用于執(zhí)行連續(xù)且無相關性的命令,當某個命令的生成需要依賴于前一個命令的返回時,就無法使用Pipelining了。

通過Scripting功能,可以規(guī)避這一局限性

事務與Scripting

Pipelining能夠讓Redis在一次交互中處理多條命令,然而在一些場景下,我們可能需要在此基礎上確保這一組命令是連續(xù)執(zhí)行的。

比如獲取當前累計的PV數(shù)并將其清0

> GET vCount12384> SET vCount 0OK

如果在GET和SET命令之間插進來一個INCR vCount,就會使客戶端拿到的vCount不準確。

Redis的事務可以確保復數(shù)命令執(zhí)行時的原子性。也就是說Redis能夠保證:一個事務中的一組命令是絕對連續(xù)執(zhí)行的,在這些命令執(zhí)行完成之前,絕對不會有來自于其他連接的其他命令插進去執(zhí)行。

通過MULTI和EXEC命令來把這兩個命令加入一個事務中:

> MULTIOK> GET vCountQUEUED> SET vCount 0QUEUED> EXEC1) 123842) OK

Redis在接收到MULTI命令后便會開啟一個事務,這之后的所有讀寫命令都會保存在隊列中但并不執(zhí)行,直到接收到EXEC命令后,Redis會把隊列中的所有命令連續(xù)順序執(zhí)行,并以數(shù)組形式返回每個命令的返回結果。

可以使用DISCARD命令放棄當前的事務,將保存的命令隊列清空。

需要注意的是,Redis事務不支持回滾:

如果一個事務中的命令出現(xiàn)了語法錯誤,大部分客戶端驅動會返回錯誤,2.6.5版本以上的Redis也會在執(zhí)行EXEC時檢查隊列中的命令是否存在語法錯誤,如果存在,則會自動放棄事務并返回錯誤。

但如果一個事務中的命令有非語法類的錯誤(比如對String執(zhí)行HSET操作),無論客戶端驅動還是Redis都無法在真正執(zhí)行這條命令之前發(fā)現(xiàn),所以事務中的所有命令仍然會被依次執(zhí)行。在這種情況下,會出現(xiàn)一個事務中部分命令成功部分命令失敗的情況,然而與RDBMS不同,Redis不提供事務回滾的功能,所以只能通過其他方法進行數(shù)據(jù)的回滾。

通過事務實現(xiàn)CAS

Redis提供了WATCH命令與事務搭配使用,實現(xiàn)CAS樂觀鎖的機制。

假設要實現(xiàn)將某個商品的狀態(tài)改為已售:

if(exec(HGET stock:1001 state) == "in stock") exec(HSET stock:1001 state "sold");

這一偽代碼執(zhí)行時,無法確保并發(fā)安全性,有可能多個客戶端都獲取到了”in stock”的狀態(tài),導致一個庫存被售賣多次。

使用WATCH命令和事務可以解決這一問題:

exec(WATCH stock:1001);if(exec(HGET stock:1001 state) == "in stock") { exec(MULTI); exec(HSET stock:1001 state "sold"); exec(EXEC);}

WATCH的機制是:在事務EXEC命令執(zhí)行時,Redis會檢查被WATCH的key,只有被WATCH的key從WATCH起始時至今沒有發(fā)生過變更,EXEC才會被執(zhí)行。如果WATCH的key在WATCH命令到EXEC命令之間發(fā)生過變化,則EXEC命令會返回失敗。

Scripting

通過EVAL與EVALSHA命令,可以讓Redis執(zhí)行LUA腳本。這就類似于RDBMS的存儲過程一樣,可以把客戶端與Redis之間密集的讀/寫交互放在服務端進行,避免過多的數(shù)據(jù)交互,提升性能。

Scripting功能是作為事務功能的替代者誕生的,事務提供的所有能力Scripting都可以做到。Redis官方推薦使用LUA Script來代替事務,前者的效率和便利性都超過了事務。

關于Scripting的具體使用,本文不做詳細介紹,請參考官方文檔

https://redis.io/commands/eval

Redis性能調優(yōu)

盡管Redis是一個非常快速的內存數(shù)據(jù)存儲媒介,也并不代表Redis不會產生性能問題。

前文中提到過,Redis采用單線程模型,所有的命令都是由一個線程串行執(zhí)行的,所以當某個命令執(zhí)行耗時較長時,會拖慢其后的所有命令,這使得Redis對每個任務的執(zhí)行效率更加敏感。

針對Redis的性能優(yōu)化,主要從下面幾個層面入手:

  • 最初的也是最重要的,確保沒有讓Redis執(zhí)行耗時長的命令
  • 使用pipelining將連續(xù)執(zhí)行的命令組合執(zhí)行
  • 操作系統(tǒng)的Transparent huge pages功能必須關閉:
echo never > /sys/kernel/mm/transparent_hugepage/enabled
  • 如果在虛擬機中運行Redis,可能天然就有虛擬機環(huán)境帶來的固有延遲。可以通過./redis-cli –intrinsic-latency 100命令查看固有延遲。同時如果對Redis的性能有較高要求的話,應盡可能在物理機上直接部署Redis。
  • 檢查數(shù)據(jù)持久化策略
  • 考慮引入讀寫分離機制

長耗時命令

Redis絕大多數(shù)讀寫命令的時間復雜度都在O(1)到O(N)之間,在文本和官方文檔中均對每個命令的時間復雜度有說明。

通常來說,O(1)的命令是安全的,O(N)命令在使用時需要注意,如果N的數(shù)量級不可預知,則應避免使用。例如對一個field數(shù)未知的Hash數(shù)據(jù)執(zhí)行HGETALL/HKEYS/HVALS命令,通常來說這些命令執(zhí)行的很快,但如果這個Hash中的field數(shù)量極多,耗時就會成倍增長。

又如使用SUNION對兩個Set執(zhí)行Union操作,或使用SORT對List/Set執(zhí)行排序操作等時,都應該嚴加注意。

避免在使用這些O(N)命令時發(fā)生問題主要有幾個辦法:

  • 不要把List當做列表使用,僅當做隊列來使用
  • 通過機制嚴格控制Hash、Set、Sorted Set的大小
  • 可能的話,將排序、并集、交集等操作放在客戶端執(zhí)行
  • 絕對禁止使用KEYS命令
  • 避免一次性遍歷集合類型的所有成員,而應使用SCAN類的命令進行分批的,游標式的遍歷

Redis提供了SCAN命令,可以對Redis中存儲的所有key進行游標式的遍歷,避免使用KEYS命令帶來的性能問題。同時還有SSCAN/HSCAN/ZSCAN等命令,分別用于對Set/Hash/Sorted Set中的元素進行游標式遍歷。SCAN類命令的使用請參考官方文檔:

https://redis.io/commands/scan

Redis提供了Slow Log功能,可以自動記錄耗時較長的命令。相關的配置參數(shù)有兩個:

slowlog-log-slower-than xxxms #執(zhí)行時間慢于xxx毫秒的命令計入Slow Logslowlog-max-len xxx #Slow Log的長度,即最大紀錄多少條Slow Log

使用SLOWLOG GET [number]命令,可以輸出最近進入Slow Log的number條命令。

使用SLOWLOG RESET命令,可以重置Slow Log

網絡引發(fā)的延遲

  • 盡可能使用長連接或連接池,避免頻繁創(chuàng)建銷毀連接
  • 客戶端進行的批量數(shù)據(jù)操作,應使用Pipeline特性在一次交互中完成。具體請參照本文的Pipelining章節(jié)

數(shù)據(jù)持久化引發(fā)的延遲

Redis的數(shù)據(jù)持久化工作本身就會帶來延遲,需要根據(jù)數(shù)據(jù)的安全級別和性能要求制定合理的持久化策略:

  • AOF + fsync always的設置雖然能夠絕對確保數(shù)據(jù)安全,但每個操作都會觸發(fā)一次fsync,會對Redis的性能有比較明顯的影響
  • AOF + fsync every second是比較好的折中方案,每秒fsync一次
  • AOF + fsync never會提供AOF持久化方案下的最優(yōu)性能
  • 使用RDB持久化通常會提供比使用AOF更高的性能,但需要注意RDB的策略配置
  • 每一次RDB快照和AOF Rewrite都需要Redis主進程進行fork操作。fork操作本身可能會產生較高的耗時,與CPU和Redis占用的內存大小有關。根據(jù)具體的情況合理配置RDB快照和AOF Rewrite時機,避免過于頻繁的fork帶來的延遲
Redis在fork子進程時需要將內存分頁表拷貝至子進程,以占用了24GB內存的Redis實例為例,共需要拷貝24GB / 4kB * 8 = 48MB的數(shù)據(jù)。在使用單Xeon 2.27Ghz的物理機上,這一fork操作耗時216ms。可以通過INFO命令返回的latest_fork_usec字段查看上一次fork操作的耗時(微秒)

Swap引發(fā)的延遲

當Linux將Redis所用的內存分頁移至swap空間時,將會阻塞Redis進程,導致Redis出現(xiàn)不正常的延遲。Swap通常在物理內存不足或一些進程在進行大量I/O操作時發(fā)生,應盡可能避免上述兩種情況的出現(xiàn)。

/proc//smaps文件中會保存進程的swap記錄,通過查看這個文件,能夠判斷Redis的延遲是否由Swap產生。如果這個文件中記錄了較大的Swap size,則說明延遲很有可能是Swap造成的。

數(shù)據(jù)淘汰引發(fā)的延遲

當同一秒內有大量key過期時,也會引發(fā)Redis的延遲。在使用時應盡量將key的失效時間錯開。

引入讀寫分離機制

Redis的主從復制能力可以實現(xiàn)一主多從的多節(jié)點架構,在這一架構下,主節(jié)點接收所有寫請求,并將數(shù)據(jù)同步給多個從節(jié)點。

在這一基礎上,我們可以讓從節(jié)點提供對實時性要求不高的讀請求服務,以減小主節(jié)點的壓力。

尤其是針對一些使用了長耗時命令的統(tǒng)計類任務,完全可以指定在一個或多個從節(jié)點上執(zhí)行,避免這些長耗時命令影響其他請求的響應。

關于讀寫分離的具體說明,請參見后續(xù)章節(jié)

主從復制與集群分片

主從復制

Redis支持一主多從的主從復制架構。一個Master實例負責處理所有的寫請求,Master將寫操作同步至所有Slave。

借助Redis的主從復制,可以實現(xiàn)讀寫分離和高可用:

  • 實時性要求不是特別高的讀請求,可以在Slave上完成,提升效率。特別是一些周期性執(zhí)行的統(tǒng)計任務,這些任務可能需要執(zhí)行一些長耗時的Redis命令,可以專門規(guī)劃出1個或幾個Slave用于服務這些統(tǒng)計任務
  • 借助Redis Sentinel可以實現(xiàn)高可用,當Master crash后,Redis Sentinel能夠自動將一個Slave晉升為Master,繼續(xù)提供服務

啟用主從復制非常簡單,只需要配置多個Redis實例,在作為Slave的Redis實例中配置:

slaveof 192.168.1.1 6379 #指定Master的IP和端口

當Slave啟動后,會從Master進行一次冷啟動數(shù)據(jù)同步,由Master觸發(fā)BGSAVE生成RDB文件推送給Slave進行導入,導入完成后Master再將增量數(shù)據(jù)通過Redis Protocol同步給Slave。之后主從之間的數(shù)據(jù)便一直以Redis Protocol進行同步

使用Sentinel做自動failover

Redis的主從復制功能本身只是做數(shù)據(jù)同步,并不提供監(jiān)控和自動failover能力,要通過主從復制功能來實現(xiàn)Redis的高可用,還需要引入一個組件:Redis Sentinel

Redis Sentinel是Redis官方開發(fā)的監(jiān)控組件,可以監(jiān)控Redis實例的狀態(tài),通過Master節(jié)點自動發(fā)現(xiàn)Slave節(jié)點,并在監(jiān)測到Master節(jié)點失效時選舉出一個新的Master,并向所有Redis實例推送新的主從配置。

Redis Sentinel需要至少部署3個實例才能形成選舉關系。

關鍵配置:

另外需要注意的是,Redis Sentinel實現(xiàn)的自動failover不是在同一個IP和端口上完成的,也就是說自動failover產生的新Master提供服務的IP和端口與之前的Master是不一樣的,所以要實現(xiàn)HA,還要求客戶端必須支持Sentinel,能夠與Sentinel交互獲得新Master的信息才行。

集群分片

為何要做集群分片:

  • Redis中存儲的數(shù)據(jù)量大,一臺主機的物理內存已經無法容納
  • Redis的寫請求并發(fā)量大,一個Redis實例以無法承載

當上述兩個問題出現(xiàn)時,就必須要對Redis進行分片了。

Redis的分片方案有很多種,例如很多Redis的客戶端都自行實現(xiàn)了分片功能,也有向Twemproxy這樣的以代理方式實現(xiàn)的Redis分片方案。然而首選的方案還應該是Redis官方在3.0版本中推出的Redis Cluster分片方案。

本文不會對Redis Cluster的具體安裝和部署細節(jié)進行介紹,重點介紹Redis Cluster帶來的好處與弊端。

Redis Cluster的能力

  • 能夠自動將數(shù)據(jù)分散在多個節(jié)點上
  • 當訪問的key不在當前分片上時,能夠自動將請求轉發(fā)至正確的分片
  • 當集群中部分節(jié)點失效時仍能提供服務

其中第三點是基于主從復制來實現(xiàn)的,Redis Cluster的每個數(shù)據(jù)分片都采用了主從復制的結構,原理和前文所述的主從復制完全一致,唯一的區(qū)別是省去了Redis Sentinel這一額外的組件,由Redis Cluster負責進行一個分片內部的節(jié)點監(jiān)控和自動failover。

Redis Cluster分片原理

Redis Cluster中共有16384個hash slot,Redis會計算每個key的CRC16,將結果與16384取模,來決定該key存儲在哪一個hash slot中,同時需要指定Redis Cluster中每個數(shù)據(jù)分片負責的Slot數(shù)。Slot的分配在任何時間點都可以進行重新分配。

客戶端在對key進行讀寫操作時,可以連接Cluster中的任意一個分片,如果操作的key不在此分片負責的Slot范圍內,Redis Cluster會自動將請求重定向到正確的分片上。

hash tags

在基礎的分片原則上,Redis還支持hash tags功能,以hash tags要求的格式明明的key,將會確保進入同一個Slot中。例如:{uiv}user:1000和{uiv}user:1001擁有同樣的hash tag {uiv},會保存在同一個Slot中。

使用Redis Cluster時,pipelining、事務和LUA Script功能涉及的key必須在同一個數(shù)據(jù)分片上,否則將會返回錯誤。如要在Redis Cluster中使用上述功能,就必須通過hash tags來確保一個pipeline或一個事務中操作的所有key都位于同一個Slot中。

有一些客戶端(如Redisson)實現(xiàn)了集群化的pipelining操作,可以自動將一個pipeline里的命令按key所在的分片進行分組,分別發(fā)到不同的分片上執(zhí)行。但是Redis不支持跨分片的事務,事務和LUA Script還是必須遵循所有key在一個分片上的規(guī)則要求。

主從復制 vs 集群分片

在設計軟件架構時,要如何在主從復制和集群分片兩種部署方案中取舍呢?

從各個方面看,Redis Cluster都是優(yōu)于主從復制的方案

  • Redis Cluster能夠解決單節(jié)點上數(shù)據(jù)量過大的問題
  • Redis Cluster能夠解決單節(jié)點訪問壓力過大的問題
  • Redis Cluster包含了主從復制的能力

那是不是代表Redis Cluster永遠是優(yōu)于主從復制的選擇呢?

并不是。

軟件架構永遠不是越復雜越好,復雜的架構在帶來顯著好處的同時,一定也會帶來相應的弊端。采用Redis Cluster的弊端包括:

  • 維護難度增加。在使用Redis Cluster時,需要維護的Redis實例數(shù)倍增,需要監(jiān)控的主機數(shù)量也相應增加,數(shù)據(jù)備份/持久化的復雜度也會增加。同時在進行分片的增減操作時,還需要進行reshard操作,遠比主從模式下增加一個Slave的復雜度要高。
  • 客戶端資源消耗增加。當客戶端使用連接池時,需要為每一個數(shù)據(jù)分片維護一個連接池,客戶端同時需要保持的連接數(shù)成倍增多,加大了客戶端本身和操作系統(tǒng)資源的消耗。
  • 性能優(yōu)化難度增加。你可能需要在多個分片上查看Slow Log和Swap日志才能定位性能問題。
  • 事務和LUA Script的使用成本增加。在Redis Cluster中使用事務和LUA Script特性有嚴格的限制條件,事務和Script中操作的key必須位于同一個分片上,這就使得在開發(fā)時必須對相應場景下涉及的key進行額外的規(guī)劃和規(guī)范要求。如果應用的場景中大量涉及事務和Script的使用,如何在保證這兩個功能的正常運作前提下把數(shù)據(jù)平均分到多個數(shù)據(jù)分片中就會成為難點。

所以說,在主從復制和集群分片兩個方案中做出選擇時,應該從應用軟件的功能特性、數(shù)據(jù)和訪問量級、未來發(fā)展規(guī)劃等方面綜合考慮,只在確實有必要引入數(shù)據(jù)分片時再使用Redis Cluster。

下面是一些建議:

  • 需要在Redis中存儲的數(shù)據(jù)有多大?未來2年內可能發(fā)展為多大?這些數(shù)據(jù)是否都需要長期保存?是否可以使用LRU算法進行非熱點數(shù)據(jù)的淘汰?綜合考慮前面幾個因素,評估出Redis需要使用的物理內存。
  • 用于部署Redis的主機物理內存有多大?有多少可以分配給Redis使用?對比(1)中的內存需求評估,是否足夠用?
  • Redis面臨的并發(fā)寫壓力會有多大?在不使用pipelining時,Redis的寫性能可以超過10萬次/秒(更多的benchmark可以參考 https://redis.io/topics/benchmarks )
  • 在使用Redis時,是否會使用到pipelining和事務功能?使用的場景多不多?
  • 綜合上面幾點考慮,如果單臺主機的可用物理內存完全足以支撐對Redis的容量需求,且Redis面臨的并發(fā)寫壓力距離Benchmark值還尚有距離,建議采用主從復制的架構,可以省去很多不必要的麻煩。同時,如果應用中大量使用pipelining和事務,也建議盡可能選擇主從復制架構,可以減少設計和開發(fā)時的復雜度。

    Redis Java客戶端的選擇

    Redis的Java客戶端很多,官方推薦的有三種:Jedis、Redisson和lettuce。

    在這里對Jedis和Redisson進行對比介紹

    Jedis:

    • 輕量,簡潔,便于集成和改造
    • 支持連接池
    • 支持pipelining、事務、LUA Scripting、Redis Sentinel、Redis Cluster
    • 不支持讀寫分離,需要自己實現(xiàn)
    • 文檔差(真的很差,幾乎沒有……)

    Redisson:

    • 基于Netty實現(xiàn),采用非阻塞IO,性能高
    • 支持異步請求
    • 支持連接池
    • 支持pipelining、LUA Scripting、Redis Sentinel、Redis Cluster
    • 不支持事務,官方建議以LUA Scripting代替事務
    • 支持在Redis Cluster架構下使用pipelining
    • 支持讀寫分離,支持讀負載均衡,在主從復制和Redis Cluster架構下都可以使用
    • 內建Tomcat Session Manager,為Tomcat 6/7/8提供了會話共享功能
    • 可以與Spring Session集成,實現(xiàn)基于Redis的會話共享
    • 文檔較豐富,有中文文檔

    對于Jedis和Redisson的選擇,同樣應遵循前述的原理,盡管Jedis比起Redisson有各種各樣的不足,但也應該在需要使用Redisson的高級特性時再選用Redisson,避免造成不必要的程序復雜度提升。

    本文轉自k8s中文社區(qū)

    總結

    以上是生活随笔為你收集整理的redis scan 效率太慢_Redis 基础、高级特性与性能调优(下)的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。