Linux C/C++ 学习日记(61):Redis(二):多种数据结构的操作指令

注:该文用于个人学习记录和知识交流,如有不足,欢迎指点。

一、redis是kv数据库

key:string

value:5 大基础核心结构

  • String(字符串)
  • Hash(哈希)
  • List(列表)
  • Set(集合)
  • Sorted Set/ZSet(有序集合)。

【 另外还有 位图(Bitmap)、地理空间(Geo) 等常用扩展结构(基于基础结构实现)】

二、string

1. 结构说明

Redis 最基础、最常用的数据结构,值可以是字符串、数字(整数 / 浮点数)、二进制数据(如图片、序列化对象),单个 String 最大支持 512MB。

特点:

字符数组,该字符串是动态字符串 raw,字符串长度小于1M 时,加倍扩容;超过 1M 每次只多扩1M;字符串最大长度为 512M;

(注意:redis 字符串是二进制安全字符串;可以存储图片,二进制协议等二进制数据)

存储结构:

  • 字符串长度小于等于 20 且能转成整数,则使用 int 存储;
  • 字符串长度小于等于 44,则使用 embstr 存储;
  • 字符串长度大于 44,则使用 raw 存储;

2. 核心操作指令

指令 语法格式 指令说明
SET `SET key value [EX seconds] [PX milliseconds] [NX | XX]` (EX、PX、NX、XX 顺序可以打乱) 设置键值对,可选参数: - EX:设置过期时间(秒) - PX:设置过期时间(毫秒) - NX:仅当键不存在时才设置(用于分布式锁) - XX:仅当键存在时才更新
GET GET key 获取指定键的值,键不存在返回 nil
INCR INCR key 对键的整数值进行原子自增 1,键不存在则先初始化为 0 再自增
DECR DECR key 对键的整数值进行原子自减 1,键不存在则先初始化为 0 再自减
INCRBY INCRBY key increment 原子自增指定数值(increment 可为正 / 负,等效于批量 INCR/DECR)
DECRBY DECRBY key decrement 原子自减指定数值
SETEX SETEX key seconds value 简化版:设置键值对并指定过期时间(秒),等效于 SET + EX
SETNX SETNX key value 简化版:仅当键不存在时设置值,等效于 SET + NX
MSET MSET key1 value1 key2 value2 ... 批量设置多个键值对,原子操作
MGET MGET key1 key2 ... 批量获取多个键的值,提升查询效率(减少网络请求)

3. 典型应用场景

  1. 单值数据缓存 :缓存商品详情、用户基本信息、首页热点数据。如果数据不常改动,可以设置多个属性(json格式)

    bash 复制代码
    SET role:10002 '{["name"]:"dcf",["sex"]:"male",["age"]:3}'
  2. 计数器系统(累加器) :文章阅读量、视频播放量、接口调用次数。

    cs 复制代码
    INCR article:read:1001 # 每次访问自增 1
  3. 临时有效数据 :手机验证码、登录临时 token。

    cs 复制代码
    SETEX sms:138xxxx1234 300 "123456" # 5 分钟后自动过期
  4. 分布式锁

    cs 复制代码
    # 分布式锁
    set lock uuid nx ex 30 # 保证锁的原子性设置和自动释放,避免死锁
    # 释放锁
    del lock
    if (get(lock) == uuid)
        del(lock)

三、list

1. 结构说明

有序、可重复的元素集合,基于双向链表实现,两端操作(头部 / 尾部)性能极高,中间插入 / 删除性能较差,适合存储有序的序列数据。

特点:

双向链表实现,列表首尾操作(删除和增加)时间复杂度O(1) ;查找中间元素时间复杂度为

O(n) ;

存储结构:

列表中数据是否压缩的依据:

  • 元素长度小于 48,不压缩;
  • 元素压缩前后长度差不超过 8,不压缩;

2. 核心操作指令

