redis快速入門-進階知識

來源:互聯網
上載者:User

標籤:

前言

前面我們已經學習了redis的資料類型,接下來將簡單學習下redis的事務,排序,管道,最佳化儲存空間以及管理等知識。

事務

事務的概念在此不贅述,學過資料庫原理的都應該知道。

redis的事務:先將屬於一個事務的命令發送給redis,然後再讓redis依次執行這些命令:

MULTI  //開始一個事務//事務的命令EXEC //執行事務

EXEC告訴redis將等待執行的事務隊列中的所有命令(即剛才所有返回QUEUED的命令)按照發送順序依次執行。

錯誤處理

1.語法錯誤:命令不存在或者命令參數的個數不對。

只要有一個命令有語法錯誤,執行EXEC後redis就會直接返回錯誤,那些文法正確的命令也不會執行。

2.運行錯誤:在命令執行時出現的錯誤,比如使用散列類型的命令操作 集合類型的鍵。

如果一條命令出現了運行錯誤,事務裡其他的命令依然會繼續執行。

  • redis的事務沒有復原功能。
WATCH命令

事務中的每個命令的執行結果都是最後一起返回的,所以我們無法將前一條命令的結果作為下一條命令的參數。

下面舉個例子,假如我們要自己實現INCR命令,可以根據GET命令的傳回值來確定該鍵是否已經存在,然後進行對應得SET操作。為防止競態,可以使用事務,但是事務存在上面提到的問題,因此無法解決競態問題。

我們再看看WATCH命令:WATCH命令可以監控一個或者多個鍵,一旦其中有一個鍵被修改(或刪除),之後的事務就不會執行,監控一直持續到EXEC命令被執行。

例子:

> SET key 1OK> WATCH keyOK> SET key 2OK> MULTIOK> SET key 3QUEUED> EXEC(nil)> GET key"2"

可以看到,我們在指向WATCH命令後,事務執行前修改了key的值,因此最後事務並沒有執行,EXEC返回空結果。

因此使用WATCH就可以通過事務實現自己的incr函數:

def incr($key)    WATCH $key    $value = GET $key    if not $value        $value = 0    $value = $value + 1    MULTI    SET $key, $value    result = EXEC    return result[0]

需要注意的三點:

1.EXEC傳回值是多行字串類型

2.WATCH命令的作用只是當監控的索引值被修改後阻止之後一個事務的執行,而不能保證其他用戶端不修改這一索引值。因此,如果EXEC執行失敗,我們需要重新執行整個函數。

3.EXEC執行之後會取消對所有鍵的監控,我們也可以使用UNWATCH來取消監控。

存留時間

在redis中,可以使用EXPIRE命令設定一個鍵的存留時間,單位是秒:

> SET foo barOK> EXPIRE foo 20(integer) 1> TTL foo(integer) 15> TTL foo(integer) 7> TTL foo(integer) -1

可以看到,隨著時間不同,foo鍵的存留時間逐漸減少,20秒後foo會被刪除,當鍵不存在時TTL返回-1,另外,當沒有設定存留時間時,預設永久存在,TTL同樣返回-1。

使用PERSIST命令可以取消鍵的存留時間:

PERSIST foo

此外使用SET或GETSET命令為鍵賦值也會同時清除鍵的存留時間。

EXPIRE的參數必須是整數,如果想更精確,可以使用PEXPIRE,其單位是毫秒。

  • 如果使用WATCH監控一個有存留時間的鍵,該鍵時間到期自動刪除並不會被WATCH命令認為該鍵被改變。
緩衝

redis可以作為緩衝使用,一方面我們可以通過給鍵設定存留時間來定期刪除緩衝,另一方面,如果記憶體已經達到上限,redis將按照一定規則淘汰不需要的緩衝鍵(常見的是LRU-最近最少使用演算法)。

最大可用記憶體大小(單位是位元組):配置maxmemory;
淘汰策略:配置maxmemory-policy。

排序

1.使用有序集合

