Redis String 类型
1. String 类型概述
Section titled “1. String 类型概述”String 是 Redis 最基础、使用最广泛的数据类型。它是二进制安全的,可以存储任意内容:普通字符串、整数、浮点数、JSON 字符串,甚至图片的二进制数据(上限 512 MB)。
| 存储内容 | 示例 | 典型场景 |
|---|---|---|
| 普通字符串 | "Hello Redis" | Token、验证码 |
| 整数 | 100 | 计数器、库存 |
| 浮点数 | 3.14 | 商品价格 |
| JSON 字符串 | {"id":1,"name":"Alice"} | 缓存对象 |
| 二进制数据 | 图片字节 | 小文件存储(不推荐) |
2. 常用命令
Section titled “2. 常用命令”2.1. 基础读写
Section titled “2.1. 基础读写”# 写入SET key value # 设置 keySET key value EX 60 # 设置并指定过期时间(秒)SET key value PX 60000 # 设置并指定过期时间(毫秒)SET key value NX # key 不存在时才设置(分布式锁常用)SET key value XX # key 存在时才设置SET key value EX 60 NX # 组合使用
# 读取GET key # 获取值,不存在返回 nilMGET key1 key2 key3 # 批量获取
# 批量写入MSET key1 val1 key2 val2 # 批量设置
# 先获取再设置GETSET key newValue # 返回旧值并更新(已废弃,用 SET...GET 替代)SET key newValue GET # Redis 6.2+ 推荐写法2.2. 计数操作
Section titled “2.2. 计数操作”SET stock 100
INCR stock # 自增 1 → 101(原子操作)DECR stock # 自减 1 → 100INCRBY stock 50 # 自增 N → 150DECRBY stock 30 # 自减 N → 120INCRBYFLOAT price 0.5 # 浮点自增2.3. 字符串操作
Section titled “2.3. 字符串操作”APPEND key " World" # 追加内容,返回新长度STRLEN key # 获取字符串长度(字节数)GETRANGE key 0 4 # 截取子串(含两端)SETRANGE key 6 "Redis" # 从指定位置覆写2.4. 命令速查
Section titled “2.4. 命令速查”| 命令 | 说明 | 时间复杂度 |
|---|---|---|
SET key value | 设置值 | O(1) |
GET key | 获取值 | O(1) |
MSET / MGET | 批量读写 | O(N) |
INCR / DECR | 原子计数 | O(1) |
INCRBY / DECRBY | 步长计数 | O(1) |
SETNX | 不存在则设置 | O(1) |
SETEX | 设置并指定过期 | O(1) |
STRLEN | 字符串长度 | O(1) |
APPEND | 追加内容 | O(1) |
3. 底层编码
Section titled “3. 底层编码”Redis 会根据 String 的实际内容自动选择底层编码,以节省内存:
String 底层编码├── int → 值为整数且范围在 long 以内(如 "100")├── embstr → 字符串长度 ≤ 44 字节(一次内存分配,只读)└── raw → 字符串长度 > 44 字节(两次内存分配,可修改)SET age 18OBJECT ENCODING age # → int
SET name "Alice"OBJECT ENCODING name # → embstr
SET bio "This is a very long string that exceeds 44 bytes limit..."OBJECT ENCODING bio # → raw4. SpringBoot 实战
Section titled “4. SpringBoot 实战”4.1. 基础配置
Section titled “4.1. 基础配置”spring: data: redis: host: localhost port: 6379 password: 123456@Configurationpublic class RedisConfig {
@Bean public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) { RedisTemplate<String, Object> template = new RedisTemplate<>(); template.setConnectionFactory(factory); // Key 使用 String 序列化 template.setKeySerializer(RedisSerializer.string()); // Value 使用 JSON 序列化 template.setValueSerializer(new Jackson2JsonRedisSerializer<>(Object.class)); template.setHashKeySerializer(RedisSerializer.string()); template.setHashValueSerializer(new Jackson2JsonRedisSerializer<>(Object.class)); template.afterPropertiesSet(); return template; }}4.2. 字符串操作
Section titled “4.2. 字符串操作”@Autowiredprivate StringRedisTemplate stringRedisTemplate;
// 基础读写ValueOperations<String, String> ops = stringRedisTemplate.opsForValue();
ops.set("token:user:1001", "abc123xyz"); // 永久存储ops.set("token:user:1001", "abc123xyz", 30, TimeUnit.MINUTES); // 30 分钟过期String token = ops.get("token:user:1001");
// 批量操作ops.multiSet(Map.of("k1", "v1", "k2", "v2"));List<String> values = ops.multiGet(List.of("k1", "k2"));
// 原子计数ops.set("article:view:1001", "0");ops.increment("article:view:1001"); // +1ops.increment("article:view:1001", 10L); // +104.3. 缓存对象(JSON 序列化)
Section titled “4.3. 缓存对象(JSON 序列化)”@Autowiredprivate RedisTemplate<String, Object> redisTemplate;
@Autowiredprivate ObjectMapper objectMapper;
// 存储对象(序列化为 JSON)public void cacheUser(User user) { String key = "user:" + user.getId(); String json = objectMapper.writeValueAsString(user); stringRedisTemplate.opsForValue().set(key, json, 1, TimeUnit.HOURS);}
// 读取对象(反序列化)public User getUser(Long userId) throws JsonProcessingException { String key = "user:" + userId; String json = stringRedisTemplate.opsForValue().get(key); if (json == null) return null; return objectMapper.readValue(json, User.class);}5. 典型业务场景
Section titled “5. 典型业务场景”5.1. 验证码存储
Section titled “5.1. 验证码存储”// 发送验证码:存入 Redis,5 分钟过期public void sendVerifyCode(String phone) { String code = String.valueOf((int)(Math.random() * 900000) + 100000); String key = "verify:code:" + phone; stringRedisTemplate.opsForValue().set(key, code, 5, TimeUnit.MINUTES); // ... 发送短信}
// 校验验证码public boolean verifyCode(String phone, String inputCode) { String key = "verify:code:" + phone; String code = stringRedisTemplate.opsForValue().get(key); if (inputCode.equals(code)) { stringRedisTemplate.delete(key); // 验证成功后删除,防止重复使用 return true; } return false;}5.2. 分布式 ID(自增序列)
Section titled “5.2. 分布式 ID(自增序列)”// 利用 INCR 的原子性生成全局唯一 IDpublic long generateId(String bizType) { String key = "id:seq:" + bizType + ":" + LocalDate.now().format(DateTimeFormatter.BASIC_ISO_DATE); Long id = stringRedisTemplate.opsForValue().increment(key); // 设置当天过期,第二天重新从 1 开始 stringRedisTemplate.expire(key, 1, TimeUnit.DAYS); return id;}// 调用:generateId("order") → 202603100000015.3. 接口幂等性(防重复提交)
Section titled “5.3. 接口幂等性(防重复提交)”// 利用 SET NX 保证同一请求只处理一次public boolean tryLock(String requestId) { String key = "idempotent:" + requestId; // NX:不存在才设置;EX:10 秒过期 Boolean success = stringRedisTemplate.opsForValue() .setIfAbsent(key, "1", 10, TimeUnit.SECONDS); return Boolean.TRUE.equals(success);}6. String vs Hash 存储对象
Section titled “6. String vs Hash 存储对象”缓存对象时,String(JSON)和 Hash 各有优缺点:
| 对比项 | String(JSON) | Hash |
|---|---|---|
| 存储方式 | 整个对象序列化为 JSON | 每个字段单独存储 |
| 读取整个对象 | 一次 GET,简单 | HGETALL,略复杂 |
| 更新单个字段 | 需要先 GET 再 SET 整体 | HSET 直接更新单字段 |
| 内存占用 | 相对较小(JSON 紧凑) | 字段少时有额外开销 |
| 序列化依赖 | 需要 JSON 库 | 无需序列化 |
| 推荐场景 | 对象整体读取为主 | 频繁更新单个字段 |