1. 程式人生 > 實用技巧 >尚矽谷Redis學習總結

尚矽谷Redis學習總結

Redis簡介

簡單介紹

Redis:開源、免費、非關係型資料庫、K-V資料庫、記憶體資料庫,支援持久化、事務和備份,叢集(支援16個庫)等高可用功能。並且效能極高(可以達到100000+的QPS),易擴充套件,豐富的資料型別,所有操作都是單執行緒,原子性的。

NOSQL:非關係型資料庫,資料與資料之間沒有關聯關係。就是為了解決大規模資料集合多重資料種類帶來的挑戰,尤其是大資料應用難題

型別

  1. 鍵值(key-value)儲存資料庫

  2. 列儲存資料庫:鍵仍然存在,但是指向了多個列,HBase (eg:部落格平臺(標籤和文章),日誌)

  3. 文件型資料庫 MongoDb (eg:淘寶商品的評價)

  4. 圖形資料庫 Neo4j (eg:好友列表)

擴充套件:

MongoDB是一個基於分散式檔案儲存的資料庫。有C++語言編寫。旨在為WEB應用提供可擴充套件的高效能資料儲存解決方案。

MongoDB是一個介於關係型資料庫和非關係型資料庫之間的產品,是非關係資料庫當中功能最豐富,最像關係型資料庫的。

文件(document)是MongoDB中資料的基本單元,非常類似於關係型資料庫系統中的行(但是比行要複雜的多); 集合(collection)就是一組文件,如果說MongoDB中的文件類似於關係型資料庫中的行,那麼集合就如同表;

使用場景:

  1. 資料模型比較簡單

  2. 需要靈活更強的IT系統

  3. 對資料庫效能要求比較高

  4. 不需要高度的資料一致性

  5. 對於給定的key,比較容易映射覆雜值的環境

SQL:關係型資料庫,表與表之間建立關聯關係

redis的安裝

#拉取映象
docker pull redis
#掛載資料卷並執行容器
docker run -p 6379:6379 --name redis -v /root/redis/data:/data -v /root/redis/conf/redis.conf:/usr/local/etc/redis/redis.conf -d redis redis-server /usr/local/etc/redis/redis.conf --appendonly yes --requirepass "xxx"

為什麼使用NOSQL

單機 MySQL 的美好時代

在90年代,一個網站的訪問量一般都不大,用單個數據庫完全可以輕鬆應付。 在那個時候,更多的都是靜態網頁,動態互動型別的網站不多。

DAL : Data Access Layer(資料訪問層 – Hibernate,MyBatis)

上述架構下,我們來看看資料儲存的瓶頸是什麼?

  1. 資料量的總大小一個機器放不下時。

  2. 資料的索引(B+ Tree)一個機器的記憶體放不下時。

  3. 訪問量(讀寫混合)一個例項不能承受。

如果滿足了上述1 or 3個時,只能對資料庫的整體架構進行重構。

Memcached(快取)+MySQL+垂直拆分

關於垂直拆分和水平拆分的簡單介紹

後來,隨著訪問量的上升,幾乎大部分使用MySQL架構的網站在資料庫上都開始出現了效能問題,web程式不再僅僅專注在功能上,同時也在追求效能。程式設計師們開始大量的使用快取技術來緩解資料庫的壓力,優化資料庫的結構和索引。開始比較流行的是通過檔案快取來緩解資料庫壓力,但是當訪問量繼續增大的時候,多臺web機器通過檔案快取不能共享,大量的小檔案快取也帶了了比較高的IO壓力。在這個時候,Memcached就自然的成為一個非常時尚的技術產品。

Memcached作為一個獨立的分散式的快取伺服器,為多個web伺服器提供了一個共享的高效能快取服務,在Memcached伺服器上,又發展了根據hash演算法來進行多臺Memcached快取服務的擴充套件,然後又出現了一致性hash來解決增加或減少快取伺服器導致重新hash帶來的大量快取失效的弊端。

Mysql主從讀寫分離

由於資料庫的寫入壓力增加,Memcached只能緩解資料庫的讀取壓力。讀寫集中在一個數據庫上讓資料庫不堪重負,大部分網站開始使用主從複製技術來達到讀寫分離,以提高讀寫效能和讀庫的可擴充套件性。Mysql的master-slave模式成為這個時候的網站標配了。

分庫分表+水平拆分+mysql叢集

在Memcached的快取記憶體,MySQL的主從複製,讀寫分離的基礎之上,這時MySQL主庫的寫壓力開始出現瓶頸,而資料量的持續猛增,由於MyISAM在寫資料的時候會使用表鎖,在高併發寫資料的情況下會出現嚴重的鎖問題,大量的高併發MySQL應用開始使用InnoDB引擎代替MyISAM。

ps:這就是為什麼 MySQL 在 5.6 版本之後使用 InnoDB 做為預設儲存引擎的原因 – MyISAM 寫會鎖表,InnoDB 有行鎖,,並且是事務優先,發生衝突的機率低,併發效能高。

注意鎖的幾個概念:行鎖和表鎖,讀鎖和寫鎖,樂觀鎖和悲觀鎖,還有一個間隙鎖

詳情請看鎖的介紹

四種NoSQL對比

3V+3高

大資料時代的3V 網際網路需求的3高 海量Volumn 3v 多樣Variety 實時Velocity 高併發 3高 高可用 高效能

ACID

事務是由一組SQL語句組成的邏輯處理單元,事務具有4屬性,通常稱為事務的ACID屬性。

  • 原子性(Actomicity):事務是一個原子操作單元,其對資料的修改,要麼全都執行,要麼全都不執行。

  • 一致性(Consistent):在事務開始和完成時,資料都必須保持一致狀態。這意味著所有相關的資料規則都必須應用於事務的修改,以操持完整性;事務結束時,所有的內部資料結構(如B樹索引或雙向連結串列)也都必須是正確的。

    eg:有3個人進行轉賬操作,為了保證一致性(即3個人 的賬號金額總數不變),那在我寫程式碼的時候,如果寫了程式碼:A=A-5000;此時資料時不一致的。那就必須要寫上,B=B+5000,或者是C=C+5000,這樣的程式碼才能保證了資料庫的一致性狀態。

  • 隔離性(Isolation):資料庫系統提供一定的隔離機制,保證事務在不受外部併發操作影響的“獨立”環境執行。這意味著事務處理過程中的中間狀態對外部是不可見的,反之亦然。具體看下面的幾個隔離級別和併發問題。

  • 永續性(Durable):事務完成之後,它對於資料的修改是永久性的,即使出現系統故障也能夠保持

CAP

C:consistency,資料在多個副本中能保持一致的狀態。

A:Availability,整個系統在任何時刻都能提供可用的服務,通常達到99.99%四個九可以稱為高可用

P:Partition tolerance,分割槽容錯性,在分散式中,由於網路的原因無法避免有時候出現數據不一致的情況,系統如果不能在時限內達成資料一致性,就意味著發生了分割槽的情況,必須就當前操作在C和A之間做出選擇,換句話說,系統可以跨網路分割槽線性的伸縮和擴充套件。

CAP理論的核心:一個分散式系統不可能同時很好的滿足一致性,可用性和分割槽容錯性這三個需求,最多隻能同時較好的滿足兩個

  • CA:單點叢集,滿足一致性,可用性的系統,通常在可擴充套件上不太強大。應用:傳統的Oracle資料庫

  • CP:滿足一致性,分割槽容錯性的系統,通常效能不是特別高。應用:Redis,MongoDB,銀行

  • AP:滿足可用性,分割槽容錯性,通常可能對一致性要求低一些。應用:大多數網站架構的選擇

CAP理論就是說在分散式儲存系統中,最多隻能實現上面的兩個。而由於當前的網路硬體肯定會出現延遲丟包等問題。所以

分割槽容忍性是我們必須需要實現的

所以我們只能在一致性和高可用之間進行權衡,沒有NoSQL系統能同時保證三點。為什麼呢?

為何CAP三者不可兼得

現在我們就來證明一下,為什麼不能同時滿足三個特性?

