Redis从基础到高阶应用:核心命令解析与延迟队列、事务消息实战设计

Redis基础知识

bash 复制代码
#切换数据库
bd:0>select 2
"OK"
bd:2>dbsize
"0"
#清空数据库
bd:0>flushdb
"OK"
#设置值
bd:0>set name "lyt"
"OK"
#查看所有key
bd:0>keys *
 1)  "name"
#获取key
bd:0>get name
"lyt"
#查看key是否存在
bd:0>exists name
"1"
#设置过期事件
bd:0>expire name 10
"1"
#查看剩余过期时间
bd:0>ttl name
"2"
#查看key的类型
bd:0>type name
"string"

String类型

bash 复制代码
#拼接字符串
bd:0>append name lyt
"6"
#获取字符串
bd:0>get name
"zjclyt"
#获取长度
bd:0>strlen name
"6"
#设置值
bd:0>set views 0
"OK"
#自增
bd:0>incr views
"1"
#指定自增
bd:0>incrby views 3
"4"
#自减
bd:0>decr views
"3"
#设置过期时间
bd:0>setnx lock haha
"1"
#设置后key值不可修改
bd:0>setnx lock xixi
"0"
bd:0>get lock
"haha"
bd:0>
#先get再set
bd:0>getset k1 v1
null
bd:0>getset k1 v2
"v1"

List类型

在redis中,我们可以把list玩成队列、栈或者阻塞队列!

bash 复制代码
#从左插入
bd:0>lpush list 1
"1"
bd:0>lpush list 2
"2"
bd:0>lpush list 3
"3"
#从左查看所有元素
bd:0>lrange list 0 -1
 1)  "3"
 2)  "2"
 3)  "1"
#查看指定位置的元素,下标是从0开始的,[1,2]
bd:0>lrange list 1 2
 1)  "2"
 2)  "1"
#从左边弹出一个数据
bd:0>lpop list
"3"
#获取下标位置的元素
bd:0>lindex list 1
"1"
#查看元素长度
bd:0>llen list
"2"
#从左移除第一个为1的元素 lrem key num value
bd:0>lrem list 1 1
"1"
bd:0>lrange list 0 -1
 1)  "2"

Set类型

bash 复制代码
#添加元素
bd:0>sadd set v1
"1"
bd:0>sadd set v2
"1"
bd:0>sadd set v3
"1"
#查看所有元素
bd:0>smembers set
 1)  "v2"
 2)  "v3"
 3)  "v1"
bd:0>smembers set
 1)  "v2"
 2)  "v3"
 3)  "v1"
#查看指定元素是否存在
bd:0>sismember set v1
"1"
bd:0>sismember set v4
"0"
#查看元素是否存在
bd:0>scard set
"3"
#移除指定元素
bd:0>srem set v1
"1"
bd:0>scard set
"2"
#随机获取一个元素
bd:0>srandmember set
"v3"
bd:0>srandmember set
"v2"
bd:0>srandmember set
"v2"
bd:0>spop set
"v2"
bash 复制代码
bd:0>sadd k1 a
"1"
bd:0>sadd k1 b
"1"
bd:0>sadd k1 c
"1"
bd:0>sadd k2 c
"1"
bd:0>sadd k2 d
"1"
bd:0>sadd k2 e
"1"
#差集
bd:0>sdiff k1 k2
 1)  "a"
 2)  "b"
bd:0>sdiff k2 k1
 1)  "d"
 2)  "e"
#并集
bd:0>sunion k1 k2
 1)  "a"
 2)  "b"
 3)  "c"
 4)  "d"
 5)  "e"
#交集
bd:0>sinter k1 k2
 1)  "c"
#移动元素
bd:0>smove k1 k2 c
"1"

Hash类型

bash 复制代码
#设置值
bd:0>hset user k1 v1
"1"
bd:0>hset user k12 v2
"1"
#取特定key的值
bd:0>hget user v1
null
bd:0>hget user k1
"v1"
#批量设置值
bd:0>hmset user k3 v3 k4 v4
"OK"
#获取所有key和value
bd:0>hgetall user
 1)  "k1"
 2)  "v1"
 3)  "k12"
 4)  "v2"
 5)  "k3"
 6)  "v3"
 7)  "k4"
 8)  "v4"
