系統不崩的秘籍:Redis的六大非緩存玩法,支撐高并發流量就靠它!
一、分布式鎖
秒殺高并發排隊神器,
當年做電商秒殺,數據庫行鎖直接被打到 奄奄一息。
Redis 一出馬,線程乖乖排隊,老板再也不用擔心超賣。
// Maven 依賴
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.23.4</version>
</dependency>
Config config = new Config();
config.useSingleServer().setAddress("redis://127.0.0.1:6379");
RedissonClient redisson = Redisson.create(config);
RLock lock = redisson.getLock("stock:1001");
try {
// **30 秒內搶鎖,鎖 30 秒后自動釋放**
if (lock.tryLock(30, 30, TimeUnit.SECONDS)) {
updateStockInDB(); // 真正的減庫存邏輯
}
} finally {
if (lock.isHeldByCurrentThread()) {
lock.unlock(); // **一定記得手動釋放**
}
}注解
1. tryLock 帶時間參數,防止死鎖。
2. finally 釋放鎖,避免“占著茅坑不拉屎”。
3. 集群場景用 RedLock,主節點掛了也能繼續嗨。
二、計數器:2 萬 QPS 的救火隊長
直播在線人數用數據庫 UPDATE?
直接 200 QPS 就跪。
Redis 的 INCR 讓在線人數 像坐火箭一樣飆升。
Jedis jedis = new Jedis("redis://127.0.0.1:6379");
String key = "rate_limit:user:" + userId + ":minute";
long count = jedis.incr(key);
if (count == 1) {
jedis.expire(key, 60); // **首次創建,設 60 秒過期**
}
if (count > 100) {
throw new RuntimeException("手速太快,歇會兒!");
}注解
1. incr 原子自增,天然抗并發。
2. expire 只在第一次設置,避免每次請求都刷新 TTL。
3. 100 次/分鐘閾值可配置,靈活限流。
三、排行榜:ZSET 的凡爾賽舞臺
年度博主排行?數據庫跑 3 小時,ZSET 5 分鐘搞定。
關鍵是還能 實時更新!。
// 點贊一次,分數 +1
redisTemplate.opsForZSet()
.incrementScore("blog:like:rank", "user:1001", 1);
// 取前 10
Set<ZSetOperations.TypedTuple<String>> top10 =
redisTemplate.opsForZSet()
.reverseRangeWithScores("blog:like:rank", 0, 9);
// 查個人排名
Long rank = redisTemplate.opsForZSet()
.reverseRank("blog:like:rank", "user:1001");注解
1. incrementScore 原子操作,并發點贊不丟分。
2. reverseRangeWithScores 一次取出成員和分數,減少網絡往返。
3. reverseRank 查排行 O(log n),再多人也不怕。
四、輕量消息隊列:異步快遞員
訂單高峰 10 倍流量?
把訂單先丟 Redis List,后臺慢慢消費,爆倉不存在的。
// 生產者
jedis.lpush("order_queue", JSON.toJSONString(order));
// 消費者
while (true) {
List<String> msg = jedis.brpop(0, "order_queue");
if (msg != null) {
try {
processOrder(msg.get(1));
} catch (Exception e) {
// 失敗丟到重試隊列
jedis.lpush("order_retry_queue", msg.get(1));
}
}
}注解
1. brpop 阻塞讀取,CPU 不空轉。
2. 失敗消息進入 retry 隊列,可配合延時策略。
3. List 隊列簡單可靠,但 沒有 ACK,需自己實現重試。
五、會話共享:分布式登錄管家
用戶在 A 機器登錄,跳到 B 機器就掉線?
用 Redis 做 統一 Session 倉庫,媽媽再也不用擔心我踢用戶下線。
# application.yml
spring:
session:
store-type: redis
timeout: 30m
redis:
host: 127.0.0.1
port: 6379注解
1. spring-session-data-redis 一行配置搞定。
2. SessionID 隨機的 UUID + 簽名,防偽造。
3. 每次登錄重新生成 SessionID,舊 Session 秒失效。
六、地理位置:附近門店秒查
“附近 5 公里有啥好吃的?”
Redis GEO 一出手,5 倍速出結果。
// 添加門店坐標
redisTemplate.opsForGeo()
.add("shop:locations",
new Point(116.4074, 39.9042), "shop:1001");
// 查 5 公里內,由近到遠
GeoResults<GeoLocation<String>> results =
redisTemplate.opsForGeo()
.radius("shop:locations",
new Circle(new Point(116.4074, 39.9042),
Metrics.KILOMETERS.toMeters(5)),
GeoRadiusCommandArgs.newGeoRadiusArgs()
.includeDistance()
.sortAscending());
results.getContent().forEach(r ->
System.out.println(r.getContent().getName()
+ " 離你 " + r.getDistance().getValue() + " 米"));注解
1. GEOADD 一次可插多條,批量效率高。
2. GEORADIUS 默認 O(N),但 Redis 用 geohash + 跳表,N<10W 穩穩的。
3. 距離單位支持 m/km/ft/mi,國際化項目無壓力。
七、野路子合集:老碼農私貨
7.1 滑動窗口限流
String key = "rate_limit:user:" + userId;
long now = System.currentTimeMillis();
// 清理 60 秒前記錄
redisTemplate.opsForZSet()
.removeRangeByScore(key, 0, now - 60000);
// 記錄當前時間戳
redisTemplate.opsForZSet().add(key, String.valueOf(now), now);
// 統計窗口內請求數
Long cnt = redisTemplate.opsForZSet().zCard(key);
if (cnt > 50) throw new RuntimeException("刷太快,小黑屋見!");注解
1. ZSET 按時間排序,天然滑動窗口。
2. removeRangeByScore 批量清理,O(log n)。
3. zCard 計數,內存占用極低。
7.2 樂觀鎖搶購
jedis.watch("stock:1001");
int stock = Integer.parseInt(jedis.get("stock:1001"));
if (stock > 0) {
Transaction tx = jedis.multi();
tx.decr("stock:1001");
List<Object> res = tx.exec();
if (res == null) {
// **被其他客戶端改了,重試**
}
}注解
1. watch + multi + exec 保證 原子減庫存。
2. exec 返回 null 即版本沖突,自旋重試即可。
3. 適合 秒殺庫存少、沖突高 的場景。
至此分享完畢,希望以上內容對你有所幫助!





























