国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

Redis知識點詳解

2019-11-08 20:51:18
字體:
來源:轉載
供稿:網友

非常感謝http://blog.csdn.net/hguisu/article/details/9165141

1、與mencache比較

性能方面:沒有必要過多的關心性能,因為二者的性能都已經足夠高了。由于Redis只使用單核,而Memcached可以使用多核,所以在比較上,平均每一個核上Redis在存儲小數據時比Memcached性能更高。而在100k以上的數據中,Memcached性能要高于Redis,雖然Redis最近也在存儲大數據的性能上進行優化,但是比起Memcached,還是稍有遜色。說了這么多,結論是,無論你使用哪一個,每秒處理請求的次數都不會成為瓶頸。(比如瓶頸可能會在網卡)內存使用效率:使用簡單的key-value存儲的話,Memcached的內存利用率更高,而如果Redis采用hash結構來做key-value存儲,由于其組合式的壓縮,其內存利用率會高于Memcached。當然,這和你的應用場景和數據特性有關。數據持久化:如果你對數據持久化和數據同步有所要求,那么推薦你選擇Redis,因為這兩個特性Memcached都不具備。即使你只是希望在升級或者重啟系統后緩存數據不會丟失,選擇Redis也是明智的。數據結構:當然,最后還得說到你的具體應用需求。Redis相比Memcached來說,擁有更多的數據結構和并支持更豐富的數據操作,通常在Memcached里,你需要將數據拿到客戶端來進行類似的修改再set回去。這大大增加了網絡IO的次數和數據體積。在Redis中,這些復雜的操作通常和一般的GET/SET一樣高效。所以,如果你需要緩存能夠支持更復雜的結構和操作,那么Redis會是不錯的選擇。

網絡IO模型方面:Memcached是多線程,分為監聽線程、worker線程,引入鎖,帶來了性能損耗。Redis使用單線程的IO復用模型,將速度優勢發揮到最大,也提供了較簡單的計算功能 

內存管理方面:Memcached使用預分配的內存池的方式,帶來一定程度的空間浪費 并且在內存仍然有很大空間時,新的數據也可能會被剔除,而Redis使用現場申請內存的方式來存儲數據,不會剔除任何非臨時數據 Redis更適合作為存儲而不是cache 

數據的一致性方面:Memcached提供了cas命令來保證.而Redis提供了事務的功能,可以保證一串 命令的原子性,中間不會被任何操作打斷 

    如果簡單地比較Redis與Memcached的區別,大多數都會得到以下觀點:

     1 、Redis不僅僅支持簡單的k/v類型的數據,同時還提供list,set,zset,hash等數據結構的存儲。

     2 、Redis支持數據的備份,即master-slave模式的數據備份。

     3 、Redis支持數據的持久化,可以將內存中的數據保持在磁盤中,重啟的時候可以再次加載進行使用。

     4、Redis可以實現主從復制,實現故障恢復。

     5、Redis的Sharding技術: 很容易將數據分布到多個Redis實例中

下面我們簡單一項一項詳解

2、數據結構說明

http://redis.io/topics/data-types

2.1 Key

1、key越短越好:Redis是個內存數據庫,Key鍵越短你需要的空間就越少,因此key不能太長,比如1024字節。

舉個例子:在一個32位的Redis服務器上,如果儲存一百萬個鍵,每個值的長度是32-character,那么在使用6-character長度鍵名時,將會消耗大約96MB的空間,但是如果使用12-character長度的鍵名時,空間消耗則會提升至111MB左右。隨著鍵的增多,15%的額外開銷將產生重大的影響。

2、key命名要表達清楚意思。建議用”:”分隔域劃分鍵名,用”.”作為單詞間的連接,如”comment:1234:reply.to”。

使用合適的命名方法會簡化你的數據庫管理,當你通過你的應用程序或者服務做鍵的命名空間時,你就可以在數據遷移、轉換或者刪除時輕松的識別。