假設有兩臺伺服器,一臺放著應用A和資料庫V,一臺放著應用B和資料庫V,他們之間的網路可以互通,也就相當於分散式系統的兩個部分。

在滿足一致性的時候,兩臺伺服器 N1和N2,一開始兩臺伺服器的資料是一樣的,DB0=DB0。在滿足可用性的時候,使用者不管是請求N1或者N2,都會得到立即響應。在滿足分割槽容錯性的情況下,N1和N2有任何一方宕機,或者網路不通的時候,都不會影響N1和N2彼此之間的正常運作。

當用戶通過N1中的A應用請求資料更新到伺服器DB0後,這時N1中的伺服器DB0變為DB1,通過分散式系統的資料同步更新操作,N2伺服器中的資料庫V0也更新為了DB1,這時,使用者通過B向資料庫發起請求得到的資料就是即時更新後的資料DB1。

上面是正常運作的情況,但分散式系統中,最大的問題就是網路傳輸問題,現在假設一種極端情況,N1和N2之間的網路斷開了,但我們仍要支援這種網路異常,也就是滿足分割槽容錯性,那麼這樣能不能同時滿足一致性和可用性呢?

假設N1和N2之間通訊的時候網路突然出現故障,有使用者向N1傳送資料更新請求,那N1中的資料DB0將被更新為DB1,由於網路是斷開的,N2中的資料庫仍舊是DB0;

如果這個時候,有使用者向N2傳送資料讀取請求,由於資料還沒有進行同步,應用程式沒辦法立即給使用者返回最新的資料DB1,怎麼辦呢?有二種選擇,第一,犧牲資料一致性,響應舊的資料DB0給使用者;第二,犧牲可用性,阻塞等待,直到網路連線恢復,資料更新操作完成之後,再給使用者響應最新的資料DB1。

上面的過程比較簡單,但也說明了要滿足分割槽容錯性的分散式系統,只能在一致性和可用性兩者中,選擇其中一個。也就是說分散式系統不可能同時滿足三個特性。這就需要我們在搭建系統時進行取捨了。

Base

Base就是為了解決關係型資料庫強一致性引起的問題而引起的可用性降低而提出的解決方案。

Base其實是下面三個術語的縮寫:

  • 基本可用(Basically Available)

  • 軟狀態(Soft state)狀態可以有一段時間不同步

  • 最終一致(Eventually consistent)最終資料是一致的就可以了,而不是時時保持強一致

它的思想是通過讓系統放鬆對某一時刻資料一致性的要求來換取系統整體伸縮性和效能上改觀。為什麼這麼說呢,緣由就在於大型系統往往由於地域分佈和極高效能的要求,不可能採用分散式事務來完成這些指標,要想獲得這些指標,我們必須採用另外一種方式來完成,這裡BASE就是解決這個問題的辦法。

案例轉賬為例,我們把使用者A給使用者B轉賬分成四個階段,第一個階段使用者A準備轉賬,第二個階段從使用者A賬戶扣減餘額,第三個階段對使用者B增加餘額,第四個階段完成轉賬。系統需要記錄操作過程中每一步驟的狀態,一旦系統出現故障,系統能夠自動發現沒有完成的任務,然後,根據任務所處的狀態,繼續執行任務,最終完成任務,達到一致的最終狀態。

在實際應用中,上面這個過程通常是通過持久化執行任務的狀態和環境資訊,一旦出現問題,定時任務會撈取未執行完的任務,繼續未執行完的任務,直到執行完成為止,或者取消已經完成的部分操作回到原始狀態。這種方法在任務完成每個階段的時候,都要更新資料庫中任務的狀態,這在大規模高併發系統中不會有太好的效能,一個更好的辦法是用Write-Ahead Log(寫前日誌),這和資料庫的Bin Log(操作日誌)相似,在做每一個操作步驟,都先寫入日誌,如果操作遇到問題而停止的時候,可以讀取日誌按照步驟進行恢復,並且繼續執行未完成的工作,最後達到一致。寫前日誌可以利用機械硬碟的追加寫而達到較好效能,因此,這是一種專業化的實現方式,多數業務繫系統還是使用資料庫記錄的欄位來記錄任務的執行狀態,也就是記錄中間的“軟狀態”,一個任務的狀態流轉一般可以通過資料庫的行級鎖來實現,這比使用Write-Ahead Log實現更簡單、更快速。

分散式和叢集

分散式:不同的多臺伺服器上面部署不同的服務模組(工程)

叢集:不同的多臺伺服器上面部署相同的服務模組。通過分散式排程軟體進行統一的排程,對外提供服務和訪問。

Redis的資料型別

Redis五種資料型別:string、hash、list、set、zset

公用命令

  • del key

  • dump key:序列化給定key,返回被序列化的值

  • exists key:檢查key是否存在

  • expire key second:為key設定過期時間,以秒計算,可以不寫second,預設為秒

  • ttl key:返回key剩餘時間,-1為永久,-2為失效

  • persist key:移除key的過期時間,key將持久儲存

  • keys pattern:查詢所有符號給定模式的key eg:keys *

  • randomkey:隨機返回一個key

  • rename key newkey:修改key的名稱

  • move key db:移動key至指定資料庫中 eg:move a 1

  • type key:返回key所儲存的值的型別

expirekey second的使用場景 1、限時的優惠活動 2、網站資料快取 3、手機驗證碼 4、限制網站訪客頻率

key的命名建議

  1. key不要太長,儘量不要超過1024位元組。不僅消耗記憶體,也會降低查詢的效率

  2. key不要太短,太短可讀性會降低

  3. 在一個專案中,key最好使用統一的命名模式,如user:123:password

  4. key區分大小寫

String

string型別是二進位制安全的,redis的string可以包含任何資料,如影象、序列化物件。一個鍵最多能儲存512MB。二進位制安全是指,在傳輸資料的時候,能保證二進位制資料的資訊保安,也就是不會被篡改、破譯;如果被攻擊,能夠及時檢測出來

  • set key_name value:命令不區分大小寫,但是key_name區分大小寫

  • setnx key value:當key不存在時設定key的值。(SET if Not eXists),分散式鎖的問題

  • setex:建立一個key,並且設定他的過期時間

  • get key_name

  • getrange key start end:獲取key中字串的子字串,從start開始,end結束

  • setrange key offset value:設定從offset往後的值

  • mget key1 [key2 …]:獲取多個key

  • getset key_name value:返回key的舊值,並設定key的值。當key不存在,返回nil

  • strlen key:返回key所儲存的字串的長度

  • incr key_name :INCR命令key中儲存的值+1,如果不存在key,則key中的值話先被初始化為0再加1

  • INCRBY KEY_NAME 增量

  • DECR KEY_NAME:key中的值自減一

  • DECRBY KEY_NAME

  • append key_name value:字串拼接,追加至末尾,如果不存在,為其賦值

String應用場景 1、String通常用於儲存單個字串或JSON字串資料 2、因為String是二進位制安全的,所以可以把保密要求高的圖片檔案內容作為字串來儲存 3、計數器:常規Key-Value快取應用,如微博數、粉絲數。INCR本身就具有原子性特性,所以不會有執行緒安全問題

hash

Redis hash是一個string型別的field和value的對映表,hash特別適用於儲存物件。每個hash可以儲存232-1(40億左右)鍵值對。可以看成KEY和VALUE的MAP容器。相比於JSON,hash佔用很少的記憶體空間。

常用命令

  • hset key_name field value:為指定的key設定field和value

  • hmset key field value[field1,value1]

  • hsetnx:當不存在才建立該field

  • hget key field

  • hmget key field[field1]

  • hgetall key:返回hash表中所有欄位和值

  • hkeys key:獲取hash表所有欄位

  • hvals key:獲取hash表所有值

  • hlen key:獲取hash表中的欄位數量

  • hdel key field [field1]:刪除一個或多個hash表的欄位

  • hexists:在key裡面是否存在指定的field

  • hincrby key field increment:增加某個field的值

應用場景

