天天看點

INCR key

對存儲在指定<code>key</code>的數值執行原子的加1操作。

如果指定的key不存在,那麼在執行incr操作之前,會先将它的值設定為<code>0</code>。

如果指定的key中存儲的值不是字元串類型(fix:)或者存儲的字元串類型不能表示為一個整數,

那麼執行這個指令時伺服器會傳回一個錯誤(eq:(error) ERR value is not an integer or out of range)。

這個操作僅限于64位的有符号整型資料。

注意: 由于redis并沒有一個明确的類型來表示整型資料,是以這個操作是一個字元串操作。

執行這個操作的時候,key對應存儲的字元串被解析為10進制的64位有符号整型資料。

事實上,Redis 内部采用整數形式(Integer representation)來存儲對應的整數值,是以對該類字元串值實際上是用整數儲存,也就不存在存儲整數的字元串表示(String representation)所帶來的額外消耗。

integer-reply:執行遞增操作後<code>key</code>對應的值。

Redis的原子遞增操作最常用的使用場景是計數器。

使用思路是:每次有相關操作的時候,就向Redis伺服器發送一個incr指令。

例如這樣一個場景:我們有一個web應用,我們想記錄每個使用者每天通路這個網站的次數。

web應用隻需要通過拼接使用者id和代表目前時間的字元串作為key,每次使用者通路這個頁面的時候對這個key執行一下incr指令。

這個場景可以有很多種擴充方法:

通過結合使用<code>INCR</code>和EXPIRE指令,可以實作一個隻記錄使用者在指定間隔時間内的通路次數的計數器

用戶端可以通過GETSET指令擷取目前計數器的值并且重置為0

通過類似于DECR或者INCRBY等原子遞增/遞減的指令,可以根據使用者的操作來增加或者減少某些值 比如線上遊戲,需要對使用者的遊戲分數進行實時控制,分數可能增加也可能減少。

限速器是一種可以限制某些操作執行速率的特殊場景。

傳統的例子就是限制某個公共api的請求數目。

假設我們要解決如下問題:限制某個api每秒每個ip的請求次數不超過10次。

我們可以通過incr指令來實作兩種方法解決這個問題。

更加簡單和直接的實作如下:

這種方法的基本點是每個ip每秒生成一個可以記錄請求數的計數器。

但是這些計數器每次遞增的時候都設定了10秒的過期時間,這樣在進入下一秒之後,redis會自動删除前一秒的計數器。

注意上面僞代碼中我們用到了MULTI和EXEC指令,将遞增操作和設定過期時間的操作放在了一個事務中, 進而保證了兩個操作的原子性。

另外一個實作是對每個ip隻用一個單獨的計數器(不是每秒生成一個),但是需要注意避免竟态條件。 我們會對多種不同的變量進行測試。

上述方法的思路是,從第一個請求開始設定過期時間為1秒。如果1秒内請求數超過了10個,那麼會抛異常。

否則,計數器會清零。

上述代碼中,可能會進入競态條件,比如用戶端在執行INCR之後,沒有成功設定EXPIRE時間。這個ip的key 會造成記憶體洩漏,直到下次有同一個ip發送相同的請求過來。

把上述INCR和EXPIRE指令寫在lua腳本并執行EVAL指令可以避免上述問題(隻有redis版本&gt;=2.6才可以使用)

還可以通過使用redis的list來解決上述問題避免進入競态條件。

實作代碼更加複雜并且利用了一些redis的新的feature,可以記錄目前請求的用戶端ip位址。這個有沒有好處 取決于應用程式本身。

The <code>RPUSHX</code> command only pushes the element if the key already exists.

RPUSHX指令會往list中插入一個元素,如果key存在的話

上述實作也可能會出現競态,比如我們在執行EXISTS指令之後傳回了false,但是另外一個用戶端建立了這個key。

後果就是我們會少記錄一個請求。但是這種情況很少出現,是以我們的請求限速器還是能夠運作良好的。

本文作者:陳群

本文來自雲栖社群合作夥伴rediscn,了解相關資訊可以關注redis.cn網站。

上一篇: JVM性能調優

繼續閱讀