指令 语法格式 指令说明
LPUSH LPUSH key value1 [value2 ...] 从列表 ** 头部(左侧)** 插入一个 / 多个元素
RPUSH RPUSH key value1 [value2 ...] 从列表 ** 尾部(右侧)** 插入一个 / 多个元素
LPOP LPOP key 从列表头部 弹出并删除一个元素,列表为空返回 nil
RPOP RPOP key 从列表尾部 弹出并删除一个元素,列表为空返回 nil
LRANGE LRANGE key start stop 获取列表中「start 到 stop」区间的元素(索引从 0 开始,stop=-1 表示获取所有元素)
LLEN LLEN key 获取列表的元素总个数
BRPOP BRPOP key [key2 ...] timeout 阻塞式从列表尾部弹出元素(列表为空时,阻塞等待 timeout 秒,timeout=0 表示永久阻塞),适合消息队列
LREM LREM key count value 删除列表中 count 个值为 value 的元素(count>0 从头部删,count<0 从尾部删,count=0 删除所有)
LTRIM LTRIM key start stop 裁剪列表,仅保留「start 到 stop」区间的元素,删除其他元素(用于限制列表长度)

3. 典型应用场景

  1. 栈(先进后出 FILO)

    cs 复制代码
    LPUSH + LPOP
    # 或者
    RPUSH + RPOP
  2. 队列 ( 先进先出FIFO)

    cs 复制代码
    LPUSH + RPOP
    # 或者
    RPUSH + LPOP
  3. 阻塞队列 (blocking queue):

    cs 复制代码
    LPUSH + BRPOP
    # 或者
    RPUSH + BLPOP
  4. 异步消息队列:操作与队列一样,但是在不同系统间;生成者和消费者;

  5. 最新消息 / 动态列表 :朋友圈动态、文章评论列表、系统通知列表。

    cs 复制代码
    RPUSH moments:1001 "动态1" "动态2" 
    ltrim moments:1001 0 99 # 限制列表长度避免内存溢出
    LRANGE moments:1001 0 9  # 获取最新 10 条动态
  6. 分页查询(旧数据) :对于无需复杂筛选的历史数据(如用户历史订单列表),可通过 LRANGE 实现简单分页(不适合高频更新、大数据量的分页场景)。

四、hash

1. 结构说明

用于存储键值对集合 (类似 Java 中的 Map、Python 中的 dict),适合存储单个对象的多个属性,可单独操作对象的某个字段,无需修改整个对象。

特点:

散列表,在很多高级语言当中包含这种数据结构;c++ unordered_map 通过 key 快速索引

value;

存储结构:

  • 节点数量大于 512(hash-max-ziplist-entries) 或所有字符串长度大于 64(hash-max-ziplist-value),则使用 dict 实现;
  • 节点数量小于等于 512 且有一个字符串长度小于 64,则使用 ziplist 实现

2. 核心操作指令

指令 语法格式 指令说明
HSET HSET key field value [field2 value2 ...] 给指定哈希表设置一个 / 多个字段 - 值对,字段不存在则新增,存在则更新
HGET HGET key field 获取哈希表中指定字段的值
HMSET HMSET key field value [field2 value2 ...] 批量设置哈希表字段(Redis 3.0 后可直接用 HSET 替代)
HMGET HMGET key field [field2 ...] 批量获取哈希表中多个字段的值
HGETALL HGETALL key 获取哈希表中所有字段和对应的值(返回键值对列表)
HDEL HDEL key field [field2 ...] 删除哈希表中一个 / 多个字段
HLEN HLEN key 获取哈希表中字段的总个数
HKEYS HKEYS key 获取哈希表中所有字段名
HVALS HVALS key 获取哈希表中所有字段对应的值
HEXISTS HEXISTS key field 判断哈希表中指定字段是否存在,返回 1(存在)/ 0(不存在)