Hash的應用場景,通常用來儲存一個使用者資訊的物件資料。

  1. 相比於儲存物件的string型別的json串,json串修改單個屬性需要將整個值取出來。而hash不需要。

  2. 相比於多個key-value儲存物件,hash節省了很多記憶體空間

  3. 如果hash的屬性值被刪除完,那麼hash的key也會被redis刪除

list

類似於Java中的LinkedList。

常用命令

  • lpush key value1 [value2]:從左側插入,右邊的先出,相當於一個棧

  • eg:lpush list 1 2 3 lrange list 0 -1 輸出:3 2 1

  • rpush key value1 [value2]: 從右側插入,左邊的先出

  • eg:rpush list 1 2 3 lrange list 0 -1 輸出:1 2 3

  • lpushx key value:從左側插入值,如果list不存在,則不操作

  • rpushx key value:從右側插入值,如果list不存在,則不操作

  • llen key:獲取列表長度

  • lindex key index:獲取指定索引的元素,從零開始

  • lrange key start stop:獲取列表指定範圍的元素

  • lpop key :從左側移除第一個元素

  • prop key:移除列表最後一個元素

  • irem:刪除指定個數的同一元素

  • eg:irem list 2 3 刪掉了集合中的兩個三

  • blpop key [key1] timeout:移除並獲取列表第一個元素,如果列表沒有元素會阻塞列表到等待超時或發現可彈出元素為止

  • brpop key [key1] timeout:移除並獲取列表最後一個元素,如果列表沒有元素會阻塞列表到等待超時或發現可彈出元素為止

  • ltrim key start stop :對列表進行修改,讓列表只保留指定區間的元素,不在指定區間的元素就會被刪除

  • eg:list1中元素1 2 3 4 5 ltrim list1 2 3 list1剩餘元素:3 4

  • lset key index value :指定索引的值

  • linsert key before|after world value:在列表元素前或則後插入元素

應用場景

  1. 對資料大的集合資料刪減

    列表顯示、關注列表、粉絲列表、留言評價...分頁、熱點新聞等

  2. 任務佇列 list通常用來實現一個訊息佇列,而且可以確保先後順序,不必像MySQL那樣通過order by來排序

補充:

  • rpoplpush list1 list2 移除list1最後一個元素,並將該元素新增到list2並返回此元素 用此命令可以實現訂單下單流程、使用者系統登入註冊簡訊等。

效能總結

它是一個字串連結串列,left、right都可以插入新增; 如果鍵不存在,建立新的連結串列; 如果鍵已存在,新增內容; 如果值全移除,對應的鍵也就消失了。 連結串列的操作無論是頭和尾效率都極高,但假如是對中間元素進行操作,效率就很慘淡了。

set

唯一、無序

  • sadd key value1[value2]:向集合新增成員

  • scard key:返回集合成員數

  • smembers key:返回集合中所有成員

  • sismember key member:判斷memeber元素是否是集合key成員的成員

  • srandmember key [count]:返回集合中一個或多個隨機數

  • srem key member1 [member2]:移除集合中一個或多個成員

  • spop key:移除並返回集合中的一個隨機元素

  • smove source destination member:將member元素從source集合移動到destination集合

  • sdiff key1 [key2]:返回給定的第一個集合和其他集合的差集(即在key1中的值而在其他key中找不到)

  • sdiffstore destination key1[key2]:返回給定的第一個集合與其他的集合的差集並存儲在destination中

    eg:set1:1 2 3 set2:3 4 5 6 sdiffstore set3 set1 set2 smembers set3 result:1 2

  • sinter key1 [key2]:返回所有集合的交集

  • sunion key1 [key2]:返回所有集合的並集

對兩個集合間的資料[計算]進行交集、並集、差集運算 1、以非常方便的實現如共同關注、共同喜好、二度好友等功能。對上面的所有集合操作,你還可以使用不同的命令選擇將結果返回給客戶端還是儲存到一個新的集合中。 2、利用唯一性,可以統計訪問網站的所有獨立 IP

zset

有序且不重複。每個元素都會關聯一個double型別的分數,Redis通過分數進行從小到大的排序。分數可以重複

  • zadd key score1 memeber1

  • zcard key :獲取集合中的元素數量

  • zcount key min max 計算在有序集合中指定區間分數的成員數

  • zcount key min max 計算在有序集合中指定區間分數的成員數

  • zrange key start stop 指定輸出索引範圍內的成員

  • zrangebyscore key min max 指定輸出score區間內的成員

  • zrank key member:返回有序集合指定成員的索引

  • zrevrange key start stop :返回有序集中指定區間內的成員,通過索引,分數從高到底

  • zrem key member [member …] 移除有序集合中的一個或多個成員

  • zremrangebyrank key start stop 移除有序集合中給定的索引區間的所有成員(第一名是0)(低到高排序)

  • zremrangebyscore key min max 移除有序集合中給定的分數區間的所有成員

常用於排行榜:

  1. 如推特可以以發表時間作為score來儲存

  2. 儲存成績

  3. 還可以用zset來做帶權重的佇列,讓重要的任務先執行

解析配置檔案

#是否在後臺執行;no:不是後臺執行
daemonize yes

#是否開啟保護模式,預設開啟。要是配置裡沒有指定bind和密碼。開啟該引數後,redis只會本地進行訪問,拒絕外部訪問。
protected-mode yes

#redis的程序檔案
pidfile /var/run/redis/redis-server.pid

#redis監聽的埠號。
port 6379

#此引數確定了TCP連線中已完成佇列(完成三次握手之後)的長度, 當然此值必須不大於Linux系統定義的/proc/sys/net/core/somaxconn值,預設是511,而Linux的預設引數值是128。當系統併發量大並且客戶端速度緩慢的時候,可以將這二個引數一起參考設定。該核心引數預設值一般是128,對於負載很大的服務程式來說大大的不夠。一般會將它修改為2048或者更大。在/etc/sysctl.conf中新增:net.core.somaxconn = 2048,然後在終端中執行sysctl -p。
tcp-backlog 511

#指定 redis 只接收來自於該 IP 地址的請求,如果不進行設定,那麼將處理所有請求
#bind 127.0.0.1
#bind 0.0.0.0

#配置unix socket來讓redis支援監聽本地連線。
# unixsocket /var/run/redis/redis.sock

#配置unix socket使用檔案的許可權
# unixsocketperm 700

# 此引數為設定客戶端空閒超過timeout,服務端會斷開連線,為0則服務端不會主動斷開連線,不能小於0。
timeout 0

#tcp keepalive引數。如果設定不為0,就使用配置tcp的SO_KEEPALIVE值,使用keepalive有兩個好處:檢測掛掉的對端。降低中間裝置出問題而導致網路看似連線卻已經與對端埠的問題。在Linux核心中,設定了keepalive,redis會定時給對端傳送ack。檢測到對端關閉需要兩倍的設定值。
tcp-keepalive 0

#指定了服務端日誌的級別。級別包括:debug(很多資訊,方便開發、測試),verbose(許多有用的資訊,但是沒有debug級別資訊多),notice(適當的日誌級別,適合生產環境),warn(只有非常重要的資訊)
loglevel notice

#指定了記錄日誌的檔案。空字串的話,日誌會列印到標準輸出裝置。後臺執行的redis標準輸出是/dev/null。
logfile /var/log/redis/redis-server.log

#是否開啟記錄syslog功能
# syslog-enabled no

#syslog的識別符號。
# syslog-ident redis

#日誌的來源、裝置
# syslog-facility local0

#資料庫的數量,預設使用的資料庫是DB 0。可以通過SELECT命令選擇一個db
databases 16

# redis是基於記憶體的資料庫,可以通過設定該值定期寫入磁碟。
# 註釋掉“save”這一行配置項就可以讓儲存資料庫功能失效
# 900秒(15分鐘)內至少1個key值改變(則進行資料庫儲存--持久化)
# 300秒(5分鐘)內至少10個key值改變(則進行資料庫儲存--持久化)
# 60秒(1分鐘)內至少10000個key值改變(則進行資料庫儲存--持久化)
save 900 1
save 300 10
save 60 10000

