INCR key
@write
@string
@fast
Increments the number stored at key
by one.
If the key does not exist, it is set to 0
before performing the operation.
An error is returned if the key contains a value of the wrong type or contains a
string that can not be represented as integer.
This operation is limited to 64 bit signed integers.
Note: this is a string operation because Redis does not have a dedicated integer type. The string stored at the key is interpreted as a base-10 64 bit signed integer to execute the operation.
Redis stores integers in their integer representation, so for string values that actually hold an integer, there is no overhead for storing the string representation of the integer.
@return
@integer-reply: the value of key
after the increment
@examples
redis> SET mykey "10"
TBD
redis> INCR mykey
TBD
redis> GET mykey
TBD
redis> ```
TBD
redis> ## Pattern: Counter
TBD
redis> The counter pattern is the most obvious thing you can do with Redis atomic
TBD
redis> increment operations.
TBD
redis> The idea is simply send an [`INCR`](/commands/incr) command to Redis every time an operation
TBD
redis> occurs.
TBD
redis> For instance in a web application we may want to know how many page views this
TBD
redis> user did every day of the year.
TBD
redis> To do so the web application may simply increment a key every time the user
TBD
redis> performs a page view, creating the key name concatenating the User ID and a
TBD
redis> string representing the current date.
TBD
redis> This simple pattern can be extended in many ways:
TBD
redis> * It is possible to use [`INCR`](/commands/incr) and [`EXPIRE`](/commands/expire) together at every page view to have
TBD
redis> a counter counting only the latest N page views separated by less than the
TBD
redis> specified amount of seconds.
TBD
redis> * A client may use GETSET in order to atomically get the current counter value
TBD
redis> and reset it to zero.
TBD
redis> * Using other atomic increment/decrement commands like [`DECR`](/commands/decr) or [`INCRBY`](/commands/incrby) it
TBD
redis> is possible to handle values that may get bigger or smaller depending on the
TBD
redis> operations performed by the user.
TBD
redis> Imagine for instance the score of different users in an online game.
TBD
redis> ## Pattern: Rate limiter
TBD
redis> The rate limiter pattern is a special counter that is used to limit the rate at
TBD
redis> which an operation can be performed.
TBD
redis> The classical materialization of this pattern involves limiting the number of
TBD
redis> requests that can be performed against a public API.
TBD
redis> We provide two implementations of this pattern using [`INCR`](/commands/incr), where we assume
TBD
redis> that the problem to solve is limiting the number of API calls to a maximum of
TBD
redis> _ten requests per second per IP address_.
TBD
redis> ## Pattern: Rate limiter 1
TBD
redis> The more simple and direct implementation of this pattern is the following:
TBD
redis> ```
TBD
redis> FUNCTION LIMIT_API_CALL(ip)
TBD
redis> ts = CURRENT_UNIX_TIME()
TBD
redis> keyname = ip+":"+ts
TBD
redis> MULTI
TBD
redis> INCR(keyname)
TBD
redis> EXPIRE(keyname,10)
TBD
redis> EXEC
TBD
redis> current = RESPONSE_OF_INCR_WITHIN_MULTI
TBD
redis> IF current > 10 THEN
TBD
redis> ERROR "too many requests per second"
TBD
redis> ELSE
TBD
redis> PERFORM_API_CALL()
TBD
redis> END
TBD
redis> ```
TBD
redis> Basically we have a counter for every IP, for every different second.
TBD
redis> But this counters are always incremented setting an expire of 10 seconds so that
TBD
redis> they'll be removed by Redis automatically when the current second is a different
TBD
redis> one.
TBD
redis> Note the used of [`MULTI`](/commands/multi) and [`EXEC`](/commands/exec) in order to make sure that we'll both
TBD
redis> increment and set the expire at every API call.
TBD
redis> ## Pattern: Rate limiter 2
TBD
redis> An alternative implementation uses a single counter, but is a bit more complex
TBD
redis> to get it right without race conditions.
TBD
redis> We'll examine different variants.
TBD
redis> ```
TBD
redis> FUNCTION LIMIT_API_CALL(ip):
TBD
redis> current = GET(ip)
TBD
redis> IF current != NULL AND current > 10 THEN
TBD
redis> ERROR "too many requests per second"
TBD
redis> ELSE
TBD
redis> value = INCR(ip)
TBD
redis> IF value == 1 THEN
TBD
redis> EXPIRE(ip,1)
TBD
redis> END
TBD
redis> PERFORM_API_CALL()
TBD
redis> END
TBD
redis> ```
TBD
redis> The counter is created in a way that it only will survive one second, starting
TBD
redis> from the first request performed in the current second.
TBD
redis> If there are more than 10 requests in the same second the counter will reach a
TBD
redis> value greater than 10, otherwise it will expire and start again from 0.
TBD
redis> **In the above code there is a race condition**.
TBD
redis> If for some reason the client performs the [`INCR`](/commands/incr) command but does not perform
TBD
redis> the [`EXPIRE`](/commands/expire) the key will be leaked until we'll see the same IP address again.
TBD
redis> This can be fixed easily turning the [`INCR`](/commands/incr) with optional [`EXPIRE`](/commands/expire) into a Lua
TBD
redis> script that is send using the [`EVAL`](/commands/eval) command (only available since Redis version
TBD
redis> 2.6).
TBD
redis> ```
TBD
redis> local current
TBD
redis> current = redis.call("incr",KEYS[1])
TBD
redis> if current == 1 then
TBD
redis> redis.call("expire",KEYS[1],1)
TBD
redis> end
TBD
redis> ```
TBD
redis> There is a different way to fix this issue without using scripting, but using
TBD
redis> Redis lists instead of counters.
TBD
redis> The implementation is more complex and uses more advanced features but has the
TBD
redis> advantage of remembering the IP addresses of the clients currently performing an
TBD
redis> API call, that may be useful or not depending on the application.
TBD
redis> ```
TBD
redis> FUNCTION LIMIT_API_CALL(ip)
TBD
redis> current = LLEN(ip)
TBD
redis> IF current > 10 THEN
TBD
redis> ERROR "too many requests per second"
TBD
redis> ELSE
TBD
redis> IF EXISTS(ip) == FALSE
TBD
redis> MULTI
TBD
redis> RPUSH(ip,ip)
TBD
redis> EXPIRE(ip,1)
TBD
redis> EXEC
TBD
redis> ELSE
TBD
redis> RPUSHX(ip,ip)
TBD
redis> END
TBD
redis> PERFORM_API_CALL()
TBD
redis> END
The RPUSHX
command only pushes the element if the key already exists.
Note that we have a race here, but it is not a problem: EXISTS
may return
false but the key may be created by another client before we create it inside
the MULTI
/ EXEC
block.
However this race will just miss an API call under rare conditions, so the rate
limiting will still work correctly.