3. 典型应用场景

  1. 对象数据存储 :存储用户信息、商品属性、购物车功能、配置信息存储

    cs 复制代码
    # 对象数据存储:存储用户信息、商品属性 
    HSET user:1001 id 1001 name "张三" age 25 phone "138xxxx1234" 
    HSET user:1001 age 26 # 可单独更新,无需修改整个用户对象)。
    ​​​​​​​
    # 购物车功能:以「用户 ID」为 Redis 键,「商品 ID」为 Hash 字段,「商品数量」为 Hash 值
    HSET cart:1001 goods:1001 2 # 表示用户 1001 购物车中有 2 件商品 1001
    HINCRBY cart:1001 goods:1001 1 # 实现商品数量加 1)。
    
    # 配置信息存储:存储系统配置、应用参数
    HSET config:app port 8080 timeout 30 log_level "info" # 方便单独查询和修改某个配置项)。
  2. 对比string存储json格式 ,hash存储的对象使用于频繁更改属性的场景

    cs 复制代码
    hmset user name dcf age 18 sex male
    # 与 string 比较
    set string '{["name"]:"mark",["sex"]:"male",["age"]:18}'
    # 修改
    # hash:
        hset user age 19
    # string: 
        get user
        # 将得到的字符串调用json解密,取出字段,修改 age 值
        # 再调用json加密
        set user '{["name"]:"dcf",["sex"]:"male",["age"]:19}'

五、set

1. 结构说明

无序、不可重复的元素集合,基于哈希表实现,支持高效的去重和集合运算(交集、并集、差集),单个元素最大支持 512MB。

特点:

  • 集合;用来存储唯一性字段,不要求有序;
  • 存储不需要有序,操作(交并差集的时候排序)

存储结构:

  • 元素都为整数且节点数量小于等于 512(set-max-intset-entries),则使用整数数组存储;
  • 元素当中有一个不是整数或者节点数量大于 512,则使用字典存储

2. 核心操作指令

指令 语法格式 指令说明
SADD SADD key member1 [member2 ...] 向集合中添加一个 / 多个元素,重复元素自动去重
SMEMBERS SMEMBERS key 获取集合中所有元素(无序返回)
SISMEMBER SISMEMBER key member 判断元素是否在集合中,返回 1(存在)/ 0(不存在)
SREM SREM key member1 [member2 ...] 从集合中删除一个 / 多个元素
SCARD SCARD key 获取集合中元素的总个数
SINTER SINTER key1 [key2 ...] 获取多个集合的交集(所有集合中都存在的元素)
SUNION SUNION key1 [key2 ...] 获取多个集合的并集(所有集合中的不重复元素)
SDIFF SDIFF key1 [key2 ...] 获取多个集合的差集(在 key1 中存在,在其他集合中不存在的元素)
SRANDMEMBER SRANDMEMBER key [count] 从集合中随机获取 count 个元素(不删除元素,count 缺省为 1)
SPOP SPOP key [count] 从集合中随机弹出并删除 count 个元素

3. 典型应用场景

  1. 数据去重 :用户点赞列表、文章收藏列表、签到记录去重、黑白名单系统

    cs 复制代码
    SADD article:like:1001 1001 1002 # 避免同一用户重复点赞
    SISMEMBER article:like:1001 1002 # 判断用户是否已点赞
    
    SADD blacklist:user 1003 # 接口请求时通过 SISMEMBER 判断用户是否在黑名单中)。
  2. 社交关系计算 :共同好友、推荐好友、共同兴趣标签。

    cs 复制代码
    SINTER user:friends:1001 user:friends:1002 # 获取用户 1001 和 1002 的共同好友
    SUNION user:friends:1001 user:friends:1002 # 获取用户的所有好友列表。
    SDIFF user:friends:1001 user:friends:1002 # 用户 1002 可能想认识的人
  3. 随机抽奖系统 :幸运大转盘、抽奖活动。

    cs 复制代码
    SADD lottery:2026 1001 1002 ...,
    SPOP lottery:2026 3 # 随机抽取 3 名中奖用户

六、zset

1. 结构说明

有序、不可重复的元素集合,每个元素关联一个浮点型分数(score),Redis 按 score 从小到大排序(可反向排序),支持按 score 范围和排名查询,兼顾了 Set 的去重特性和 List 的有序特性。

特点:

有序集合;用来实现排行榜;它是一个有序唯一;

存储结构:

  • 节点数量大于 128 或者有一个字符串长度大于 64,则使用跳表(skiplist);
  • 节点数量小于等于 128(zset-max-ziplist-entries)且所有字符串长度小于等于 64(zset-max-ziplist-value),则使用 ziplist 存储;

复杂度:

  • 数据少的时候,节省空间; O(n)
  • 数量多的时候,访问性能;O(1) or O(log_{2}{n})

2. 核心操作指令