#當RDB持久化出現錯誤後,是否依然進行繼續進行工作,yes:不能進行工作,no:可以繼續進行工作,可以通過info中的rdb_last_bgsave_status瞭解RDB持久化是否有錯誤
stop-writes-on-bgsave-error yes

#使用壓縮rdb檔案,rdb檔案壓縮使用LZF壓縮演算法,yes:壓縮,但是需要一些cpu的消耗。no:不壓縮,需要更多的磁碟空間
rdbcompression yes

#是否校驗rdb檔案。從rdb格式的第五個版本開始,在rdb檔案的末尾會帶上CRC64的校驗和。這跟有利於檔案的容錯性,但是在儲存rdb檔案的時候,會有大概10%的效能損耗,所以如果你追求高效能,可以關閉該配置。
rdbchecksum yes

#rdb檔案的名稱
dbfilename dump.rdb

#資料目錄,資料庫的寫入會在這個目錄。rdb、aof檔案也會寫在這個目錄
dir /data


############### 主從複製 ###############

#複製選項,slave複製對應的master。
# slaveof <masterip> <masterport>

#如果master設定了requirepass,那麼slave要連上master,需要有master的密碼才行。masterauth就是用來配置master的密碼,這樣可以在連上master後進行認證。
# masterauth <master-password>

#當從庫同主機失去連線或者複製正在進行,從機庫有兩種執行方式:1) 如果slave-serve-stale-data設定為yes(預設設定),從庫會繼續響應客戶端的請求。2) 如果slave-serve-stale-data設定為no,除去INFO和SLAVOF命令之外的任何請求都會返回一個錯誤”SYNC with master in progress”。
slave-serve-stale-data yes

#作為從伺服器,預設情況下是隻讀的(yes),可以修改成NO,用於寫(不建議)。
slave-read-only yes

#是否使用socket方式複製資料。目前redis複製提供兩種方式,disk和socket。如果新的slave連上來或者重連的slave無法部分同步,就會執行全量同步,master會生成rdb檔案。有2種方式:disk方式是master建立一個新的程序把rdb檔案儲存到磁碟,再把磁碟上的rdb檔案傳遞給slave。socket是master建立一個新的程序,直接把rdb檔案以socket的方式發給slave。disk方式的時候,當一個rdb儲存的過程中,多個slave都能共享這個rdb檔案。socket的方式就的一個個slave順序複製。在磁碟速度緩慢,網速快的情況下推薦用socket方式。
repl-diskless-sync no

#diskless複製的延遲時間,防止設定為0。一旦複製開始,節點不會再接收新slave的複製請求直到下一個rdb傳輸。所以最好等待一段時間,等更多的slave連上來。
repl-diskless-sync-delay 5

#slave根據指定的時間間隔向伺服器傳送ping請求。時間間隔可以通過 repl_ping_slave_period 來設定,預設10秒。
# repl-ping-slave-period 10

#複製連線超時時間。master和slave都有超時時間的設定。master檢測到slave上次傳送的時間超過repl-timeout,即認為slave離線,清除該slave資訊。slave檢測到上次和master互動的時間超過repl-timeout,則認為master離線。需要注意的是repl-timeout需要設定一個比repl-ping-slave-period更大的值,不然會經常檢測到超時。
# repl-timeout 60

#是否禁止複製tcp連結的tcp nodelay引數,可傳遞yes或者no。預設是no,即使用tcp nodelay。如果master設定了yes來禁止tcp nodelay設定,在把資料複製給slave的時候,會減少包的數量和更小的網路頻寬。但是這也可能帶來資料的延遲。預設我們推薦更小的延遲,但是在資料量傳輸很大的場景下,建議選擇yes。
repl-disable-tcp-nodelay no

#複製緩衝區大小,這是一個環形複製緩衝區,用來儲存最新複製的命令。這樣在slave離線的時候,不需要完全複製master的資料,如果可以執行部分同步,只需要把緩衝區的部分資料複製給slave,就能恢復正常複製狀態。緩衝區的大小越大,slave離線的時間可以更長,複製緩衝區只有在有slave連線的時候才分配記憶體。沒有slave的一段時間,記憶體會被釋放出來,預設1m。
# repl-backlog-size 5mb

#master沒有slave一段時間會釋放複製緩衝區的記憶體,repl-backlog-ttl用來設定該時間長度。單位為秒。
# repl-backlog-ttl 3600

#當master不可用,Sentinel會根據slave的優先順序選舉一個master。最低的優先順序的slave,當選master。而配置成0,永遠不會被選舉。
slave-priority 100

#redis提供了可以讓master停止寫入的方式,如果配置了min-slaves-to-write,健康的slave的個數小於N,mater就禁止寫入。master最少得有多少個健康的slave存活才能執行寫命令。這個配置雖然不能保證N個slave都一定能接收到master的寫操作,但是能避免沒有足夠健康的slave的時候,master不能寫入來避免資料丟失。設定為0是關閉該功能。
# min-slaves-to-write 3

#延遲小於min-slaves-max-lag秒的slave才認為是健康的slave。
# min-slaves-max-lag 10

# 設定1或另一個設定為0禁用這個特性。
# Setting one or the other to 0 disables the feature.
# By default min-slaves-to-write is set to 0 (feature disabled) and
# min-slaves-max-lag is set to 10.


############### 安全相關 ###############

#requirepass配置可以讓使用者使用AUTH命令來認證密碼,才能使用其他命令。這讓redis可以使用在不受信任的網路中。為了保持向後的相容性,可以註釋該命令,因為大部分使用者也不需要認證。使用requirepass的時候需要注意,因為redis太快了,每秒可以認證15w次密碼,簡單的密碼很容易被攻破,所以最好使用一個更復雜的密碼。注意只有密碼沒有使用者名稱。
# requirepass foobared

#把危險的命令給修改成其他名稱。比如CONFIG命令可以重新命名為一個很難被猜到的命令,這樣使用者不能使用,而內部工具還能接著使用。
# rename-command CONFIG b840fc02d524045429941cc15f59e41cb7be6c52

#設定成一個空的值,可以禁止一個命令
# rename-command CONFIG ""


############### 程序限制相關 ###############

# 設定能連上redis的最大客戶端連線數量。預設是10000個客戶端連線。由於redis不區分連線是客戶端連線還是內部開啟檔案或者和slave連線等,所以maxclients最小建議設定到32。如果超過了maxclients,redis會給新的連線傳送’max number of clients reached’,並關閉連線。
# maxclients 10000

#redis配置的最大記憶體容量。當記憶體滿了,需要配合maxmemory-policy策略進行處理。注意slave的輸出緩衝區是不計算在maxmemory內的。所以為了防止主機記憶體使用完,建議設定的maxmemory需要更小一些。
# maxmemory <bytes>

#記憶體容量超過maxmemory後的處理策略。
#volatile-lru:利用LRU演算法移除設定過過期時間的key。
#volatile-random:隨機移除設定過過期時間的key。
#volatile-ttl:移除即將過期的key,根據最近過期時間來刪除(輔以TTL)
#allkeys-lru:利用LRU演算法移除任何key。
#allkeys-random:隨機移除任何key。
#noeviction:不移除任何key,只是返回一個寫錯誤。
#上面的這些驅逐策略,如果redis沒有合適的key驅逐,對於寫命令,還是會返回錯誤。redis將不再接收寫請求,只接收get請求。寫命令包括:set setnx setex append incr decr rpush lpush rpushx lpushx linsert lset rpoplpush sadd sinter sinterstore sunion sunionstore sdiff sdiffstore zadd zincrby zunionstore zinterstore hset hsetnx hmset hincrby incrby decrby getset mset msetnx exec sort。
# maxmemory-policy noeviction

#lru檢測的樣本數。使用lru或者ttl淘汰演算法,從需要淘汰的列表中隨機選擇sample個key,選出閒置時間最長的key移除。
# maxmemory-samples 5


############### APPEND ONLY 持久化方式 ###############