2.使用SORT命令(有BY, GET, STORE等參數可以選擇)
時間複雜度為O(n+mlogm),n表示要排序的列表中元素個數,m表示要返回的元素個數。

訊息通知

訊息通知可以使用“任務隊列”——“傳遞任務的隊列”,也就是常見的生產者/消費者模型,生產者會將需要處理的任務放入任務隊列中,而消費者則不斷地從任務隊列中讀入任務資訊並執行。

優點:

  • 松耦合:生產者和消費者無需知道彼此的實現細節;
  • 易於擴充:消費者可以有多個,且可以分布在不同的伺服器中。
使用redis實現任務隊列

生產者:將任務使用LPUSH命令假如到某個鍵中;
消費者:不斷地適應RPOP命令從該鍵中取出任務。

以下為消費者的虛擬碼:

# 無限迴圈讀取任務隊列中的內容loop    $task = RPOP queue    if $task        execute($task)    else        wait 1 second

以上不斷輪詢,效率低下,我們可以使用BRPOP來最佳化:

loop    #如果隊列中沒有新任務,BRPOP將會一直阻塞    $task = BRPOP queue, 0    #傳回值是一個數組,數組第二個元素是我們需要的任務    execute($task[1])

BRPOP接收兩個參數:鍵名和逾時時間(秒為單位,0表示無限等待)。

BRPOP返回兩個值:鍵名和元素值。

優先隊列

有下面的情況:假設你的網站有推送功能,即當你發表新文章時,會發寄件提醒粉絲,同時,你的網站註冊時需要郵件驗證。如果沒有使用優先隊列,那麼當你發表新文章之後,發郵箱的線程被佔用,使得註冊使用者得不到及時的響應。因此需要使得註冊的郵件優先被處理髮送。

loop    $task =         BRPOP queue:confirmation.email,              queue:notification.eamil,              0    execute($task[1])  

BRPOP可以同時接收多個鍵,如果多個鍵都有元素則按從左至右的順序取第一個鍵中的一個元素。

“發布/訂閱”模式

除了實現任務隊列之外,redis還提供了一組命令可以讓開發人員實現“發布/訂閱”(publish/subscribe)模式:
訂閱者可以訂閱一個或若干個頻道(channel),而發行者可以向指定的頻道發送訊息,所以訂閱此頻道的訂閱者都會收到此訊息。

發布訊息:

PUBLISH channel message

訂閱訊息:

SUBSCRIBE channel [channel ...]

執行SUBSCRIBE命令後用戶端會進入訂閱狀態,不能使用除SUBSCRIBE/UNSUBSCRIBE/PSUBSCRIBE/PUNSUBSCRIBE這4個屬於“發布/訂閱”模式的命令之外的命令,否則會報錯。

PSUBSCRIBE命令可以訂閱指定的規則,如:

>PSUBSCRIBE channel.?*

它可以匹配channel.1和channel.10等,不會匹配channel.。

管道

Redis是一個TCP伺服器,並支援要求/響應協議。redis的一個請求完成需要下面的步驟:

  • 用戶端發送一個查詢到伺服器,並等待伺服器的響應。

  • 伺服器處理命令並將響應返回給用戶端。

而通過管道,用戶端可以發送多個請求給伺服器,而無需等待回覆。即通過管道可以一次性發送多條命令並在執行完之後一次性將結果返回。

節省空間的

1.精簡鍵名和索引值

2.內部編碼最佳化

查看內部編碼:

OBJECT ENCODING foo

redis的每個索引值都使用一個redisObject結構體儲存:

typedef struct redisObject {    unsigned type:4;    unsigned notused:2; /*Not used*/    unsigned encoding:4;    ussigned lru:22; /*lru time*/    int refcount;    void *ptr;} robj;

type欄位是索引值的資料類型:

#define REDIS_STRING 0#define REDIS_LIST 1#define REDIS_SET 2#define REDIS_ZSET 3#define REDIS_HASH 4

encoding欄位表示索引值的內部編碼方式:

