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);
        }
    }
相关推荐
Javatutouhouduan13 分钟前
Java全栈面试进阶宝典:内容全面,题目高频!
java·高并发·java面试·java面试题·后端开发·java程序员·java八股文
SEO-狼术26 分钟前
RAD Studio 13.1 Florence adds
java
ywf121537 分钟前
Spring Boot接收参数的19种方式
java·spring boot·后端
敲代码的瓦龙1 小时前
Java?面向对象三大特性!!!
java·开发语言
架构师沉默1 小时前
AI 写的代码,你敢上线吗?
java·后端·架构
野犬寒鸦1 小时前
Redis复习记录day1
服务器·开发语言·数据库·redis·缓存
骑龙赶鸭1 小时前
java开发项目中遇到的难点,面试!
java·开发语言·面试
NGC_66112 小时前
Java线程池七大核心参数介绍
java·开发语言