Files
zhuangang/topics/backend/redis/01-data-structures.md
T

3.7 KiB
Raw Blame History

id, title, type, domain, stack, tags, level, est_hours, related, week, order
id title type domain stack tags level est_hours related week order
backend.redis.data-structures 数据结构和常用命令:String、Hash、List、Set、ZSet、Stream lesson backend
redis
String
Hash
ZSet
Stream
过期时间
basic 2
backend.redis.cache-patterns
backend.redis.interview
3
1

数据结构和常用命令:String、Hash、List、Set、ZSet、Stream

解释说明

类型 是什么 常见用途 常用命令
String 字符串 / 数字 缓存 JSON、计数器、验证码 SET k v EX 60 NX、GET、INCR、INCRBY
Hash 字段 → 值 对象(用户资料、购物车) HSET、HGET、HGETALL、HINCRBY
List 双端链表 最新列表、简单队列 LPUSH、RPOP、LRANGE、BRPOP
Set 无序去重集合 标签、点赞用户、共同好友 SADD、SISMEMBER、SINTER
ZSet 按分数排序的集合 排行榜、延迟队列(分数=时间) ZADD、ZINCRBY、ZREVRANGE、ZRANGEBYSCORE
Stream 追加式日志,支持消费组 轻量消息队列 XADD、XREADGROUP、XACK

还有 Bitmap(签到)、HyperLogLog(UV 近似计数)、Geo(附近的人)。

关键概念

  • 单线程执行命令:每条命令是原子的;多条命令要原子执行用 Lua 脚本或 MULTI/EXEC。
  • 过期时间:EX 秒 / PX 毫秒;TTL 查看剩余时间。过期删除 = 惰性删除(访问时检查)+ 定期抽样删除。
  • 内存淘汰策略:内存满时按 maxmemory-policy 淘汰,缓存场景常用 allkeys-lru 或 allkeys-lfu。
  • 持久化:RDB(定时快照)和 AOF(追加命令日志),可以同时开启。

案例

  • 文章阅读数:INCR article:42:views,定时批量写回数据库。
  • 积分排行榜:ZINCRBY rank:2026-10 10 user:7,ZREVRANGE rank:2026-10 0 9 WITHSCORES 取前 10。
  • 登录验证码:SET sms:13800000000 123456 EX 300 NX,NX 保证 5 分钟内不重复发送。

代码示例

package main

import (
	"context"
	"fmt"
	"os"

	"github.com/redis/go-redis/v9"
)

func main() {
	url := os.Getenv("REDIS_URL") // 例如 redis://localhost:6379/0
	if url == "" {
		fmt.Println("未设置 REDIS_URL,跳过")
		return
	}
	opt, err := redis.ParseURL(url)
	if err != nil {
		panic(err)
	}
	rdb := redis.NewClient(opt)
	defer rdb.Close()
	ctx := context.Background()

	// 计数器
	n, _ := rdb.Incr(ctx, "demo:views:42").Result()
	fmt.Println("阅读数", n)

	// 排行榜
	rdb.ZIncrBy(ctx, "demo:rank", 30, "alice")
	rdb.ZIncrBy(ctx, "demo:rank", 50, "bob")
	rdb.ZIncrBy(ctx, "demo:rank", 10, "alice")
	top, _ := rdb.ZRevRangeWithScores(ctx, "demo:rank", 0, 9).Result()
	for i, z := range top {
		fmt.Printf("第 %d 名 %v %.0f 分\n", i+1, z.Member, z.Score)
	}

	// 对象用 Hash
	rdb.HSet(ctx, "demo:user:7", map[string]any{"name": "张三", "level": 3})
	u, _ := rdb.HGetAll(ctx, "demo:user:7").Result()
	fmt.Println(u["name"], u["level"])

	rdb.Del(ctx, "demo:views:42", "demo:rank", "demo:user:7")
}

易错点

  • 大 key:一个 Hash / ZSet 里放几百万元素,HGETALL、DEL 会阻塞 Redis。拆分 key,删除用 UNLINK。
  • KEYS *:线上会阻塞,用 SCAN 分批遍历。
  • 忘记设过期时间:缓存类 key 一律带 TTL,否则内存只增不减。
  • 用 List 做可靠队列:RPOP 后消费者崩溃,消息就丢了;需要可靠性用 Stream(消费组 + ACK)或专业 MQ。
  • 把 Redis 当主存储:持久化有丢数据窗口,重要数据以数据库为准。

面试常问

见 Redis 面试题:Redis 为什么快、有哪些数据结构和场景、过期和淘汰策略、持久化方式。