Redis另一個常見用例是作為熱數據項作的第二數據存儲,大部分的數據被保存在其他的數據庫中,比如PostgreSQL或MongoDB。在這些用例中,當數據從主存儲移除時,開發者經常會忘記刪除Redis中對應的數據。這種存在跨數據存儲的情況下,通常需要做級聯刪除,這種情況下,可以通過在Redis配置保存特定數據項的所有識別符來實現,從而保證數據在主數據庫被刪除后,系統會調用一個清理程序來刪除所有相關副本和信息。

2.2 String

String是最簡單的類型,一個key對應一個value,string類型是二進制安全的。Redis的string可以包含任何數據,比如jpg圖片或者序列化的對象(php中對象序列化函數serialize)

       內部實現,其本質是一個byte數組,字符串的大小被限制在512M以內

[cpp] view plain copy PRint?在CODE上查看代碼片struct sdshdr {          long len; //buf數組的長度          long free; //buf數組中剩余可用字節數          char buf[]; //存儲實際字符串內容    }    

所有常用命令的復雜度都是O(1),普通的Get/Set方法,可以用來做Cache,存session,為了簡化架構甚至可以替換掉Memcached。

Incr/IncrBy/IncrByFloat/Decr/DecrBy,可以用來做

3、Redis持久化讀寫性能

AOF重寫和RDB寫入都是在fork出新進程后,遍歷新進程的內存順序寫的,既不阻塞主進程繼續處理客戶端請求,順序寫的速度也比隨機寫快。測試把剛才benchmark的11G數據寫成一個1.3的RDB文件,或者等大的AOF文件rewrite,需要80秒,在redis-cli info中可查看。啟動時載入一個AOF或RDB文件的速度與上面寫入時相同,在log中可查看。Fork一個使用了大量內存的進程也要時間,大約10ms per GB的樣子,但Xen在EC2上是讓人郁悶的239ms (KVM和VMWare貌似沒有這個毛病),各種系統的對比,Info指令里的latest_fork_usec顯示上次花費的時間。在 bgrewriteaof過程中,所有新來的寫入請求依然會被寫入舊的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秒!!!NFS或者Amazon上的EBS都不推薦,因為它們也要消耗帶寬。bgsave和bgaofrewrite不會被同時執行,如果bgsave正在執行,bgaofrewrite會自動延后。2.4版以后,寫入AOF時的fdatasync由另一條線程來執行,不會再阻塞主線程。2.4版以后,lpush/zadd可以輸入一次多個值了,使得AOF重寫時可以將舊版本中的多個lpush/zadd指令合成一個,每64個key串一串。

4、Redis持久化性能調整

因為RDB文件只用作后備用途,建議只在Slave上持久化RDB文件,而且只要15分鐘備份一次就夠了,只保留save 900 1這條規則。

如 果Enalbe AOF,好處是在最惡劣情況下也只會丟失不超過兩秒數據,啟動腳本較簡單只load自己的AOF文件就可以了。代價一是帶來了持續的IO,二是AOF rewrite的最后將rewrite過程中產生的新數據寫到新文件造成的阻塞幾乎是不可避免的。只要硬盤許可,應該盡量減少AOF rewrite的頻率,AOF重寫的基礎大小默認值64M太小了,可以設到5G以上。默認超過原大小100%大小時重寫可以改到適當的數值,比如之前的 benchmark每個小時會產生40G大小的AOF文件,如果硬盤能撐到半夜系統閑時才用cron調度bgaofrewrite就好了。

如 果不Enable AOF ,僅靠Master-Slave Replication 實現高可用性也可以。能省掉一大筆IO也減少了rewrite時帶來的系統波動。代價是如果Master/Slave同時倒掉,會丟失十幾分鐘的數據,啟 動腳本也要比較兩個Master/Slave中的RDB文件,載入較新的那個。新浪微博就選用了這種架構,見Tim的博客

5 Trouble Shooting —— Enable AOF可能導致整個Redis被Block住,在2.6.12版之前

現象描述:當AOF rewrite 15G大小的內存時,Redis整個死掉的樣子,所有指令甚至包括slave發到master的ping,redis-cli info都不能被執行。

原因分析:

官方文檔,由IO產生的Latency詳細分析, 已經預言了悲劇的發生,但一開始沒留意。Redis為求簡單,采用了單請求處理線程結構。打開AOF持久化功能后, Redis處理完每個事件后會調用write(2)將變化寫入kernel的buffer,如果此時write(2)被阻塞,Redis就不能處理下一個事件。Linux規定執行write(2)時,如果對同一個文件正在執行fdatasync(2)將kernel buffer寫入物理磁盤,或者有system wide sync在執行,write(2)會被block住,整個Redis被block住。如 果系統IO繁忙,比如有別的應用在寫盤,或者Redis自己在AOF rewrite或RDB snapshot(雖然此時寫入的是另一個臨時文件,雖然各自都在連續寫,但兩個文件間的切換使得磁盤磁頭的尋道時間加長),就可能導致 fdatasync(2)遲遲未能完成從而block住write(2),block住整個Redis。為了更清晰的看到fdatasync(2)的執行時長,可以使用”strace -p (pid of redis server) -T -e -f trace=fdatasync”,但會影響系統性能。Redis 提供了一個自救的方式,當發現文件有在執行fdatasync(2)時,就先不調用write(2),只存在cache里,免得被block。但如果已經 超過兩秒都還是這個樣子,則會硬著頭皮執行write(2),即使redis會被block住。此時那句要命的log會打印:“Asynchronous AOF fsync is taking too long (disk is busy?). Writing the AOF buffer without waiting for fsync to complete, this may slow down Redis.” 之后用redis-cli INFO可以看到aof_delayed_fsync的值被加1。因此,對于fsync設為everysec時丟失數 據的可能性的最嚴謹說法是:如果有fdatasync在長時間的執行,此時redis意外關閉會造成文件里不多于兩秒的數據丟失。如果fdatasync 運行正常,redis意外關閉沒有影響,只有當操作系統crash時才會造成少于1秒的數據丟失。解決方法:最后發現,原來是AOF rewrite時一直埋頭的調用write(2),由系統自己去觸發sync。在RedHat Enterprise 6里,默認配置vm.dirty_background_ratio=10,也就是占用了10%的可用內存才會開始后臺flush,而我的服務器有64G 內存。很明顯一次flush太多數據會造成阻塞,所以最后果斷設置了sysctl vm.dirty_bytes=33554432(32M),問題解決。

然后提了個issue,AOF rewrite時定時也執行一下fdatasync嘛, antirez三分鐘后就回復了,新版中,AOF rewrite時32M就會重寫主動調用fdatasync。

6、高可用(2)- 主從復制

1、概述

slave可以在配置文件、啟動命令行參數、以及redis-cli執行SlaveOf指令來設置自己是奴隸。測試表明同步延時非常小,指令一旦執行完畢就會立刻寫AOF文件和向Slave轉發,除非Slave自己被阻塞住了。比較蠢的是,即使在配置文件里設了slavof,slave啟動時依然會先從數據文件載入一堆沒用的數據,再去執行slaveof。“Slaveof no one”,立馬變身master。2.8 版本將支持PSYNC部分同步,master會撥出一小段內存來存放要發給slave的指令,如果slave短暫的斷開了,重連時會從內存中讀取需要補讀 的指令,這樣就不需要斷開兩秒也搞一次全同步了。但如果斷開時間較長,已經超過了內存中保存的數據,就還是要全同步。Slave也可以接收Read-Only的請求。

2、slaveof執行過程

完全重用已有功能,非常經濟先執行一次全同步 — 請求master BgSave出自己的一個RDB Snapshot文件發給slave,slave接收完畢后,清除掉自己的舊數據,然后將RDB載入內存。再進行增量同步 — master作為一個普通的client連入slave,將所有寫操作轉發給slave,沒有特殊的同步協議。

3 Trouble Shooting again

有時候明明master/slave都活得好好的,突然間就說要重新進行全同步了:

1.Slave顯示:# MASTER time out: no data nor PING received…

slave 會每隔repl-ping-slave-period(默認10秒)ping一次master,如果超過repl-timeout(默認60秒)都沒有收 到響應,就會認為Master掛了。如果Master明明沒掛但被阻塞住了也會報這個錯。可以適當調大repl-timeout。

2.Master 顯示:# Client addr=10.175.162.123:44670 flags=S oll=104654 omem=2147487792 events=rw cmd=sync scheduled to be closed ASAP for overcoming of output buffer limits.

當 slave沒掛但被阻塞住了,比如正在loading Master發過來的RDB, Master的指令不能立刻發送給slave,就會放在output buffer中(見oll是命令數量,omem是大小),在配置文件中有如下配置:client-output-buffer-limit slave 256mb 64mb 60, 這是說負責發數據給slave的client,如果buffer超過256m或者連續60秒超過64m,就會被立刻強行關閉!!! Traffic大的話一定要設大一點。否則就會出現一個很悲劇的循環,Master傳輸一個大的RDB給Slave,Slave努力的裝載,但還沒裝載 完,Master對client的緩存滿了,再來一次。

平時可以在master執行 redis-cli client list 找那個cmd=sync,flag=S的client,注意OMem的變化。

7、高可用(3)- Fail-over

Redis-sentinel是2.6版開始加入的另一組獨立運行的節點,提供自動Fail Over的支持。

官方文檔 與 Redis核心解讀–集群管理工具(Redis-sentinel)antirez 對 Sentinel的反駁,與下篇

1 、主要執行過程

Sentinel每秒鐘對所有master,slave和其他sentinel執行Ping,redis-server節點要應答+PONG或-LOADING或-MASTERDOWN.如果某一臺Sentinel沒有在30秒內(可配置得短一些哦)收到上述正確應答,它就會認為master處于sdown狀態(主觀Down)它向其他sentinel詢問是否也認為該master倒了(SENTINEL is-master-down-by-addr ), 如果quonum臺(默認是2)sentinel在5秒鐘內都這樣認為,就會認為master真是odown了(客觀Down)。此時會選出一臺sentinel作為Leader執行fail-over, Leader會從slave中選出一個提升為master(執行slaveof no one),然后讓其他slave指向它(執行slaveof new master)。

2、 master/slave 及 其他sentinel的發現

master 地址在sentinel.conf里, sentinel會每10秒一次向master發送INFO,知道master的slave有哪些。 如果master已經變為slave,sentinel會分析INFO的應答指向新的master。以前,sentinel重啟時,如果master已經 切換過了,但sentinel.conf里master的地址并沒有變,很可能有悲劇發生。另外master重啟后如果沒有切換成slave,也可能有悲 劇發生。新版好像修復了一點這個問題,待研究。

另 外,sentinel會在master上建一個pub/sub channel,名為”sentinel:hello”,通告各種信息,sentinel們也是通過接收pub/sub channel上的+sentinel的信息發現彼此,因為每臺sentinel每5秒會發送一次自己的host信息,宣告自己的存在。

3、自定義reconfig腳本

sentinel在failover時還會執行配置文件里指定的用戶自定義reconfig腳本,做用戶自己想做的事情,比如讓master變為slave并指向新的master。腳 本的將會在命令行按順序傳入如下參數: <master-name> <role(leader/observer)> <state(上述三種情況)> <from-ip> <from-port> <to-ip> <to-port>腳本返回0是正常,如果返回1會被重新執行,如果返回2或以上不會。 如果超過60秒沒返回會被強制終止。覺得Sentinel至少有兩個可提升的地方:

一是如果master 主動shutdown,比如系統升級,有辦法主動通知sentinel提升新的master,減少服務中斷時間。二是比起redis-server太原始了,要自己丑陋的以nohup sentinel > logfile 2>&1 & 啟動,也不支持shutdown命令,要自己kill pid。

4、Client的高可用性

基 于Sentinel的方案,client需要執行語句SENTINEL get-master-addr-by-name mymaster 可獲得當前master的地址。 Jedis正在集成sentinel,已經支持了sentinel的一些指令,但還沒發布,但sentinel版的連接池則暫時完全沒有,在公司的項目里 我參考網友的項目自己寫了一個。

淘 寶的Tedis driver,使用了完全不同的思路,不基于Sentinel,而是多寫隨機讀, 一開始就同步寫入到所有節點,讀的話隨便讀一個還活著的節點就行了。但有些節點成功有些節點失敗如何處理? 節點死掉重新起來后怎么重新同步?什么時候可以重新Ready? 所以不是很敢用。

另外如Ruby寫的redis_failover,也是拋開了Redis Sentinel,基于ZooKeeper的臨時方案。

Redis作者也在博客里抱怨怎么沒有人做Dynamo-style 的client。

8、數據一致性:事務

      用Multi(Start Transaction)、Exec(Commit)、Discard(Rollback)實現。 在事務提交前,不會執行任何指令,只會把它們存到一個隊列里,不影響其他客戶端的操作。在事務提交時,批量執行所有指令。《Redis設計與實現》中的詳述。

注意,Redis里的事務,與我們平時的事務概念很不一樣:

它僅僅是保證事務里的操作會被連續獨占的執行。因為是單線程架構,在執行完事務內所有指令前是不可能再去同時執行其他客戶端的請求的。它沒有隔離級別的概念,因為事務提交前任何指令都不會被實際執行,也就不存在”事務內的查詢要看到事務里的更新,在事務外查詢不能看到”這個讓人萬分頭痛的問題。它不保證原子性——所有指令同時成功或同時失敗,只有決定是否開始執行全部指令的能力,沒有執行到一半進行回滾的能力。在redis里失敗分兩種,一種是明顯的指令錯誤,比如指令名拼錯,指令參數個數不對,在2.6版中全部指令都不會執行。另一種是隱含的,比如在事務里,第一句是SET foo bar, 第二句是LLEN foo,對第一句產生的String類型的key執行LLEN會失敗,但這種錯誤只有在指令運行后才能發現,這時候第一句成功,第二句失敗。還有,如果事務執行到一半redis被KILL,已經執行的指令同樣也不會被回滾。Watch指令,類似樂觀鎖,事務提交時,如果Key的值已被別的客戶端改變,比如某個list已被別的客戶端push/pop過了,整個事務隊列都不會被執行。

9、內存淘汰清理機制

官方文檔 與 《Redis設計與實現》中的詳述,過期數據的清除從來不容易,為每一條key設置一個timer,到點立刻刪除的消耗太大,每秒遍歷所有數據消耗也大,Redis使用了一種相對務實的做法: 當client主動訪問key會先對key進行超時判斷,過時的key會立刻刪除。 如果clien永遠都不再get那條key呢? 它會在Master的后臺,每秒10次的執行如下操作: 隨機選取100個key校驗是否過期,如果有25個以上的key過期了,立刻額外隨機選取下100個key(不計算在10次之內)。可見,如果過期的key不多,它最多每秒回收200條左右,如果有超過25%的key過期了,它就會做得更多,但只要key不被主動get,它占用的內存什么時候最終被清理掉只有天知道。

redis為了更好地實現這個功能,必須為不同的應用場景提供不同的策略,內存淘汰策略講的是為實現內存淘汰我們具體怎么做,要解決的問題包括淘汰鍵空間如何選擇?在鍵空間中淘汰鍵如何選擇?

Redis提供了下面幾種淘汰策略供用戶選擇,其中默認的策略為noeviction策略:

noeviction:當內存使用達到閾值的時候,所有引起申請內存的命令會報錯。

allkeys-lru:在主鍵空間中,優先移除最近未使用的key。

volatile-lru:在設置了過期時間的鍵空間中,優先移除最近未使用的key。

allkeys-random:在主鍵空間中,隨機移除某個key。

volatile-random:在設置了過期時間的鍵空間中,隨機移除某個key。

volatile-ttl:在設置了過期時間的鍵空間中,具有更早過期時間的key優先移除。

提示:主鍵空間和設置了過期時間的鍵空間,舉個例子,假設我們有一批鍵存儲在Redis中,則有那么一個哈希表用于存儲這批鍵及其值,如果這批鍵中有一部分設置了過期時間,那么這批鍵還會被存儲到另外一個哈希表中,這個哈希表中的值對應的是鍵被設置的過期時間。設置了過期時間的鍵空間為主鍵空間的子集。

如何選擇淘汰策略

我們了解了Redis大概提供了這么幾種淘汰策略,那么如何選擇呢?淘汰策略的選擇可以通過下面的配置指定:

# maxmemory-policy noeviction

但是這個值填什么呢?為解決這個問題,我們需要了解我們的應用請求對于Redis中存儲的數據集的訪問方式以及我們的訴求是什么。同時Redis也支持Runtime修改淘汰策略,這使得我們不需要重啟Redis實例而實時的調整內存淘汰策略。

下面看看幾種策略的適用場景:

allkeys-lru:如果我們的應用對緩存的訪問符合冪律分布(也就是存在相對熱點數據),或者我們不太清楚我們應用的緩存訪問分布狀況,我們可以選擇allkeys-lru策略。

allkeys-random:如果我們的應用對于緩存key的訪問概率相等,則可以使用這個策略。

volatile-ttl:這種策略使得我們可以向Redis提示哪些key更適合被eviction。

另外,volatile-lru策略和volatile-random策略適合我們將一個Redis實例既應用于緩存和又應用于持久化存儲的時候,然而我們也可以通過使用兩個Redis實例來達到相同的效果,值得一提的是將key設置過期時間實際上會消耗更多的內存,因此我們建議使用allkeys-lru策略從而更有效率的使用內存。

非精準的LRU

上面提到的LRU(Least Recently Used)策略,實際上Redis實現的LRU并不是可靠的LRU,也就是名義上我們使用LRU算法淘汰鍵,但是實際上被淘汰的鍵并不一定是真正的最久沒用的,這里涉及到一個權衡的問題,如果需要在全部鍵空間內搜索最優解,則必然會增加系統的開銷,Redis是單線程的,也就是同一個實例在每一個時刻只能服務于一個客戶端,所以耗時的操作一定要謹慎。為了在一定成本內實現相對的LRU,早期的Redis版本是基于采樣的LRU,也就是放棄全部鍵空間內搜索解改為采樣空間搜索最優解。自從Redis3.0版本之后,Redis作者對于基于采樣的LRU進行了一些優化,目的是在一定的成本內讓結果更靠近真實的LRU。

10、總結

根據業務需要選擇合適的數據類型,并為不同的應用場景設置相應的緊湊存儲參數。當業務場景不需要數據持久化時,關閉所有的持久化方式可以獲得最佳的性能以及最大的內存使用量。如果需要使用持久化,根據是否可以容忍重啟丟失部分數據在快照方式與語句追加方式之間選擇其一,不要使用虛擬內存以及diskstore方式。不要讓你的Redis所在機器物理內存使用超過實際內存總量的3/5。


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 茶陵县| 乾安县| 平邑县| 涟水县| 墨脱县| 长汀县| 德庆县| 和静县| 休宁县| 安远县| 鱼台县| 东城区| 扎鲁特旗| 陇川县| 城步| 泸水县| 武胜县| 巴楚县| 鹤庆县| 五家渠市| 景洪市| 偃师市| 长子县| 南宁市| 肥西县| 收藏| 岳阳县| 中西区| 分宜县| 江油市| 永登县| 迭部县| 青铜峡市| 玛曲县| 德州市| 西宁市| 平谷区| 连山| 茌平县| 万载县| 新安县|