我干后端这些年,见过太多人把Redis当高级memcache用。set一个key,get一个key,完事。最离谱的是,有人问我:Redis和Memcache有什么区别?我说:大概就是兰博基尼和五菱宏光的区别吧,你非拿超跑拉货也不是不行,就是有点暴殄天物。
今天我就给你们看看,这辆兰博基尼到底能跑多快。
第一件事:Sorted Set 實現即時排行榜
游戏服务器最怕什么?排行榜。每天几百万人同時查榜,傳統方案是寫進數據庫,order by score desc limit 10,數據庫表示我去年買了個錶。
Redis的Sorted Set帮你搞定:
// 玩家積分更新
ZADD leaderboard:2026 8500 "player:9527"
// 前十名排行榜
ZREVRANGE leaderboard:2026 0 9 WITHSCORES
// 查詢某個玩家的排名
ZREVRANK leaderboard:2026 "player:9527"
// 計算某個分數區間有多少人
ZCOUNT leaderboard:2026 8000 9000
原理很簡單:內部是跳躍表(Skip List),查詢複雜度O(log N),無論數據多少,查詢時間都穩定。你沒看錯,是穩定。不是數據量越大越慢的那種垃圾實現。
之前做遊戲的時候,1200萬玩家的即時排行榜,查詢時間穩定在2毫秒以內。MySQL?對不起,1秒往上走。
第二件事:GEO 命令做「附近的人」
外賣、交友、房產APP,幾乎都有「附近的人」功能。傳統方案:經緯度存數據庫,計算兩點距離,SELECT周圍多少公里,數據庫又開始罵娘了。
Redis給你安排得明明白白:
// 添加商戶位置 (經緯度)
GEOADD shops:location 116.397128 39.916527 "shop:001"
GEOADD shops:location 116.397228 39.916627 "shop:002"
GEOADD shops:location 116.398128 39.917527 "shop:003"
// 查詢方圓3公里內的商戶
GEORADIUS shops:location 116.397128 39.916527 3 km WITHDIST ASC COUNT 10
// 查詢某商戶方圓5公里內有多少個商戶
GEODIST shops:location "shop:001" "shop:002" km
背後是GeoHash算法,把二維的經緯度編碼成一維字符串,附近的位置有相同前綴。查詢方圓5公里,響應時間不到1毫秒。
有人問我:為什麼不用PostGIS?我說你高興就好,PostGIS查一個方圓5公里要多少資源你不清楚嗎?
第三件事:Bitmap 做精確簽到統計
用戶簽到、活躍統計,常見方案:每天一張表,幾千萬用戶,COUNT(*)跑半天。Redis Bitmap讓你用一個字節存8天的簽到信息:
// 用戶2026年5月1日簽到
SETBIT user:sign:2026:05:01 95270000 1
// 查詢某用戶連續簽到天數
// 需要程序配合,遍歷每天的BITCOUNT
// 統計某天簽到人數
BITCOUNT user:sign:2026:05:01
假設你有5000萬用戶,一天的簽到數據只需要6.25MB內存。6.25MB!同等數據存MySQL起碼要幾百MB還不帶索引。
有人說這不是內存換時間嗎?我說你這個認知有問題,這叫用極低成本換極高收益,懂不懂?
第四件事:HyperLogLog 做 UV 統計
UV(獨立訪客)統計,每個運営同學都要看。但精確UV需要存每個用戶ID,內存開銷巨大。HyperLogLog用概率演算法,0.81%誤差的代價,換取極小的記憶體開銷:
// 記錄一次訪問
PFADD page:uv:2026:05:16 "user:session:9527"
PFADD page:uv:2026:05:16 "user:session:8866"
// 獲取UV估值
PFCOUNT page:uv:2026:05:16
// 合併多個頁面的UV(假設你要算全站UV)
PFMERGE page:uv:month:2026:05 page:uv:2026:05:01 page:uv:2026:05:02 page:uv:2026:05:03
HyperLogLog每個key只需要12KB記憶體,就能統計2的64次方那麼多用戶。12KB!這是什么概念,你用任何資料庫統計同等數量的UV,沒有幾百MB下不來。
有人問誤差不會有問題嗎?我說你一個月UV有12億6884萬,用HyperLogLog算出來12億6900萬,誤差12萬,你運營同學看得出來?看不出來是吧,那就用。
第五件事:Stream 做輕量級消息隊列
RabbitMQ、Kafka,你們是挺好,但部署維護一套消息隊列要多少精力?Redis Stream讓你在已有Redis的情況下,擁有一個堪用的消息隊列:
// 生產消息
XADD mystream:orders * order_id "ORD-20260516-001" amount 299 status "pending"
// 消費消息(阻塞方式)
XREADGROUP group1 consumer1 BLOCK 3000 STREAMS mystream:orders ">"
// 創建消費者組
XGROUP CREATE mystream:orders consumer-group 0
// 確認消息已處理
XACK mystream:orders consumer-group 1654214400000-0
有人說Stream功能不如Kafka。我說你說得對,但問題是你有沒有那麼大的量級?日均百萬消息以下,Kafka能做的Stream都能做,還不用折騰那套JVM參數調優。
之前有個小項目,日均訂單十幾萬,用Kafka浪費,用Redis Stream剛好。老闆問你消息隊列用的什麼,你說Redis,他以為你在開玩笑,其實你是認真的。
第六件事:Pipeline 批量操作省網路開銷
這個嚴格來說不算是「神奇用法」,但我發現90%的Redis用戶都不知道或者從來不用Pipeline,導致明明可以並行的操作變成了串行:
// 錯誤示範:十次網路來回
for i in range(10):
r.get(f"key:{i}") # 10次網路RTT
// 正確姿勢:一次網路來回
pipe = r.pipeline()
for i in range(10):
pipe.get(f"key:{i}")
results = pipe.execute() # 1次網路RTT
很多人不知道這個差異有多大。在本機延遲1ms的環境下,10個key的查詢,無Pipeline要10ms,有Pipeline只需要1ms。如果網路延遲更高,這個差距會更誇張。
有人說現在網路延遲很低影響不大。我說你去看看你們的監控,有多少介面耗時就耗在Redis網路來回上,你就知道Pipeline有多香了。
總結:Redis不是cache,是數據結構服務器
寫到這裡,我發現這篇文章其實就在講一句話:Redis不是cache,至少不僅僅是cache。它的五種基本數據結構(String、Hash、List、Set、Sorted Set),加上GEO、Bitmap、HyperLogLog、Stream這些高級功能,讓它成為了瑞士軍刀一樣的存在。
你用它做cache,它不會說什麼,它就是脾氣好。但如果你有點追求,試試上面的用法,你會發現:
同樣的機器、同樣的資源,有些人的系統能扛住十倍流量,有些人的系統一碰就崩。差距不在於你用了什麼框架,而在於你有沒有把工具用到極致。
蘭博基尼用來飆車是本分,用來拉貨是暴殄天物。但問題是——你連飆車都不會,怪誰?
下次有人問你Redis能用來做什麼,別再只回答「做緩存」了。建議你反問他:你想要即時排行榜、附近的人、精確UV統計、還是輕量級消息隊列?
看他一臉懵的样子,你不覺得很快樂嗎?