#預設redis使用的是rdb方式持久化,這種方式在許多應用中已經足夠用了。但是redis如果中途宕機,會導致可能有幾分鐘的資料丟失,根據save來策略進行持久化,Append Only File是另一種持久化方式,可以提供更好的持久化特性。Redis會把每次寫入的資料在接收後都寫入 appendonly.aof 檔案,每次啟動時Redis都會先把這個檔案的資料讀入記憶體裡,先忽略RDB檔案。
appendonly no

#aof檔名
appendfilename "appendonly.aof"

#aof持久化策略的配置
#no表示不執行fsync,由作業系統保證資料同步到磁碟,速度最快。
#always表示每次寫入都執行fsync,以保證資料同步到磁碟。
#everysec表示每秒執行一次fsync,可能會導致丟失這1s資料。
appendfsync everysec

# 在aof重寫或者寫入rdb檔案的時候,會執行大量IO,此時對於everysec和always的aof模式來說,執行fsync會造成阻塞過長時間,no-appendfsync-on-rewrite欄位設定為預設設定為no,是最安全的方式,不會丟失資料,但是要忍受阻塞的問題。如果對延遲要求很高的應用,這個欄位可以設定為yes,,設定為yes表示rewrite期間對新寫操作不fsync,暫時存在記憶體中,不會造成阻塞的問題(因為沒有磁碟競爭),等rewrite完成後再寫入,這個時候redis會丟失資料。Linux的預設fsync策略是30秒。可能丟失30秒資料。因此,如果應用系統無法忍受延遲,而可以容忍少量的資料丟失,則設定為yes。如果應用系統無法忍受資料丟失,則設定為no。
no-appendfsync-on-rewrite no

#aof自動重寫配置。當目前aof檔案大小超過上一次重寫的aof檔案大小的百分之多少進行重寫,即當aof檔案增長到一定大小的時候Redis能夠呼叫bgrewriteaof對日誌檔案進行重寫。當前AOF檔案大小是上次日誌重寫得到AOF檔案大小的二倍(設定為100)時,自動啟動新的日誌重寫過程。
auto-aof-rewrite-percentage 100
#設定允許重寫的最小aof檔案大小,避免了達到約定百分比但尺寸仍然很小的情況還要重寫
auto-aof-rewrite-min-size 64mb

#aof檔案可能在尾部是不完整的,當redis啟動的時候,aof檔案的資料被載入記憶體。重啟可能發生在redis所在的主機作業系統宕機後,尤其在ext4檔案系統沒有加上data=ordered選項(redis宕機或者異常終止不會造成尾部不完整現象。)出現這種現象,可以選擇讓redis退出,或者匯入儘可能多的資料。如果選擇的是yes,當截斷的aof檔案被匯入的時候,會自動釋出一個log給客戶端然後load。如果是no,使用者必須手動redis-check-aof修復AOF檔案才可以。
aof-load-truncated yes


############### LUA SCRIPTING ###############

# 如果達到最大時間限制(毫秒),redis會記個log,然後返回error。當一個指令碼超過了最大時限。只有SCRIPT KILL和SHUTDOWN NOSAVE可以用。第一個可以殺沒有調write命令的東西。要是已經呼叫了write,只能用第二個命令殺。
lua-time-limit 5000


############### 叢集相關 ###############

#叢集開關,預設是不開啟叢集模式。
# cluster-enabled yes

#叢集配置檔案的名稱,每個節點都有一個叢集相關的配置檔案,持久化儲存叢集的資訊。這個檔案並不需要手動配置,這個配置檔案有Redis生成並更新,每個Redis叢集節點需要一個單獨的配置檔案,請確保與例項執行的系統中配置檔名稱不衝突
# cluster-config-file nodes-6379.conf

#節點互連超時的閥值。叢集節點超時毫秒數
# cluster-node-timeout 15000

#在進行故障轉移的時候,全部slave都會請求申請為master,但是有些slave可能與master斷開連線一段時間了,導致資料過於陳舊,這樣的slave不應該被提升為master。該引數就是用來判斷slave節點與master斷線的時間是否過長。判斷方法是:
#比較slave斷開連線的時間和(node-timeout * slave-validity-factor) + repl-ping-slave-period
#如果節點超時時間為三十秒, 並且slave-validity-factor為10,假設預設的repl-ping-slave-period是10秒,即如果超過310秒slave將不會嘗試進行故障轉移
# cluster-slave-validity-factor 10

#master的slave數量大於該值,slave才能遷移到其他孤立master上,如這個引數若被設為2,那麼只有當一個主節點擁有2 個可工作的從節點時,它的一個從節點會嘗試遷移。
# cluster-migration-barrier 1

#預設情況下,叢集全部的slot有節點負責,叢集狀態才為ok,才能提供服務。設定為no,可以在slot沒有全部分配的時候提供服務。不建議開啟該配置。
# cluster-require-full-coverage yes


############### SLOW LOG 慢查詢日誌 ###############

###slog log是用來記錄redis執行中執行比較慢的命令耗時。當命令的執行超過了指定時間,就記錄在slow log中,slog log儲存在記憶體中,所以沒有IO操作。
#執行時間比slowlog-log-slower-than大的請求記錄到slowlog裡面,單位是微秒,所以1000000就是1秒。注意,負數時間會禁用慢查詢日誌,而0則會強制記錄所有命令。
slowlog-log-slower-than 10000

#慢查詢日誌長度。當一個新的命令被寫進日誌的時候,最老的那個記錄會被刪掉。這個長度沒有限制。只要有足夠的記憶體就行。你可以通過 SLOWLOG RESET 來釋放記憶體。
slowlog-max-len 128

############### 延遲監控 ###############
#延遲監控功能是用來監控redis中執行比較緩慢的一些操作,用LATENCY列印redis例項在跑命令時的耗時圖表。只記錄大於等於下邊設定的值的操作。0的話,就是關閉監視。預設延遲監控功能是關閉的,如果你需要開啟,也可以通過CONFIG SET命令動態設定。
latency-monitor-threshold 0

############### EVENT NOTIFICATION 訂閱通知 ###############
#鍵空間通知使得客戶端可以通過訂閱頻道或模式,來接收那些以某種方式改動了 Redis 資料集的事件。因為開啟鍵空間通知功能需要消耗一些 CPU ,所以在預設配置下,該功能處於關閉狀態。
#notify-keyspace-events 的引數可以是以下字元的任意組合,它指定了伺服器該傳送哪些型別的通知:
##K 鍵空間通知,所有通知以 __keyspace@__ 為字首
##E 鍵事件通知,所有通知以 __keyevent@__ 為字首
##g DEL 、 EXPIRE 、 RENAME 等型別無關的通用命令的通知
##$ 字串命令的通知
##l 列表命令的通知
##s 集合命令的通知
##h 雜湊命令的通知
##z 有序集合命令的通知
##x 過期事件:每當有過期鍵被刪除時傳送
##e 驅逐(evict)事件:每當有鍵因為 maxmemory 政策而被刪除時傳送
##A 引數 g$lshzxe 的別名
#輸入的引數中至少要有一個 K 或者 E,否則的話,不管其餘的引數是什麼,都不會有任何 通知被分發。詳細使用可以參考http://redis.io/topics/notifications

notify-keyspace-events ""

############### ADVANCED CONFIG 高階配置 ###############
#資料量小於等於hash-max-ziplist-entries的用ziplist,大於hash-max-ziplist-entries用hash
hash-max-ziplist-entries 512
#value大小小於等於hash-max-ziplist-value的用ziplist,大於hash-max-ziplist-value用hash。
hash-max-ziplist-value 64

#資料量小於等於list-max-ziplist-entries用ziplist,大於list-max-ziplist-entries用list。
list-max-ziplist-entries 512
#value大小小於等於list-max-ziplist-value的用ziplist,大於list-max-ziplist-value用list。
list-max-ziplist-value 64

#資料量小於等於set-max-intset-entries用iniset,大於set-max-intset-entries用set。
set-max-intset-entries 512

#資料量小於等於zset-max-ziplist-entries用ziplist,大於zset-max-ziplist-entries用zset。
zset-max-ziplist-entries 128
#value大小小於等於zset-max-ziplist-value用ziplist,大於zset-max-ziplist-value用zset。
zset-max-ziplist-value 64