指令 语法格式 指令说明
ZADD ZADD key score1 member1 [score2 member2 ...] 向有序集合中添加一个 / 多个元素,score 为排序依据,重复 member 会更新其 score
ZRANGE ZRANGE key start stop [WITHSCORES] 按 score从小到大 获取「start 到 stop」区间的元素,WITHSCORES 同时返回元素和对应的 score
ZREVRANGE ZREVRANGE key start stop [WITHSCORES] 按 score从大到小获取区间元素(适合排行榜倒序查询)
ZRANGEBYSCORE ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count] 按 score 范围获取元素(min/max 可使用 ( 表示开区间,如 (100 表示大于 100)
ZINCRBY ZINCRBY key increment member 给指定元素的 score 原子增减指定数值(increment 可为正 / 负)
ZREM ZREM key member1 [member2 ...] 从有序集合中删除一个 / 多个元素
ZCARD ZCARD key 获取有序集合中元素的总个数
ZSCORE ZSCORE key member 获取指定元素对应的 score 值
ZRANK ZRANK key member 获取指定元素的排名(从小到大,排名从 0 开始)
ZREVRANK ZREVRANK key member 获取指定元素的排名(从大到小,排名从 0 开始)

3. 典型应用场景

  1. 各类排行榜系统 :销量榜、积分榜、热度榜、游戏天梯榜

    cs 复制代码
    ZADD goods:sales 100 goods:1001 200 goods:1002
    ZREVRANGE goods:sales 0 9 WITHSCORES # 获取销量前 10 商品;
    ZINCRBY goods:sales 1 goods:1001 # 实现销量加 1
  2. 延时队列 :将任务的执行时间戳作为 score,消费者定期通过 ZRANGEBYSCORE 获取当前时间之前的任务执行。

    cs 复制代码
    ZADD delay:queue 1735660800 task:1001 # 表示任务 1001 在 2025-01-01 00:00 执行)。
  3. 分布式定时器:
    生产者将定时任务 hash 到不同的 redis 实体中,为每一个 redis 实体分配一个 dispatcher 进程,用来定时获取 redis 中超时事件并发布到不同的消费者中;

  4. 时间窗口限流:
    系统限定用户的某个行为在指定的时间范围内(动态)只能发生 N 次;

    cs 复制代码
    # 指定用户 user_id 的某个行为 action 在特定时间内 period 只允许发生该行为做大次数 max_count
    local function is_action_allowed(red, userid, action, period, max_count)
        local key = tab_concat({"hist", userid, action}, ":")
        local now = zv.time()
        red:init_pipeline()
        -- 记录行为
        red:zadd(key, now, now)
        -- 移除时间窗口之前的行为记录,剩下的都是时间窗口内的记录
        red:zremrangebyscore(key, 0, now - period *100)
        -- 获取时间窗口内的行为数量
        red:zcard(key)
        -- 设置过期时间,避免冷用户持续占用内存 时间窗口的长度+1秒
        red:expire(key, period + 1)
        local res = red:commit_pipeline()
        return res[3] <= max_count
    end
    
    # 维护一次时间窗口,将窗口外的记录全部清理掉,只保留窗口内的记录;
    # 缺点:记录了所有时间窗口内的数据,如果这个量很大,不适合做这样的限流;漏斗限流
    # 注意:如果用 key + expire 操作也能实现,但是实现的是熔断限流,这里是时间窗口限流的功能;

七、常用扩展数据结构

1. 位图(Bitmap)

1.1 结构说明

并非独立数据结构,基于 String 实现,按「位」存储数据,每个位只能是 0 或 1,极大节省内存空间(1 个字节 = 8 位,可存储 8 个状态)。

1.2 核心操作指令

指令 语法格式 指令说明
SETBIT SETBIT key offset value 设置指定偏移量(offset,从 0 开始)的位值(0/1)
GETBIT GETBIT key offset 获取指定偏移量的位值
BITCOUNT BITCOUNT key [start end] 统计位图中 1 的个数(start/end 为字节索引,可选)
BITOP BITOP operation destkey key1 [key2 ...] 位图运算(AND/OR/XOR/NOT),结果存入 destkey

1.3 典型应用场景

  • 用户签到系统

    cs 复制代码
    SETBIT user:sign:1001 0 1 # 表示用户 1001 第 1 天签到
    BITCOUNT user:sign:1001 # 统计当月签到天数
  • 活跃度统计

    cs 复制代码
    # 查询用户1001是否当月有活跃
    BITOP OR user:active:202601 user:active:20260101 user:active:20260102  # 合并所有日期
    GETBIT user:active:202601 1001  # 1=有活跃,0=无活跃
    
    
    # 2. 查询用户1001是否当月满勤
    BITOP AND user:full:202601 user:active:20260101 user:active:20260102  # 合并所有日期
    GETBIT user:full:202601 1001  # 1=满勤,0=有缺勤
  • 批量状态判断(如判断用户是否领取过某类优惠券,1 表示已领取,0 表示未领取)。

2. 地理空间(Geo)

2.1 结构说明

并非独立数据结构,基于 ZSet 实现,用于存储经纬度坐标,支持距离计算、附近地点查询等地理空间操作。

2.2 核心操作指令

简略:

指令 语法格式 指令说明
GEOADD GEOADD key longitude latitude member [lon2 lat2 member2 ...] 添加地理位置(经度、纬度、地点名称)
GEOPOS GEOPOS key member1 [member2 ...] 获取指定地点的经纬度坐标
GEODIST GEODIST key member1 member2 [unit] 计算两个地点之间的距离(unit 可选:m 米、km 千米,默认 m)
GEORADIUS GEORADIUS key longitude latitude radius unit [WITHDIST] [WITHCOORD] 根据指定经纬度,查询半径范围内的地点(WITHDIST 返回距离,WITHCOORD 返回坐标)
GEOSEARCH `GEOSEARCH key FROMMEMBER member BYRADIUS radius unit [ASC | DESC]` 根据指定地点,查询半径范围内的地点(Redis 6.2+ 支持,更易用)

详尽:

指令 语法格式 指令说明
GEOADD GEOADD key longitude latitude member [lon2 lat2 member2 ...] 添加地理位置(经度、纬度、地点名称) ・限制:经度范围-180~180,纬度范围-85.05112878~85.05112878(超出会报错) ・返回值:成功添加的member数量
GEOPOS GEOPOS key member1 [member2 ...] 获取指定地点的经纬度坐标 ・返回值:每个member对应[经度, 纬度]数组;不存在的member返回nil
GEODIST GEODIST key member1 member2 [unit] 计算两个地点之间的距离 • unit可选值:m(米,默认)、km(千米)、mi(英里)、ft(英尺) • 返回值:距离字符串(如"1.5210");任意member不存在则返回nil
GEORADIUS `GEORADIUS key longitude latitude radius unit [WITHDIST] [WITHCOORD] [WITHHASH] [COUNT count] [ASC | DESC]` 根据指定经纬度,查询半径范围内的地点 ・附加参数: - WITHDIST:返回地点与中心的距离 - WITHCOORD:返回地点的经纬度 - WITHHASH:返回地点的 52 位 GeoHash 值 - COUNT count:限制返回结果的数量 - ASC/DESC:按距离「由近到远 / 由远到近」排序
GEOSEARCH `GEOSEARCH key [FROMMEMBER member | FROMLONLAT lon lat] [BYRADIUS radius unit | BYBOX width height unit] [WITHDIST] [WITHCOORD] [WITHHASH] [COUNT count] [ASC | DESC]` 灵活查询地理范围(Redis 6.2 + 支持) ・范围中心: - FROMMEMBER:以已有地点为中心 - FROMLONLAT:以指定经纬度为中心 ・范围形状: - BYRADIUS:圆形半径范围 - BYBOX:矩形范围(宽度、高度为边长) ・附加参数同GEORADIUS
GEOSEARCHSTORE `GEOSEARCHSTORE destkey sourcekey [FROMMEMBER member | FROMLONLAT lon lat] [BYRADIUS radius unit | BYBOX width height unit] [COUNT count] [ASC | DESC]` GEOSEARCH的查询结果存储到destkey(类型为 ZSet) ・返回值:成功存储的地点数量 ・用途:批量查询后持久化结果,便于后续二次处理

补充说明:

  • GEORADIUS是旧版指令(Redis <6.2),GEOSEARCH是新版替代指令(更推荐);
  • 单位支持m(米)、km(公里)、mi(英里)、ft(英尺);
  • WITHDIST会返回距离数值,ASC表示按距离由近到远排序(DESC是由远到近)。

2.3 典型应用场景

  • 附近的人 / 门店

    cs 复制代码
    # 添加外卖商家到key `store:location`(经度 纬度 商家ID)
    GEOADD store:location 116.3956 39.9299 "商家1"  # 北京王府井附近
    GEOADD store:location 116.4100 39.9100 "商家2"  # 北京东单附近
    GEOADD store:location 116.4300 39.9000 "商家3"  # 北京国贸附近(距离中心>3公里)
    
    
    # 以 "用户位置(116.40 39.90,北京建国门附近)" 为中心,
    # 查 3 公里内的商家,显示距离 + 按距离由近到远排序:
    GEORADIUS store:location 116.40 39.90 3 km WITHDIST ASC
  • 地理位置排序(如旅游平台按距离远近排序景点、酒店)。

    cs 复制代码
    # 添加旅游景点到key `scenic:location`
    GEOADD scenic:location 116.3972 39.9165 "故宫"    # 北京故宫
    GEOADD scenic:location 116.4038 39.9240 "天安门"  # 北京天安门
    GEOADD scenic:location 116.4810 39.9128 "颐和园"  # 北京颐和园(距离中心较远)
    
    # 以 "用户位置(116.40 39.90)" 为中心,查 5 公里内的景点,按距离由近到远排序
    # Redis 6.2+推荐用GEOSEARCH(更灵活):按成员为中心查询
    GEOSEARCH scenic:location FROMLONLAT 116.40 39.90 BYRADIUS 5 km WITHDIST ASC
  • 配送范围判断(如判断用户是否在商家的配送范围内,通过 GEODIST 计算距离是否小于配送阈值)。

    cs 复制代码
    # 添加外卖商家到key `store:location`(经度 纬度 商家ID)
    GEOADD store:location 116.3956 39.9299 "商家1"  # 北京王府井附近
    GEOADD store:location 116.4100 39.9100 "商家2"  # 北京东单附近
    GEOADD store:location 116.4300 39.9000 "商家3"  # 北京国贸附近(距离中心>3公里)
    
    # 假设 "商家 1 的配送范围是 2 公里",计算用户(116.40 39.90)到商家 1 的距离,
    # 判断是否≤2 公里:
    # 指令:GEODIST key 成员1 成员2 单位
    GEODIST store:location "商家1" 116.40 39.90 km

八、细节补充

1. key的设计

  • 单一功能一个key:取有意义的就行
  • 相同功能多个key:以" : "作为分割
    (tips:为什么使用冒号,业内常用,界面客户端的工具也常用:来表示子目录)

2. 怎么删除key

  • del key:删除该键值对
  • 当value为空的时候,会自动移除该key

3. 怎么创建key

  • 添加的时候,如果不存在key,那么redis会自动创建

4. 业务中常用组合数据结构

hash 的 value 存放 list/set/zet 的 key

  • hash + list
  • hash + set
  • hash + zet
相关推荐
西岸行者3 天前
学习笔记:SKILLS 能帮助更好的vibe coding
笔记·学习
悠哉悠哉愿意3 天前
【单片机学习笔记】串口、超声波、NE555的同时使用
笔记·单片机·学习
别催小唐敲代码3 天前
嵌入式学习路线
学习
毛小茛3 天前
计算机系统概论——校验码
学习
babe小鑫3 天前
大专经济信息管理专业学习数据分析的必要性
学习·数据挖掘·数据分析
winfreedoms3 天前
ROS2知识大白话
笔记·学习·ros2
在这habit之下3 天前
Linux Virtual Server(LVS)学习总结
linux·学习·lvs
我想我不够好。3 天前
2026.2.25监控学习
学习
im_AMBER3 天前
Leetcode 127 删除有序数组中的重复项 | 删除有序数组中的重复项 II
数据结构·学习·算法·leetcode
CodeJourney_J3 天前
从“Hello World“ 开始 C++
c语言·c++·学习