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