#value大小小於等於hll-sparse-max-bytes使用稀疏資料結構(sparse),大於hll-sparse-max-bytes使用稠密的資料結構(dense)。一個比16000大的value是幾乎沒用的,建議的value大概為3000。如果對CPU要求不高,對空間要求較高的,建議設定到10000左右。
hll-sparse-max-bytes 3000

#Redis將在每100毫秒時使用1毫秒的CPU時間來對redis的hash表進行重新hash,可以降低記憶體的使用。當你的使用場景中,有非常嚴格的實時性需要,不能夠接受Redis時不時的對請求有2毫秒的延遲的話,把這項配置為no。如果沒有這麼嚴格的實時性要求,可以設定為yes,以便能夠儘可能快的釋放記憶體。
activerehashing yes

##對客戶端輸出緩衝進行限制可以強迫那些不從伺服器讀取資料的客戶端斷開連線,用來強制關閉傳輸緩慢的客戶端。
#對於normal client,第一個0表示取消hard limit,第二個0和第三個0表示取消soft limit,normal client預設取消限制,因為如果沒有尋問,他們是不會接收資料的。
client-output-buffer-limit normal 0 0 0
#對於slave client和MONITER client,如果client-output-buffer一旦超過256mb,又或者超過64mb持續60秒,那麼伺服器就會立即斷開客戶端連線。
client-output-buffer-limit slave 256mb 64mb 60
#對於pubsub client,如果client-output-buffer一旦超過32mb,又或者超過8mb持續60秒,那麼伺服器就會立即斷開客戶端連線。
client-output-buffer-limit pubsub 32mb 8mb 60

#redis執行任務的頻率為1s除以hz。
hz 10

#在aof重寫的時候,如果打開了aof-rewrite-incremental-fsync開關,系統會每32MB執行一次fsync。這對於把檔案寫入磁碟是有幫助的,可以避免過大的延遲峰值。
aof-rewrite-incremental-fsync yes

Redis的持久化

RDB

介紹

在指定的時間間隔內生成記憶體中整個資料集的持久化快照。快照檔案預設被儲存在當前資料夾中,名稱為dump.rdb,可以通過dir和dbfilename引數來修改預設值。

Redis會單獨建立(fork)一個子程序來進行持久化,會先將資料寫入到一個臨時檔案中,待持久化過程都結束了,再用這個臨時檔案替換上次持久化好的檔案。整個過程中,主程序是不進行任何的IO操作的,這就確保了極高的效能。

配置檔案

# redis是基於記憶體的資料庫,可以通過設定該值定期寫入磁碟。
# 註釋掉“save”這一行配置項就可以讓儲存資料庫功能失效
# 900秒(15分鐘)內至少1個key值改變(則進行資料庫儲存--持久化)
# 300秒(5分鐘)內至少10個key值改變(則進行資料庫儲存--持久化)
# 60秒(1分鐘)內至少10000個key值改變(則進行資料庫儲存--持久化)
save 900 1
save 300 10
save 60 10000

#當RDB持久化出現錯誤後,是否依然進行繼續進行工作,yes:不能進行工作,no:可以繼續進行工作,可以通過info中的rdb_last_bgsave_status瞭解RDB持久化是否有錯誤
stop-writes-on-bgsave-error yes

#使用壓縮rdb檔案,rdb檔案壓縮使用LZF壓縮演算法,yes:壓縮,但是需要一些cpu的消耗。no:不壓縮,需要更多的磁碟空間
rdbcompression yes

#是否校驗rdb檔案。從rdb格式的第五個版本開始,在rdb檔案的末尾會帶上CRC64的校驗和。這跟有利於檔案的容錯性,但是在儲存rdb檔案的時候,會有大概10%的效能損耗,所以如果你追求高效能,可以關閉該配置。
rdbchecksum yes

#rdb檔案的名稱
dbfilename dump.rdb

#資料目錄,資料庫的寫入會在這個目錄。rdb、aof檔案也會寫在這個目錄
dir /data

Fork

fork的作用相當於複製一個與當前程序一樣的程序。但是是一個全新的程序,並作為原程序的子程序。

觸發條件

  1. 通過配製檔案中的save條件(可自己配置)

    save 900 1
    save 300 10
    save 60 10000
  2. 手動通過save和bgsave命令

  • save:save時只管儲存,其他不管,全部阻塞

  • bgsave:redis會在後臺非同步的進行快照操作,同時還可以響應客戶端請求。可以通過lastsave命令獲取最後一次成功執行快照的事件

  1. 通過flushall命令,也會產生dump.rdb檔案,但是裡面是空的,無意義。

  2. 通過shutdown命令,安全退出,也會生成快照檔案(和異常退出形成對比,比如:kill殺死程序的方式)

如何恢復

appendonly no
dbfilename dump.rdb
dir /var/lib/redis #可以自行指定

appendonly 設定成no,redis啟動時會把/var/lib/redis 目錄下的dump.rdb 中的資料恢復。dir 和dbfilename 都可以設定。我測試時appendonly 設定成yes 時候不會將dump.rdb檔案中的資料恢復

優勢

  1. 恢復資料的速度很快,適合大規模的資料恢復,而又對部分資料不敏感的情況

  2. dump.db檔案是一個壓縮的二進位制檔案,檔案暫用空間小

劣勢

  1. 當出現異常退出時,會丟失最後一次快照後的資料

  2. 當fork的時候,記憶體的中的資料會被克隆一份,大致兩倍的膨脹需要考慮。而且,當資料過大時,fork操作佔用過多的系統資源,造成主伺服器程序假死。

使用場景

  1. 資料備份

  2. 可容忍部分資料丟失

  3. 跨資料中心的容災備份

AOF

介紹

以日誌的形式來記錄每個寫操作,將Redis執行過的所有寫指令記錄下來(讀操作補不可記錄),只許追加檔案但不可以改寫檔案,redis啟動之初會讀取改檔案重新構建資料。儲存的是appendonly.aof檔案

aof機制預設關閉,可以通過appendonly = yes引數開啟aof機制,通過appendfilename = myaoffile.aof指定aof檔名稱。

aof持久化的一些策略配置

#aof持久化策略的配置
#no表示不執行fsync,由作業系統保證資料同步到磁碟,速度最快。
#always表示每次寫入都執行fsync,以保證資料同步到磁碟。
#everysec表示每秒執行一次fsync,可能會導致丟失這1s資料。
appendfsync everysec

對於觸發aof重寫機制也可以通過配置檔案來進行設定:

# aof自動重寫配置。當目前aof檔案大小超過上一次重寫的aof檔案大小的百分之多少進行重寫,即當aof檔案增長到一定大小的時候Redis能夠呼叫bgrewriteaof對日誌檔案進行重寫。當前AOF檔案大小是上次日誌重寫得到AOF檔案大小的二倍(設定為100)時,自動啟動新的日誌重寫過程。
auto-aof-rewrite-percentage 100
# 設定允許重寫的最小aof檔案大小,避免了達到約定百分比但尺寸仍然很小的情況還要重寫
auto-aof-rewrite-min-size 64mb

當aop重寫時會引發重寫和持久化追加同時發生的問題,可以通過no-appendfsync-on-rewrite no進行配置

# 在aof重寫或者寫入rdb檔案的時候,會執行大量IO,此時對於everysec和always的aof模式來說,執行fsync會造成阻塞過長時間,no-appendfsync-on-rewrite欄位設定為預設設定為no,是最安全的方式,不會丟失資料,但是要忍受阻塞的問題。如果對延遲要求很高的應用,這個欄位可以設定為yes,,設定為yes表示rewrite期間對新寫操作不fsync,暫時存在記憶體中,不會造成阻塞的問題(因為沒有磁碟競爭),等rewrite完成後再寫入,這個時候redis會丟失資料。Linux的預設fsync策略是30秒。可能丟失30秒資料。因此,如果應用系統無法忍受延遲,而可以容忍少量的資料丟失,則設定為yes。如果應用系統無法忍受資料丟失,則設定為no。
no-appendfsync-on-rewrite no

如何恢復

正常恢復