//字串類型編碼#define REDIS_ENCODING_RAW 0//字串類型編碼#define REDIS_ENCODING_INT 1//散列類型,集合類型編碼#define REDIS_ENCODING_HT 2#define REDIS_ENCODING_ZIPMAP 3//清單類型編碼#define REDIS_ENCODINGLINKEDLIST 4//散列類型,清單類型,有序集合類型編碼#define REDIS_ENCODING_ZIPLIST 5//集合類型編碼#define REDIS_ENCODING_INTSET 6//有序集合類型編碼#define REDIS_ENCODING_SKIPLIST 7

具體各個類型的最佳化參見《Redis入門指南》(李子驊 編著)。

指令碼

Redis使用Lua解譯器用於計算指令碼。它Redis從2.6.0版本開始內建,使用指令碼用eval命令。

> EVAL script numkeys key [key ...] arg [arg ...]

指令碼功能允許開發人員使用Lua語言編寫指令碼傳到Redis中執行,在Lua指令碼中可以調用大部分的Redis命令。

使用指令碼的好處:

  • 減少網路開銷:原本需要多次請求的代碼,使用指令碼功能只需要一次請求。
  • 原子操作:redis會將整個指令碼作為一個整體執行。
  • 複用:用戶端發送的指令碼會永久儲存在redis中。
管理持久化

redis的資料是儲存在記憶體中的,為了使redis在重啟之後仍能保證資料不丟失,需要將資料從記憶體中以某種形式同步到硬碟中,這個過程叫做持久化。
redis支援兩種持久化方式:RDB方式和AOF方式。

RDB方式

RDB方式是通過快照(snapshotting)完成的,當符合一定條件時redis會自動將記憶體中所有資料進行快照並儲存到硬碟上。

進行快照的條件可由使用者在設定檔中自訂,由兩個參數構成:時間(單位為秒)和改動的鍵的個數。

當指定時間內被更改的鍵的個數大於指定的數值時就會進行快照。

RDB是預設的持久化方式,設定檔預置了3個條件:

save 900 1save 300 10save 60 10000

多個快照條件之間是“或”的關係。想禁止快照只需要把所有的save參數刪除即可。

RDB持久化預設產生的檔案名稱為dump.rdb,可以通過配置dir和dbfilename來指定儲存的路徑和檔案名稱。

快照過程如下:

1.redis使用fork複製一份當前進程的副本;
2.父進程繼續接收並處理用戶端發來的命令,而子進程開始將記憶體中的資料寫入硬碟中的臨時檔案;
3.當子進程寫入完所有資料後會用該臨時檔案替換舊的RDB檔案。

可以看到,通過臨時檔案,rdis保證了任何時候RDB檔案都是完整的。

RDB檔案是經過壓縮的二進位格式,佔用空間小。

除了自動快照,還可以手動發送SAVE或BGSAVE讓redis執行快照,前者在主進程中進行快照,後者會fork子進程進行快照。

另外,你可能會好奇,伺服器是怎麼知道我做了多少修改的?

伺服器中有個dirty計數器和一個lastsave時間戳記。

當伺服器執行一個資料庫修改命令之後,dirty計數器就會進行更新,命令修改了多少次資料庫,dirty就會增加多少,如:【set msg hello】修改了一個,那麼dirty就加一,如果【mset msg word name nihao age 20】那麼dirty就增加三。

lastsave屬性記錄上次伺服器執行儲存操作的時間,是一個unix時間戳記,通過這兩個屬性,可以很簡單的距離上次儲存已經多少時間了,以及修改了多少次資料庫,一旦滿足以上三個條件,那麼就自動調用bgsave命令,同時更新lastsave屬性和dirty屬性歸零。

至於檢查儲存條件是否滿足這個工作,是由Redis伺服器周期性操作函數serverCron預設間隔100毫秒執行一次檢查,這個函數有很多地方用到,注意一下,這個函數是對正在啟動並執行伺服器進行維護的函數,在Redis事件中會有提到(Redis伺服器是一個事件驅動程式,什麼是事件驅動呢?就是發生事件的時候才會動一下,不然就跟死了一樣,事件驅動又分為檔案事件和時間事件,ServerCron就是一種時間驅動,至於檔案驅動,其實就是用戶端發過來一個命令,伺服器才會去執行,然後給用戶端返回結果)

