一、Redis 命令基础
Redis 是键值(Key-Value)型内存数据库,命令具有以下特点:
- 命令不区分大小写(如
GET/get都生效),但Key/Value 区分大小写; - 命令参数用空格分隔,部分命令支持可选参数(如过期时间);
- 所有命令都围绕 "操作 Key 或 Key 对应的 Value" 展开。
二、核心命令分类讲解
1. 通用命令(所有数据类型通用)
这类命令用于管理 Key 本身,与 Value 类型无关,是最基础的操作。
| 命令 | 作用 | 示例(结合设备状态场景) |
|---|---|---|
KEYS pattern |
按通配符匹配查询所有 Key(生产环境慎用,会阻塞 Redis) | KEYS device:status:GW001:* → 查 GW001 设备所有从机状态 Key |
EXISTS key |
判断 Key 是否存在(返回 1 = 存在,0 = 不存在) | EXISTS device:status:GW001:03 → 检查 03 从机状态是否缓存 |
DEL key [key...] |
删除指定 Key(返回删除成功的数量) | DEL device:status:GW001:03 → 删除 03 从机状态缓存 |
EXPIRE key sec |
为 Key 设置过期时间(单位:秒,返回 1 = 成功,0=Key 不存在) | EXPIRE device:status:GW001:03 3600 → 1 小时过期 |
TTL key |
查询 Key 的剩余过期时间(返回秒数;-1 = 永不过期;-2=Key 不存在) | TTL device:status:GW001:03 → 查看剩余过期时间 |
PERSIST key |
移除 Key 的过期时间(设为永不过期) | PERSIST device:status:GW001:03 → 取消过期 |
RENAME old new |
重命名 Key(若 new 已存在则覆盖) | RENAME device:status:GW001:03 device:status:GW001:04 |
2. 字符串(String)命令
String 是 Redis 最基础的 Value 类型,可存储字符串、数字、序列化对象(如我代码中的 DeviceStatusCacheDTO),即之前 saveDeviceStatus 中使用的类型。
| 命令 | 作用 | 示例(设备状态场景) |
|---|---|---|
SET key value [EX sec] |
设置 Key-Value,可选 EX 指定过期时间(秒) |
SET device:status:GW001:03 "online" EX 3600 |
SETNX key value |
仅当 Key 不存在时设置(避免覆盖,返回 1 = 成功,0 = 失败) | SETNX device:status:GW001:03 "online" |
GET key |
获取 Key 对应的 Value(返回 null 表示 Key 不存在) | GET device:status:GW001:03 → 获取 03 从机状态 |
MSET key1 v1 key2 v2... |
批量设置多个 Key-Value(效率高于多次 SET) | MSET a 1 b 2 c 3 |
MGET key1 key2... |
批量获取多个 Key 的 Value(返回值数组) | MGET a b c → ["1","2","3"] |
INCR key |
将 Key 的数值 Value 自增 1(非数字则报错) | INCR alarm:count:GW001 → 统计 GW001 告警次数 |
INCRBY key num |
数值 Value 自增指定数(如 INCRBY key 5) | INCRBY alarm:count:GW001 2 → 告警次数 + 2 |
DECR key/DECRBY key num |
数值 Value 自减 1 / 指定数(与 INCR 对应) | DECR alarm:count:GW001 |
我的代码关联 :redisTemplate.opsForValue().set(key, status, expire, unit) 底层就是调用 SET key value EX 过期秒数 命令。
3. 哈希(Hash)命令
Hash 用于存储 "Key - 字段 - 值" 的二维结构,适合存储单个对象的多个属性(如设备状态的多个字段),比 String 存储序列化对象更灵活(可单独修改某个字段)。
| 命令 | 作用 | 示例(设备状态场景) |
|---|---|---|
HSET key field value |
给 Hash Key 设置指定字段的值(字段不存在则新增,存在则覆盖) | HSET device:status:GW001:03 online_status 1 |
HGET key field |
获取 Hash Key 中指定字段的值 | HGET device:status:GW001:03 online_status → 1 |
HMSET key f1 v1 f2 v2... |
批量设置 Hash Key 的多个字段值 | HMSET device:status:GW001:03 power 0 battery 0 |
HMGET key f1 f2... |
批量获取 Hash Key 的多个字段值 | HMGET device:status:GW001:03 power battery → ["0","0"] |
HGETALL key |
获取 Hash Key 的所有字段和值(返回 [f1,v1,f2,v2...] 数组) | HGETALL device:status:GW001:03 → 所有状态字段 |
HDEL key field [field...] |
删除 Hash Key 的指定字段 | HDEL device:status:GW001:03 rssi |
HEXISTS key field |
判断 Hash Key 中指定字段是否存在 | HEXISTS device:status:GW001:03 online_status → 1 |
4. 列表(List)命令
List 是双向链表结构,适合存储有序的一组数据(如设备告警日志、消息队列)。
| 命令 | 作用 | 示例 |
|---|---|---|
LPUSH key value... |
从列表左侧(头部)添加元素(返回列表长度) | LPUSH alarm:log:GW001 "2025-11-11 14:35 离线" |
RPUSH key value... |
从列表右侧(尾部)添加元素 | RPUSH alarm:log:GW001 "2025-11-11 14:40 恢复" |
LPOP key |
从列表左侧弹出并返回元素(列表空则返回 null) | LPOP alarm:log:GW001 → 获取最新告警日志 |
RPOP key |
从列表右侧弹出并返回元素 | RPOP alarm:log:GW001 → 获取最早告警日志 |
LRANGE key start end |
获取列表指定范围元素(0 = 第一个,-1 = 最后一个) | LRANGE alarm:log:GW001 0 -1 → 所有告警日志 |
LLEN key |
获取列表长度 | LLEN alarm:log:GW001 → 告警日志条数 |
5. 集合(Set)命令
Set 用于存储不重复的元素,支持交集、并集、差集等集合运算(如存储设备关联的用户 ID、设备标签)。
| 命令 | 作用 | 示例 |
|---|---|---|
SADD key member... |
向集合添加元素(返回新增元素数量,重复元素会忽略) | SADD device:tag:GW001 "温湿度" "养殖舍" |
SMEMBERS key |
获取集合所有元素 | SMEMBERS device:tag:GW001 → ["温湿度","养殖舍"] |
SISMEMBER key member |
判断元素是否在集合中(1 = 是,0 = 否) | SISMEMBER device:tag:GW001 "温湿度" → 1 |
SREM key member... |
从集合删除指定元素 | SREM device:tag:GW001 "养殖舍" |
SINTER key1 key2 |
获取多个集合的交集(共同元素) | SINTER device:tag:GW001 device:tag:GW002 |
SUNION key1 key2 |
获取多个集合的并集(所有元素) | SUNION device:tag:GW001 device:tag:GW002 |
6. 有序集合(ZSet)命令
ZSet 是 Set 的升级版,每个元素关联一个分数(score),按分数排序(如设备告警优先级、排行榜)。
| 命令 | 作用 | 示例 |
|---|---|---|
ZADD key score member... |
向有序集合添加元素(分数为数字,重复元素会更新分数) | ZADD alarm:priority 10 "GW001:03 离线" 5 "GW001:03 低电" |
ZRANGE key start end [WITHSCORES] |
按分数升序获取指定范围元素(加 WITHSCORES 显示分数) | ZRANGE alarm:priority 0 -1 WITHSCORES → 按优先级升序 |
ZREVRANGE key start end [WITHSCORES] |
按分数降序获取指定范围元素 | ZREVRANGE alarm:priority 0 -1 → 高优先级在前 |
ZSCORE key member |
获取元素的分数 | ZSCORE alarm:priority "GW001:03 离线" → 10 |
ZREM key member... |
删除有序集合中的元素 | ZREM alarm:priority "GW001:03 离线" |
三、生产环境注意事项
- 避免使用
KEYS *:KEYS命令会遍历所有 Key,Redis 是单线程,会阻塞其他操作,生产环境可用SCAN命令(分批遍历)替代; - 设置过期时间 :所有缓存 Key 建议设置过期时间(如你代码中的
DEVICE_STATUS_EXPIRE_HOURS),避免 Redis 内存溢出; - 批量操作优先 :用
MSET/HMSET等批量命令替代多次单条命令,减少网络交互开销; - 命令原子性:Redis 单条命令是原子性的(执行过程不会被打断),适合做分布式锁、计数器等场景。
java中使用redis
1、引入依赖
bash
<!-- Spring Boot Redis 起步依赖,自动整合 RedisTemplate -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
核心作用:自动引入 Redis 客户端(Lettuce 作为默认客户端)、Spring 封装的 RedisTemplate/StringRedisTemplate,无需手动管理依赖版本。
2、配置 Redis 连接(application.yml)
java
spring:
data:
redis:
host: localhost
port: 6379
database: 0
password: 1234
3、配置序列化器(可选但强烈推荐)
Spring Boot 自带的 RedisTemplate 默认使用 JDK 序列化,会导致 Redis 中存储的 Key/Value 乱码(如出现 \xAC\xED\x00\x05t\x00\x0Edevice:status:GW001:03),因此需要自定义序列化配置:
java
/**
* Redis 序列化配置类
*
* 【类核心信息】
* 1. 作用:自定义 RedisTemplate 的序列化规则,替换默认 JDK 序列化,使用 JSON 序列化保证数据可读性和对象兼容性
* 2. 依赖:Spring Data Redis 核心组件(RedisConnectionFactory)、Jackson 序列化组件
* 3. 核心产出:配置好序列化规则的 RedisTemplate<String, Object> Bean,供业务层自动注入使用
* 4. 适用场景:存储自定义 Java 对象(如 DeviceStatusCacheDTO、List<AlarmRecordVO>)到 Redis,避免序列化乱码/失败
*/
@Configuration
public class RedisConfig {
// 核心 Bean 配置方法(见下文拆解)
}
redisTemplate 方法 :
设计目标:构建支持 JSON 序列化的 RedisTemplate,适配自定义 Java 对象的存储/读取,解决默认序列化乱码问题。
1、参数:RedisConnectionFactory connectionFactory ( 必传)
Redis 连接工厂(Spring 自动注入,封装 Redis 连接信息)
2、返回值:
RedisTemplate<String, Object> 配置完成的 Redis 操作模板,可直接注入到 Service 层使用
java
1、初始化 RedisTemplate 并绑定连接工厂(保证模板能连接到 Redis 服务器)
2、配置 Jackson 的 ObjectMapper
3、创建序列化器并绑定到 ObjectMapper
4、为 RedisTemplate 设置序列化规则
5、初始化模板并返回
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
// 原函数逻辑(省略)
}
4、注入并使用 RedisTemplate
java
@Service
public class RedisService {
// 通用对象模板(支持存储自定义对象,如 DeviceStatusCacheDTO)
@Autowired
private RedisTemplate<String, Object> redisTemplate;
// 字符串专用模板(仅存储 String 类型,无需序列化)
@Autowired
private StringRedisTemplate stringRedisTemplate;
}
Spring Boot 中 Redis 核心 API 详解
| 方法 | 对应 Redis 数据类型 | 核心作用 |
|---|---|---|
opsForValue() |
String(字符串) | 操作单个 Key-Value,支持过期时间 |
opsForHash() |
Hash(哈希) | 操作 Hash 类型的 Key - 字段 - 值 |
opsForList() |
List(列表) | 操作有序列表(LPUSH/RPUSH/LPOP 等) |
opsForSet() |
Set(集合) | 操作无序不重复集合 |
opsForZSet() |
ZSet(有序集合) | 操作带分数的有序集合 |
keys(pattern) |
通用命令 | 按通配符匹配 Key |
delete(key) |
通用命令 | 删除指定 Key |
opsForValue() 是你代码中使用最多的 API,对应 Redis 的 SET/GET/EXPIRE 等命令:
序列化器介绍
一、什么是序列化器?
1、序列化器(Serializer)是将 "内存中的 Java 对象" 和 "字节流 / 字符串" 相互转换的工具: 序列化:把内存中的 Java 对象(如 DeviceStatusCacheDTO)转换成可存储(Redis / 文件)、可传输(网络)的字节流 / 字符串; 反序列化:把存储 / 传输的字节流 / 字符串还原为内存中的 Java 对象。
2、为什么 Redis 需要序列化器? Redis 是键值数据库,只能存储字节序列(二进制数据),无法直接存储 Java 对象。因此 RedisTemplate 操作 Redis 时,必须通过序列化器: 存储对象:Java 对象 → 序列化器 → 字节流 → 存入 Redis; 获取对象:Redis 字节流 → 序列化器 → Java 对象 → 返回给业务代码。
二、JDK 序列化器 vs Jackson JSON 序列化器
对于jdk序列化起的乱码原因:
1、 Key 乱码的原因
JDK 序列化器对 Key(String 类型)的序列化逻辑: 1)先将 String 作为一个 Java 对象序列化,会在字节 流开头添加 序列化魔数(\xAC\xED)、版本号(00\x05)、类型标识(t 表示字符串)等额外二进制数据; 2)最终 Redis 中看到的 Key 是 "二进制前缀 + 实际字符串字节",用 Redis 客户端查看时,二进制前缀会显示为乱码(如 ��tdevice:GW001:03)。
2、 Value 乱码的原因
-
JDK 序列化 Value 时,会把整个 Java 对象(包括类的全限定名、字段类型、访问修饰符、序列化版本号等元数据)转换成二进制字节流;
-
这些二进制字节流在 Redis 客户端中会被解析为乱码字符(如
�sr�com.xxx.DeviceStatusCacheDTO����),人类无法识别,也无法手动排查问题。
举个实际例子 存储 DeviceStatusCacheDTO(deviceAddr=GW001,onlineStatus=0): 1)JDK 序列化后 Redis 中的 Value:\xAC\xED\x00\x05sr\x00&com.xxx.DeviceStatusCacheDTO\x00\x00\x00\x00\x00\x00\x00\x01\x02\x00\x02L\x00\ndeviceAddrt\x00\x12Ljava/lang/String;L\x00\nonlineStatusI\x00\x00\x00\x00xp\x00\x00\x00\x00t\x00\x04GW001(完全不可读); 2)Jackson 序列化后 Redis 中的 Value:{"@class":"com.xxx.DeviceStatusCacheDTO","deviceAddr":"GW001","onlineStatus":0}(明文可直接看懂)。 】
三、总结:
1、Redis 存储的所有数据最终都是二进制字节流(包括字符串、数字),这是 Redis 的底层存储特性;
2、Redis 本身确实只存储二进制字节流,但 "乱码" 的核心不是 "二进制本身",而是 JDK 序列化的二进制包含大量 "Java 专属的元数据",这些元数据对 Redis 客户端(按 "通用字符串" 解析)来说是无意义的二进制垃圾,所以显示为乱码。
JDK 序列化的二进制 = Java 类元数据(魔数、版本号、类名、字段类型) + 实际数据字节 3、
bash
Jackson 序列化 → 把 Java 对象转成 JSON 字符串(表现层,人类可读)
↓
String 转二进制 → JSON 字符串按 UTF-8 编码转成二进制字节流(存储层,Redis 能存)
↓
Redis 存储 → 仅存储这个"纯数据的二进制字节流"
↓
反序列化 → 从 Redis 读取二进制字节流 → 按 UTF-8 转成 JSON 字符串 → Jackson 转成 Java 对象
| 序列化方式 | 业务层看到的形态 | 底层存储的形态 | Redis 客户端解析结果 |
|---|---|---|---|
| JDK 序列化器 | Java 对象 | 带 Java 元数据的二进制字节流 | 乱码(无法识别元数据二进制) |
| Jackson + String | JSON 字符串 | 纯 JSON 字符串的 UTF-8 二进制 | 明文 JSON(可读) |