將檔案放到dir指定的資料夾下,當redis啟動的時候會自動載入資料,注意:aof檔案的優先順序比dump大

異常恢復
  • 有些操作可以直接到appendonly.aof檔案裡去修改。

    eg:使用了flushall這個命令,此刻持久化檔案中就會有這麼一條命令記錄,把它刪掉就可以了

  • 寫壞的檔案可以通過 redis-check-aof --fix進行修復

####

優勢

  1. 根據不同的策略,可以實現每秒,每一次修改操作的同步持久化,就算在最惡劣的情況下只會丟失不會超過兩秒資料。

  2. 當檔案太大時,會觸發重寫機制,確保檔案不會太大。

  3. 檔案可以簡單的讀懂

劣勢

  1. aof檔案的大小太大,就算有重寫機制,但重寫所造成的阻塞問題是不可避免的

  2. aof檔案恢復速度慢

總結

  1. 如果你只希望你的資料在伺服器執行的時候存在,可以不使用任何的持久化方式

  2. 一般建議同時開啟兩種持久化方式。AOF進行資料的持久化,確保資料不會丟失太多,而RDB更適合用於備份資料庫,留著一個做萬一的手段。

  3. 效能建議:

    因為RDB檔案只用做後備用途,建議只在slave上持久化RDB檔案,而且只要在15分鐘備份一次就夠了,只保留900 1這條規則。

    如果Enalbe AOF,好處是在最惡劣情況下也只會丟失不超過兩秒資料,啟動指令碼較簡單隻load自己的AOF檔案就可以了。代價:1、帶來了持續的IO;2、AOF rewrite的最後將rewrite過程中產生的新資料寫到新檔案造成的阻塞幾乎是不可避免的。只要硬碟許可,應該儘量減少AOF rewrite的頻率,AOF重寫的基礎大小預設值64M太小了,可以設到5G以上。預設超過原大小100%大小時重寫可以改到適當的數值。

    如果不Enable AOF,僅靠Master-Slave Replication 實現高可用性也可以。能省掉一大筆IO也減少了rewrite時帶來的系統波動。代價是如果Master/Slave同時宕掉,會丟失10幾分鐘的資料,啟動指令碼也要比較兩個Master/Slave中的RDB檔案,載入較新的那個。新浪微博就選用了這種架構。

Redis的事務

是什麼?

可以一次執行多個命令,本質是一組命令的集合。一個事物中的所有命令都會被序列化,按順序的序列執行而不會被其他命令插入,不許加塞。

能幹嘛?

一個佇列中,一次性的,順序的,排他的執行一系列命令。

常用命令

命令描述
multi 標記一個事務的開始
exec 執行所有事務塊內的命令
discard 取消事務,放棄執行事務塊內的所有命令
watch key [key] 監視一個(或多個) key ,如果在事務執行之前這個(或這些) key 被其他命令所改動,那麼事務將被打斷。
unwatch 取消watch命令對所有 key 的監視。

這麼玩?

  1. 正常執行

  2. 放棄事務

  3. 全體連坐

  4. 冤頭債主

樂觀鎖和悲觀鎖

參考

Watch監控

watch指令,類似樂觀鎖,如果key的值已經被修改了,那麼整個事務佇列都不會被執行,同時返回一個Nullmulti-bulk應答以通知呼叫者事務執行失敗。

注意:一旦執行了exec或者discard,之前加的所有監控鎖都會被取消掉了。

例子:

  • 初始化信用卡的可用餘額和欠額

  • 無加塞篡改

  • 有加塞篡改,當watch的key被修改,後面的那個事務全部執行失敗

  • unwatch

3階段

開啟:以multi開啟事務

入隊:將多個命令入隊到事務中,接到這些命令不會立刻執行,而是放到等待執行的事務佇列裡面

執行:有exec命令觸發事務

開啟 入隊 執行

3特性

單獨的隔離操作:事務中的所有命令都會序列化,按順序的執行。事務在等待執行的時候,不會被其他客戶端傳送來的米命令請求打斷

沒有隔離級別的概念:佇列中的所有命令沒有提交exec之前都是不會被執行的

不保證原子性:redis中如果一條命令執行失敗,其後的命令仍然會被執行,沒有回滾,參考冤頭債主

Redis的釋出訂閱(一般不用)

釋出訂閱

Redis 釋出訂閱(pub/sub)是一種訊息通訊模式:傳送者(pub)傳送訊息,訂閱者(sub)接收訊息。

Redis 客戶端可以訂閱任意數量的頻道。

下圖展示了頻道 channel1 , 以及訂閱這個頻道的三個客戶端 —— client2 、 client5 和 client1 之間的關係:


當有新訊息通過 PUBLISH 命令傳送給頻道 channel1 時, 這個訊息就會被髮送給訂閱它的三個客戶端:



命令

  • subscribe channel [channel…]:訂閱一個或多個頻道的資訊
  • psubscribe pattern [pattern…]:訂閱一個或多個符合規定模式的頻道
  • publish channel message :將資訊傳送到指定頻道
  • unsubscribe [channel[channel…]]:退訂頻道
  • punsubscribe [pattern[pattern…]]:退訂所有給定模式的頻道

應用場景

構建實時的訊息系統,比如普通聊天、群聊等功能。 1、部落格網站訂閱,當作者釋出就可以推送給粉絲 2、微信公眾號模式

Redis的複製(Master/Slave)

是什麼?

就是我們常說的主從複製,主機資料更新後根據配置和策略,自動同步到備機的master/slaver機制,Master以寫為主,Slave以讀為主

能幹嘛?

讀寫分離

容災恢復

怎麼玩?

配從(庫)不配主(庫)

從庫配置

#配置從庫
slaveof 主庫ip 主庫埠
#檢視主從資訊
info replication

每次與master斷開後,都需要重新連線,除非你配置進redis.conf檔案

常用的主從方式

一主二僕

含義:就是一個Master兩個Slave

通過info replication檢視主從資訊

# Replication
role:master
connected_slaves:0
master_replid:f6baff9abfda12ca58048cfce4b0e2c1f4683da1
master_replid2:e8fe596d47d9d1d923d56d884b28128b78d2c1e0
master_repl_offset:0
second_repl_offset:1
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0
# Replication
role:slave
master_host:127.0.0.1
master_port:6379
master_link_status:down
master_last_io_seconds_ago:-1
master_sync_in_progress:0
slave_repl_offset:0
master_link_down_since_seconds:1585217521
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:adbec19afa734e84a333b07ea2f33c43c73fe743
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:0
second_repl_offset:-1
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0

注意:

  1. 第一次slave1 和slave2切入點,是全量複製,之後是增量複製

  2. 主機可以寫,但是從機不可以寫,從機只能讀

  3. 主機shutdowm後從機待機狀態,等主機回來後,主機新增記錄從機可以順利複製

  4. 從機shutdowm後,每次與master斷開之後,都需要重新連線,除非你配置進redis.conf檔案

  5. 從機複製到的資料,會被本機持久化。就算shutdown斷開連線依然會有資料。

  6. 重新連線或者變更master,會清除之前的資料,重新建立拷貝最新的資料

薪火相傳

含義:就是上一個Slave可以是下一個slave的Master,Slave同樣可以接收其他slaves的連線和同步請求,那麼該slave作為了鏈條中下一個的master,可以有效減輕master的寫壓力。

注意事項和一主二僕差不多,但注意雖然有slave是相對master,但是依然是slave 檢視

反客為主

SLAVEOF no one

使當前資料庫停止與其他資料庫的同步,轉成主資料庫

哨兵模式(sentinel)

反客為主的自動版,能夠後臺監控Master庫是否故障,如果故障了根據投票數自動將slave庫轉換為主庫。一組sentinel能

同時監控多個Master。

使用步驟:

  1. 在Master對應redis.conf同目錄下新建sentinel.conf檔案,名字絕對不能錯;

  2. 配置哨兵,在sentinel.conf檔案中填入內容(可以配置多個):

    #說明:最後一個數字1,表示主機掛掉後slave投票看讓誰接替成為主機,得票數多少後成為主機。
    sentinel monitor 被監控資料庫名字(自己起名字) ip port 1
  3. 啟動哨兵模式(路徑按照自己的需求進行配置):

    redis-sentinel  /myredis/sentinel.conf