最後說一點,Redis本身內建了一個RDB檔案檢查工具redis-check-dump,可以使用這個工具對rdb檔案是否完整進行檢查。

AOF方式

redis預設不開啟AOF(append only file)持久化,可以通過以下參數開啟:

appendonly yes

開啟AOF之後,每執行一條會更改redis中資料的命令,都會將該命令寫入硬碟中的AOF檔案中,儲存位置與RDB相同,檔案名稱預設為appendonly.aof。

AOF檔案是純文字檔案,其內容為redis用戶端向redis發送的原始通訊協定的內容。

雖然每次的修改都會記錄到AOF檔案中,但是由於作業系統的緩衝機制,資料並沒有真正寫入硬碟,而是進入系統的硬碟緩衝,預設情況每30秒才執行一次同步操作,因此系統異常退出時會丟失未同步的資料。為解決這個問題,redis需要在寫入AOF檔案後主動要求系統進行同步:

#appendfsync alwaysappendfsync everysec#appendfsync no
複製

為避免單點故障,最好將資料庫複寫多個副本以部署在不同的伺服器上,redis提供了複製(replication)功能可以自動實現同步的過程。

配置

主要資料庫(master)可以進行讀寫操作,當發生寫操作時自動將資料同步給從資料庫(slave)。

從資料庫一般是唯讀,它只能有一個主要資料庫。

配置非常簡單:

主要資料庫無需任何配置,從資料庫只需在設定檔中加入“slaveof 主要資料庫IP 主要資料庫連接埠”即可。

原理

從資料庫啟動後,先向主要資料庫發送SYNC命令;主要資料庫接到SYNC命令後就開始儲存快照(即使關閉了RDB持久化方式),在此期間,所有發給主要資料庫的命令都被緩衝起來;快照儲存完成後,主要資料庫把快照和緩衝的命令一起發給從資料庫;從資料庫儲存主要資料庫發來的快照檔案,並依次執行主要資料庫發來的緩衝命令。在同步過程中,從資料庫不會阻塞,它預設使用同步之前的資料繼續響應用戶端發來的命令。

之後,主要資料的任何資料變化都是通過TCP同步給從資料庫。

讀寫分離

可以通過複製功能建立多個從資料庫,主要資料庫只進行寫操作,從資料庫負責讀操作,以提高伺服器的負載能力。

從資料庫持久化

為提高效能,可通過複製功能建立一個從資料庫,並在從資料庫中啟用持久化,而主要資料庫禁用持久化。

當從資料庫崩潰時,重啟後主要資料庫會將資料同步過來,無需擔心資料丟失;

當主要資料庫崩潰時,在從資料庫中使用SLAVEOF NO ONE命令將從資料庫提升為主要資料庫繼續服務,並在原來的主要資料庫重啟後使用SLAVEOF命令將其設定為新的主要資料庫的從資料庫,即可把資料同步回來。

安全

Redis資料庫可以設定密碼,相關的任何用戶端都需要在執行命令之前進行身分識別驗證。

密碼可以通過設定檔的requirepass來設定。

例子

127.0.0.1:6379> CONFIG get requirepass1) "requirepass"2) ""

預設情況下,此屬性為空白,表示沒有設定密碼。可以通過執行以下命令來更改這個屬性

127.0.0.1:6379> CONFIG set requirepass "jiange"OK127.0.0.1:6379> CONFIG get requirepass1) "requirepass"2) "jiange"

用戶端需要使用AUTH命令進行認證。

127.0.0.1:6379> AUTH password

另外,redis可以修改bind參數來限制訪問的地址。

結語

好啦,關於redis快速入門的內容就到這裡了,接下來需要做的就是在實踐中熟練地使用。

後期如果有需要,有時間,我將學習一下redis的源碼實現( ̄▽ ̄)”。

redis快速入門-進階知識

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.