redis多種方式實現訪問計數器例項詳解

2021-07-23 17:41:53 字數 3385 閱讀 2028

用法

incrkey,可以將key值原子自增1,並返回遞增操作後key對應的新值。如果指定的key不存在,那麼在執行incr操作之前,會先將它的值設定為0。

/*測試前,清除當前資料庫所有key*/

127.0

.0.1:6379> flushdb

ok/*沒有key*/

127.0

.0.1:6379> keys *

(empty list or

set)

/*使用incr 乙個不存在的key,有返回為1(如果指定的key不存在,那麼在執行incr操作之前,會先將它的值設定為0,並返回自增後的值1)*/

127.0

.0.1:6379> incr incrkey

(integer) 1

127.0

.0.1:6379> get incrkey

"1"/*自增1,返回增加後的值2*/

127.0

.0.1:6379> incr incrkey

(integer) 2

127.0

.0.1:6379> get incrkey

"2"

使用場景1 - 計數器

例如:乙個web應用,我們想記錄每個使用者每天訪問這個**的次數。就可以使用這個使用者的id和當天日期拼接乙個key,每訪問一次只用incr對該key操作,從而獲得該使用者當天的訪問**次數。比如使用者id為9eda3e419e6eadb99293f5c9105816c93a0ca760,今日是20161015,則可以使用incr 9eda3e419e6eadb99293f5c9105816c93a0ca760:20161015作為統計該使用者在2016-10-15當天的訪問次數。

該場景的擴充套件:統計該使用者在某個時間範圍之內的訪問次數,可以結合incr、expire來達到目標。

使用場景2 - 限制訪問次數(一)

假設我們有這樣的需求:每個api介面,每秒每個ip的訪問次數不能超過10次。

我們可以為ip:時間戳(到秒)設定key,以下使用偽碼展示:

function limit_access_count(ip)

currsecond = current_unix_time()

keyname = ip+":"+currsecond

currentcnt = get(keyname)

if currentcnt != null

and currentcnt > 10

then

error "一秒內訪問次數過多"

else

multi

/*比如10.192.168.27在2016-10-15 15:20:19時訪問次數不到10,一直自增*/

incr(keyname,1)

/*計數器每次遞增的時候都設定了10秒的過期時間,這樣在進入下一秒時,redis會自動刪除前一秒的計數器。

* 鍵 10.192.168.27:2016-10-15 15:20:19將會在2016-10-15 15:20:29之後刪除

*/expire(keyname,10)

exec

do_job()

end

使用場景2 - 限制訪問次數(二)

前面例子是每個ip每一秒都生成乙個key。在此例中,我們乙個ip只會生成乙個key,但是實際使用中需要注意競態條件的出現。

具體思路是:從第乙個請求開始設定過期時間為1秒。如果1秒內請求數超過了10個,那麼會提示錯誤資訊。到了下一秒,計數器會清零後重新開始計數。

function limit_access_count(ip)

keyname = ip

currentcnt = get(keyname)

if currentcnt != null

and currentcnt > 10

then

error "一秒內訪問次數過多"

else

multi

/*比如10.192.168.27在2016-10-15 15:20:19時訪問次數不到10,一直自增*/

currentcnt = incr(ip)

if currentcnt == 1

then

/*計數器每次遞增的時候都設定了1秒的過期時間,只有在第一次訪問時才設定超時時間為1秒

* 鍵 10.192.168.27:2016-10-15 15:20:19將會在2016-10-15 15:20:20之後刪除

*/expire(keyname,1)

endexec

do_job()

end

處理競態條件: 使用lua指令碼。

在前面的例子中,如果使用incr後,沒有成功執行expire,會導致這個ip的key引起記憶體洩漏,知道下次有同乙個ip傳送相同請求過來。可以將可能發生競態條件的邏輯放在lua指令碼中,再使用eval解決(要求redis2.6版本以上)

/*lua指令碼*/

local currentcnt

currentcnt = redis.call("incr",keys[1])

if tonumber(currentcnt) == 1

then

redis.call("expire",keys[1],1)

end

getsetkey value 會將value設定為key的值,但是返回的是key原來的值。如果key存在但是對應的value不是字串,就返回錯誤。如果之前key不存在將返回nil。

127.0

.0.1:6379> flushdb

ok127.0

.0.1:6379> keys *

(empty list or

set)

/*使用incr實現計數器自增,使用getset可以重置為0*/

127.0

.0.1:6379> incr testkey

(integer) 1

127.0

.0.1:6379> incr testkey

(integer) 2

127.0

.0.1:6379> getset testkey 0

"2"127.0

.0.1:6379> get testkey

"0"/*key不存在返回nil*/

127.0

.0.1:6379> getset testkey2 0

(nil)

多種方式實現http服務

在對伺服器實現web服務的時候,面對多個使用者的請求時,我們可以採取多程序或者多執行緒。下面是他們的實現 import urllib.request import multiprocessing import re import socket def service client new socke...

redis實現訪問頻次限制的幾種方式

頻次限制器模式是一種特殊的計數器,它常被用來限制某個操作可以被執行的頻次。這個模式的實質其實是限制對乙個公共api執行訪問請求的次數限制。我們使用incr命令提供該模式的兩種實現。這裡我們假設需要解決的問題是 對每個ip,限制對某api的呼叫次數最高位10次每秒。對該模式乙個相對簡單和直接的實現,請...

redis實現訪問頻次限制的幾種方式

頻次限制器模式是一種特殊的計數器,它常被用來限制某個操作可以被執行的頻次。這個模式的實質其實是限制對乙個公共api執行訪問請求的次數限制。我們使用incr命令提供該模式的兩種實現。這裡我們假設需要解決的問題是 對每個ip,限制對某api的呼叫次數最高位10次每秒。對該模式乙個相對簡單和直接的實現,請...