注意:

  1. 當master掛掉後,會通過選票進行選出下一個master。而且只有使用了sentinel.conf啟動的才能開啟選票

  2. 當原來的master後來後,很不幸變成了slave。

複製原理

  1. Slave啟動成功連線到master後會傳送一個sync命令;

  2. Master接到命令啟動後的存檔程序,同時收集所有接收到的用於修改資料集命令,在後臺程序執行完畢之後,master

    將傳送整個資料檔案到slave,以完成一次完全同步;

  3. 全量複製:而slave服務在資料庫檔案資料後,將其存檔並載入到記憶體中;

  4. 增量複製:Master繼續將新的所有收集到的修改命令依次傳給slave,完成同步;

  5. 但是隻要是重新連線master,一次完全同步(全量複製)將被自動執行。

結合這幾個注意事項

複製的缺點

延時,由於所有的寫操作都是在Master上操作,然後同步更新到Slave上,所以從Master同步到Slave機器有一定的延遲,當系統很繁忙的時候,延遲問題會更加嚴重,Slave機器數量的增加也會使得這個問題更加嚴重。

命令

命令作用
slaveof 主庫ip 主庫埠 配置從庫
info replication 檢視redis主從複製的情況
slaveof no one 使當前資料庫停止與其他資料庫的同步,轉成主資料庫
sentinel monitor 被監控資料庫名字(自己起名字) 127.0.0.1 6379 1 配置哨兵,監視master
redis-sentinel /myredis/sentinel.conf 以哨兵模式啟動redis

Redis叢集

容量不夠,redis如何擴容?

併發寫操作,redis如何分攤?

什麼是Redis叢集?

Redis叢集實現了對Redis的水平擴容,即啟動N個redis節點,將整個資料庫分佈儲存在這N個節點中,每個節點儲存總資料的1/N

Redis叢集通過分割槽(partition)來提供一定程度的可用性(availability):即使叢集中有一部分節點失效或者無法進行通訊,叢集也可以繼續處理命令請求。

叢集搭建

搭建看這篇文章,有效

啟動6個容器 cluster meet 匯入安裝包 修改配置檔案 建立基本映象 建立節點映象 進入一個redis-cli 叢集新增節點 配置槽點 配置主從高可用

叢集命令

CLUSTER INFO 列印叢集的資訊 
CLUSTER NODES 列出叢集當前已知的所有節點(node),以及這些節點的相關資訊。  

//節點(node) 
CLUSTER MEET <ip> <port> 將 ip 和 port 所指定的節點新增到叢集當中,讓它成為叢集的一份子。 
CLUSTER FORGET <node_id> 從叢集中移除 node_id 指定的節點。 
CLUSTER REPLICATE <node_id> 將當前節點設定為 node_id 指定的節點的從節點。 
CLUSTER SAVECONFIG 將節點的配置檔案儲存到硬盤裡面。  

//槽(slot) 
CLUSTER ADDSLOTS <slot> [slot ...] 將一個或多個槽(slot)指派(assign)給當前節點。 
CLUSTER DELSLOTS <slot> [slot ...] 移除一個或多個槽對當前節點的指派。 
CLUSTER FLUSHSLOTS 移除指派給當前節點的所有槽,讓當前節點變成一個沒有指派任何槽的節點。 
CLUSTER SETSLOT <slot> NODE <node_id> 將槽 slot 指派給 node_id 指定的節點,如果槽已經指派給另一個節點,那麼先讓另一個節點刪除該槽>,然後再進行指派。 
CLUSTER SETSLOT <slot> MIGRATING <node_id> 將本節點的槽 slot 遷移到 node_id 指定的節點中。 
CLUSTER SETSLOT <slot> IMPORTING <node_id> 從 node_id 指定的節點中匯入槽 slot 到本節點。 
CLUSTER SETSLOT <slot> STABLE 取消對槽 slot 的匯入(import)或者遷移(migrate)。  

//鍵 (key) 
CLUSTER KEYSLOT <key> 計算鍵 key 應該被放置在哪個槽上。 
CLUSTER COUNTKEYSINSLOT <slot> 返回槽 slot 目前包含的鍵值對數量。 
CLUSTER GETKEYSINSLOT <slot> <count> 返回 count 個 slot 槽中的鍵。

節點

  1. 一個叢集至少要有三個主節點,即要有六個節點。

  2. 分配原則儘量保證每個主資料庫執行在不同的ip地址,每個從庫和主庫不在一個ip地址。

  3. 當主節點崩了,從節點能自動升為主節點;當主節點再次恢復時,主節點變為slave。參考哨兵模式。

  4. redis.conf有個引數cluster-require-full-coverage

    #預設情況下,叢集全部的slot有節點負責,叢集狀態才為ok,才能提供服務。設定為no,可以在slot沒有全部分配的時候提供服務。不建議開啟該配置。
    # cluster-require-full-coverage yes

SLOTS

  • 一個Redis 叢集包含16384個插槽(hash slot), 資料庫中的每個鍵都屬於這16384個插槽的其中一個,叢集使用公式CRC1 6(key)% 16384來計算鍵key屬於哪個槽(如果有組的話就只算組的部分),其中CRC16(key)語句用於計算鍵key的CRC16校驗和。

  • 叢集中的每個節點負責處理一部分插槽。 舉個例子, 如果一個叢集可以有主節點。其中:

    • 節點A負責處理0號至5500號插槽

    • 節點B負責處理5501號至11000號插槽

    • 節點C負責處理11001號至16383號插槽

(注意:每個節點分配的插槽具體數字可能不同,當然可以通過一個小指令碼來指定)

一個疑問:為什麼是16384(2^14),而不是65535(2^16)呢?

在redis節點發送心跳包時需要把所有的槽放到這個心跳包裡,以便讓節點知道當前叢集資訊,16384=16k,在傳送心跳包時使用char進行bitmap壓縮後是2kb(16384÷8÷1024=2kb),也就是說使用2k的空間建立了16k的槽數65535=65k,壓縮後就是8kb(65536÷8÷1024=8kb),也就是說需要需要8k的心跳包。

Redis Cluster原理

  1. node1和node2首先進行握手meet,知道彼此的存在

  2. 握手成功後,兩個節點會定期傳送ping/pong訊息,交換資料資訊(訊息頭,訊息體)

  3. 訊息頭裡面有個欄位:unsigned char myslots[CLUSTER_SLOTS/8],每一位代表一個槽,如果該位是1,代表該槽屬於這個節點

  4. 訊息體中會攜帶一定數量的其他節點的資訊,大約佔叢集節點總數量的十分之一,至少是3個節點的資訊。節點數量越多,訊息體內容越大。

  5. 每秒都在傳送ping訊息。每秒隨機選取5個節點,找出最久沒有通訊的節點發送ping訊息。

  6. 每100毫秒都會掃描本地節點列表,如果發現節點最近一次接受pong訊息的時間大於cluster-node-timeout/2,則立即傳送ping訊息

redis叢集的主節點數量基本不可能超過1000個,超過的話可能會導致網路擁堵。

在叢集中錄入值(組的概念)

redis-cli客戶端提供-c引數實現自動重定向

redis-cli -c -p 6379

不在一個slot下的鍵值,是不能使用mget,mset等多鍵操作

可以通過{}來定義組的概念,從而使key中{}內相同內容的鍵值對放到一個slot中去。

set user:{info}:name xxx
set age{info} 12
set {info}email [email protected]
hset user{info} name jiang
hset user{info} age 19
hset user{info} eamil [email protected]

#結果
172.17.0.3:6379> keys *
1) "user{info}"
2) "{info}email"
3) "user:{info}:name"
4) "age{info}"
------------------------------------------------------
172.17.0.3:6379> hkeys user{info}
1) "name"
2) "age"
3) "eamil"



翻譯 朗讀 複製 正在查詢,請稍候…… 重試 朗讀 複製 複製 朗讀 複製 via 谷歌翻譯(國內)