Master Sliding‑Window Rate Limiting with Redis Sorted Sets
This article explains how to implement a precise sliding‑window rate‑limiting algorithm using Redis Sorted Sets, detailing the problem scenario, the algorithm steps, Lua script implementation, Java integration, and practical extensions for high‑performance backend services.
Redis series article: implementing a sliding‑window rate‑limiting algorithm based on Redis Sorted Sets.
Scenario: a SMS verification code API or a user behavior logging endpoint is overwhelmed by thousands of requests, causing cost spikes and system crashes. The goal is a rate‑limiting method that can accurately count accesses within the past N seconds, works with Redis’s distributed nature, and responds in seconds.
Answer: Use a sliding‑window algorithm implemented with Redis Sorted Set.
The sliding‑window algorithm tracks events within a fixed time window, where the window size represents the time span and the number of keys in the set represents the frequency of events.
Specific Steps
1. Remove all values older than the current window period.
2. Count the total number of values (each value is a unique ID) in the current window.
3. If the count exceeds the allowed limit, trigger rate limiting; otherwise, add a new value with an expiration time.
4. Use ZADD to add the value (key = event identifier, score = timestamp, value = unique ID).
Rate‑Limiting Algorithm
The core idea is to limit the number of uses within a unit of time. By using Redis Sorted Set, the key (MD5 of the deduplicated content) represents an event, the score (timestamp) defines the window, and the set members count as the allowed permits. When the set reaches the threshold, further requests are blocked. The key’s TTL equals the window size, so it expires automatically when no new requests arrive.
Implementation
Lua script for atomic operations:
--KEYS[1]: limit key (deduplicated content hash)
--ARGV[1]: window size (ms)
--ARGV[2]: current timestamp (score)
--ARGV[3]: threshold (max permits)
--ARGV[4]: unique value (ID)
-- return 0 for success, 1 for limit exceeded
redis.call('zremrangeByScore', KEYS[1], 0, ARGV[2]-ARGV[1])
if (redis.call('zcard', KEYS[1]) < tonumber(ARGV[3])) then
redis.call('zadd', KEYS[1], ARGV[2], ARGV[4])
redis.call('expire', KEYS[1], ARGV[1]/1000)
return 0
else
return 1
endJava method integrating the script with Redisson:
public boolean tryAccess(String key, int permits, long time, long timeout, TimeUnit unit) {
if (permits <= 0) return false;
if (key.length() > Su4jConstants.KEY_MAX_LEN) {
key = DigestUtil.md5Hex(key);
}
key = Su4jConstants.RATELIMIT_PREFIX + key;
List<Object> keys = Collections.singletonList(key);
String score = String.valueOf(System.currentTimeMillis());
String scoreValue = IdUtil.getSnowflakeNextIdStr();
String expireTime = String.valueOf(time * 1000);
Object[] args = new Object[]{expireTime, score, permits, scoreValue};
boolean result = redissonClient.getScript(StringCodec.INSTANCE)
.eval(RScript.Mode.READ_WRITE, LUA_SCRIPT, RScript.ReturnType.BOOLEAN, keys, args);
return result;
}Extensions: the same Sorted Set technique can be applied to ranking systems, where each key represents a business entity (e.g., user points or product sales) and values are unique IDs with scores indicating the ranking metric. Regular cleanup of old entries prevents overload.
Conclusion: Using Redis Sorted Set for sliding‑window rate limiting provides precise time‑based counting, natural ordering, range queries, and automatic expiration, making it an optimal solution for high‑traffic backend services.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Lin is Dream
Sharing Java developer knowledge, practical articles, and continuous insights into computer engineering.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