#删除值
bd:0>hdel user k3
"1"
#获取长度
bd:0>hlen user
"3"
#判断key是否存在
bd:0>hexists user k2
"0"
bd:0>hexists user k3
"0"
bd:0>hdel user k4
"1"
bd:0>hkeys user
 1)  "k1"
 2)  "k12"
bd:0>hvals user
 1)  "v1"
 2)  "v2"

Zset类型

案例思路:st排序存储班级成绩表,工资表排序!

普通消息,1,重要消息2,带权重进行判断!

排行榜应用实现,取Top N

bash 复制代码
#添加元素(携带分数)
bd:0>zadd rank 1 lyt
"1"
bd:0>zadd rank 2 zjc
"1"
bd:0>zadd rank 3 hyw
"1"
#按照分数排名 从小到大
bd:0>zrangebyscore rank -inf +inf
 1)  "lyt"
 2)  "zjc"
 3)  "hyw"
#获取所有值携带分数
bd:0>zrangebyscore rank -inf +inf withscores
 1)  "lyt"
 2)  "1"
 3)  "zjc"
 4)  "2"
 5)  "hyw"
 6)  "3"
#获取 (-∞,2]的数据
bd:0>zrangebyscore rank -inf 2 withscores
 1)  "lyt"
 2)  "1"
 3)  "zjc"
 4)  "2"
#获取数据 从大到小
bd:0>zrevrange rank 0 -1
 1)  "hyw"
 2)  "zjc"
 3)  "lyt"
#移除指定元素
bd:0>zrem rank lyt
"1"
#元素大小
bd:0>zcard rank
"2"
#分数在[1,1]的元素
bd:0>zcount rank 1 1
"0"
#分数在[1,2]的元素
bd:0>zcount rank 1 2
"1"

使用场景

延迟队列

延迟队列中的消息在指定时间后才能被消费,常用于定时任务、订单超时等场景。

实现方案

有序集合(ZSET)

  • 原理:将消息的到期时间戳作为分数(score),消息内容作为成员(value)。消费者定期轮询ZSET,获取当前时间之前到期的消息。

  • 关键命令

    添加消息(延迟30秒)

    ZADD delay_queue <current_timestamp+30> "message"

    轮询到期消息

    ZRANGEBYSCORE delay_queue 0 <current_timestamp> WITHSCORES ZREMRANGEBYSCORE delay_queue 0 <current_timestamp>

  • 优点:简单高效,天然支持排序。

  • 缺点:需主动轮询,间隔时间需权衡实时性和性能。

应用场景

  • 订单超时未支付自动关闭。

  • 定时提醒或通知。

  • 异步任务调度。

优先级队列

优先级队列中,高优先级的消息先被消费,适用于VIP服务、紧急任务处理。

实现方案

有序集合(ZSET)

  • 原理:将优先级作为分数(score),消息作为成员,按分数排序。

  • 关键命令

    插入消息(优先级为3)

    ZADD pri_queue 3 "message"

    获取最高优先级消息(分数范围0-5)

    ZRANGEBYSCORE pri_queue 5 0 WITHSCORES LIMIT 0 1

  • 优点:动态调整优先级,支持范围查询。

  • 缺点:需处理并发消费(需Lua脚本保证原子性)。

应用场景
  • 客服系统优先处理VIP用户请求。

  • 日志处理中优先处理错误日志。

  • 任务调度中高优先级任务插队。

队列类型 数据结构 实时性 可靠性 适用场景
延迟队列 ZSET/Redisson 定时任务、订单超时
优先级队列 多队列/ZSET VIP服务、紧急任务

事务消息的核心概念

1. 什么是事务消息?

事务消息是一种 保证消息发送与本地事务原子性 的机制,解决以下问题:

  • 场景:业务操作(如扣减库存)和消息发送(如订单创建通知)需要同时成功或失败。

  • 挑战:直接发送消息可能导致:

    • 消息发送成功,本地事务失败 → 消费者收到无效消息。

    • 本地事务成功,消息发送失败 → 业务状态不一致。

2. 事务消息的实现原理

通过 两阶段提交(2PC) 实现:

  1. Prepare 阶段

    • 将消息暂存到中间存储(如 Redis 延迟队列)。

    • 执行本地事务。

  2. Commit/Rollback 阶段

    • 本地事务成功 → 提交消息到 Kafka。

    • 本地事务失败 → 丢弃消息。

3. 代码中的事务消息逻辑

阶段 代码行为 目的
Prepare 消息存入 Redis 延迟队列 暂存消息,等待事务结果
Commit 发送消息到 Kafka,删除消息 保证消息与事务同时成功
Rollback 直接删除消息 保证消息与事务同时失败

四、关键设计点

1. 延迟队列的作用
  • 防丢失:本地事务执行期间消息暂存,避免因系统崩溃导致消息丢失。

  • 超时处理:若事务未完成,延迟队列可触发重试或告警。

2. 同步发送的权衡

template.send(...).get(); // 同步等待发送结果

  • 优点:确保消息发送成功后才删除延迟队列中的消息。

  • 缺点:阻塞当前线程,影响性能(可优化为异步 + 回调)。

java 复制代码
/**
     * 发送事务消息
     *
     * @param topic     消息主题
     * @param payload   消息内容
     * @param ordering  顺序消息标识
     * @param partition 指定分区
     * @param callback  本地事务回调函数
     * @param <K>       顺序标识类型
     * @param <V>       消息载体类型
     */
    public static <K, V> void send(@NonNull String topic, @NonNull V payload, K ordering, Integer partition,
                                   @NonNull Predicate<String> callback) {
        // 将事务消息放入延时队列
        String id = StringUtils.uuid();
        Session session = SessionContextHolder.getSession(false);
        TransactionMessage transaction = TransactionMessage.builder().id(id).topic(topic).payload(payload)
                .ordering(ordering).partition(partition).session(session).build();
        String message = JacksonUtils.serialize(transaction);
        DelayRedisQueue<String> queue = getTransactionQueue();
        queue.offer(message, Duration.ofMillis(queue.getTimeout()));

        // 处理本地事务
        boolean committable;
        try {
            committable = callback.test(id);
        } catch (Exception e) {
            // 本地事务处理异常则删除事务延时消息
            try {
                queue.remove(message);
            } catch (Throwable t) {
                log.error("Kafka transaction message remove failed: {}", message, t);
            }
            throw e;
        }

        // 本地事务处理成功则发送Kafka消息
        if (committable) {
            KafkaTemplate<K, V> template = getKafkaTemplate();
            try {
                template.send(topic, partition, ordering, payload).get();
            } catch (InterruptedException | ExecutionException e) {
                throw new RuntimeException(e);
            }
        }

        // 处理完成后删除事务延时消息
        try {
            queue.remove(message);
        } catch (Throwable t) {
            log.error("Kafka transaction message remove failed: {}", message, t);
        }
    }
相关推荐
码农飞哥21 分钟前
互联网大厂Java面试实战:从Spring Boot到微服务的技术问答与解析
java·数据库·spring boot·安全·微服务·面试·电商
雨落白笙21 分钟前
端口转发与跨域处理
java
焜昱错眩..38 分钟前
代码随想录训练营第二十一天 |589.N叉数的前序遍历 590.N叉树的后序遍历
数据结构·算法
曼岛_43 分钟前
[Java实战]Spring Boot 定时任务(十五)
java·spring boot·python
Go高并发架构_王工1 小时前
从零到精通:GoFrame ORM 使用指南 - 特性、实践与经验分享
数据结构·经验分享·golang
oliveira-time1 小时前
app加固
java
菲兹园长1 小时前
MyBatis-Plus
java·开发语言·mybatis
计算机学姐1 小时前
基于SpringBoot的在线教育管理系统
java·vue.js·spring boot·后端·mysql·spring·mybatis
onkel in blog1 小时前
【Docker】Docker Compose方式搭建分布式内存数据库(Redis)集群
数据库·redis·分布式·docker
菜鸟破茧计划1 小时前
滑动窗口:穿越数据的时光机
java·数据结构·算法