当前位置: 首页 > news >正文

如何在Java中整合Redis?

一、Redis 基础认知:为什么选择 Redis?

1.1 Redis 的核心特性

高性能

  • 内存存储机制:Redis 将数据完全存储在内存中,数据读写操作直接在内存中进行,避免了磁盘I/O的瓶颈。内存读写速度通常是磁盘的100倍以上(内存访问约100ns,SSD约100μs)。
  • 单线程模型:采用单线程处理命令请求,避免了多线程上下文切换和锁竞争的开销,同时通过I/O多路复用技术(如epoll)实现高并发处理。
  • 性能基准:在标准服务器配置(如8核CPU,16GB内存)下,Redis单节点可达到:
    • 读操作(QPS):10万-15万次/秒
    • 写操作(QPS):8万-12万次/秒
    • 特别优化的场景下甚至可达20万+ QPS

丰富的数据结构

  • String:最基本类型,最大512MB,常用于缓存简单数据或计数器
  • Hash:键值对集合,适合存储对象(如用户信息:user:1001 {name:"张三",age:28})
  • List:双向链表,可实现消息队列(LPUSH+BRPOP)或最新N条记录
  • Set:无序集合,支持交并差运算,适合标签系统
  • Sorted Set:带权重的有序集合,可用于排行榜(ZADD game:score 100 "player1")
  • Bitmap:位图操作,适合签到统计(SETBIT sign:202301 100 1)
  • HyperLogLog:基数统计,误差率0.81%,适合UV统计(PFADD uv:20230101 "user1")

持久化机制

  • RDB(快照)

    • 定时将内存数据生成二进制快照文件(dump.rdb)
    • 配置示例:save 900 1(900秒内至少1次修改触发保存)
    • 优点:文件紧凑,恢复速度快
    • 缺点:可能丢失最后一次快照后的数据
  • AOF(Append Only File)

    • 记录所有写操作命令(类似MySQL的binlog)
    • 提供三种同步策略:
      • always:每次写操作都同步
      • everysec:每秒同步(默认)
      • no:由操作系统决定
    • 优点:数据安全性高,最多丢失1秒数据
    • 缺点:文件较大,恢复速度较慢
  • 混合模式:Redis 4.0+支持RDB+AOF混合持久化,结合两者优势

高可用与分布式

  • 主从复制

    • 一个主节点(Master)可配置多个从节点(Slave)
    • 从节点异步复制主节点数据
    • 读写分离:写操作走主节点,读操作可分散到从节点
  • 哨兵模式(Sentinel)

    • 由2个以上哨兵节点监控Redis集群状态
    • 自动故障转移:主节点宕机时,自动将从节点提升为主节点
    • 提供配置中心和通知机制
  • Redis Cluster

    • 官方分布式解决方案(Redis 3.0+)
    • 数据分片存储:16384个哈希槽分配到不同节点
    • 节点间使用Gossip协议通信
    • 支持自动故障转移和请求重定向

多语言支持

  • Java:Jedis、Lettuce、Redisson
  • Python:redis-py
  • Go:go-redis
  • Node.js:ioredis
  • 所有客户端库均遵循Redis协议规范,支持连接池、管道等高级特性

1.2 Redis 在 Java 项目中的典型应用场景

缓存热点数据

  • 实现方式
    // Spring Cache示例
    @Cacheable(value="products", key="#productId")
    public Product getProduct(String productId) {return productDao.findById(productId); 
    }
    

  • 缓存策略
    • 缓存穿透:布隆过滤器或空值缓存
    • 缓存雪崩:随机过期时间
    • 缓存击穿:互斥锁更新

分布式锁

  • 原生实现

    // SETNX实现
    String lockKey = "order_lock_"+orderId;
    String clientId = UUID.randomUUID().toString();
    try {Boolean result = redisTemplate.opsForValue().setIfAbsent(lockKey, clientId, 30, TimeUnit.SECONDS);if(result) {// 执行业务逻辑} else {// 获取锁失败}
    } finally {// 使用Lua脚本保证原子性String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";redisTemplate.execute(new DefaultRedisScript<>(script, Long.class), Collections.singletonList(lockKey), clientId);
    }
    

  • Redisson实现

    RLock lock = redissonClient.getLock("order_lock_"+orderId);
    try {// 尝试加锁,最多等待100秒,上锁后30秒自动解锁if(lock.tryLock(100, 30, TimeUnit.SECONDS)) {// 执行业务逻辑}
    } finally {lock.unlock();
    }
    

计数器与限流

  • 计数器示例

    // 文章阅读量计数
    redisTemplate.opsForValue().increment("article:read:count:"+articleId);// 获取阅读量
    Long count = redisTemplate.opsForValue().increment("article:read:count:"+articleId, 0);
    

  • 限流实现(滑动窗口算法):

    public boolean isAllowed(String key, int maxCount, int period) {long now = System.currentTimeMillis();long windowStart = now - period * 1000;// 删除旧时间戳redisTemplate.opsForZSet().removeRangeByScore(key, 0, windowStart);// 获取当前请求数long count = redisTemplate.opsForZSet().zCard(key);if(count < maxCount) {// 添加当前请求redisTemplate.opsForZSet().add(key, String.valueOf(now), now);return true;}return false;
    }
    

消息队列

  • List实现简单队列

    // 生产者
    redisTemplate.opsForList().leftPush("queue:order", orderJson);// 消费者
    String orderJson = redisTemplate.opsForList().rightPop("queue:order", 30, TimeUnit.SECONDS);
    

  • Redis Stream实现(Redis 5.0+):

    // 创建消费者组
    redisTemplate.opsForStream().createGroup("order_stream", "order_group");// 生产者
    Map<String, String> message = new HashMap<>();
    message.put("orderId", "1001");
    message.put("amount", "99.9");
    redisTemplate.opsForStream().add("order_stream", message);// 消费者
    List<MapRecord<String, String, String>> records = redisTemplate.opsForStream().read(Consumer.from("order_group", "consumer1"),StreamReadOptions.empty().count(1).block(Duration.ofSeconds(1)),StreamOffset.create("order_stream", ReadOffset.lastConsumed()));
    

分布式Session

  • Spring Session集成

    <!-- pom.xml -->
    <dependency><groupId>org.springframework.session</groupId><artifactId>spring-session-data-redis</artifactId>
    </dependency>
    

    # application.properties
    spring.session.store-type=redis
    spring.session.timeout=1800 # 30分钟
    

    // 自动将Session存储到Redis
    @RestController
    @SessionAttributes("user")
    public class UserController {@PostMapping("/login")public String login(@RequestParam String username, Model model) {model.addAttribute("user", username);return "login success";}
    }
    

  • Session共享效果

    • 用户登录后,Session信息存储在Redis
    • 同一用户的请求可路由到集群中任意节点
    • 各服务通过统一Session ID获取用户状态
    • 默认使用Jackson序列化Session对象

二、环境搭建:Redis 服务与 Java 开发环境准备

2.1 Redis 服务端部署(以 Linux 为例)

步骤 1:下载并解压 Redis

下载 Redis(版本可根据需求选择,此处以 6.2.6 为例)
# 下载 Redis 源码包
wget https://download.redis.io/releases/redis-6.2.6.tar.gz# 验证下载完整性(可选)
wget https://download.redis.io/releases/redis-6.2.6.tar.gz.asc
gpg --verify redis-6.2.6.tar.gz.asc# 解压 Redis 源码包
tar -zxvf redis-6.2.6.tar.gz# 进入解压后的目录
cd redis-6.2.6

注意事项:
  • 建议使用稳定版本(stable)而非开发版本
  • 下载前可访问 https://download.redis.io/releases/ 查看最新版本
  • 对于生产环境,建议使用 Redis 6.x 及以上版本以支持多线程处理

步骤 2:编译与安装

# 安装编译依赖(CentOS/RHEL)
yum install -y gcc make tcl# 或 Ubuntu/Debian
apt-get install -y build-essential tcl# 编译 Redis(此过程可能需要几分钟)
make# 运行测试(可选,但推荐)
make test# 安装 Redis 到系统目录
make install

安装结果验证:
  • 默认安装路径:/usr/local/bin
  • 检查是否安装成功:
    ls -l /usr/local/bin/redis-*
    

步骤 3:配置并启动 Redis

基本配置管理:
# 创建 Redis 配置目录
mkdir -p /etc/redis# 复制配置文件模板
cp redis.conf /etc/redis/redis.conf# 创建数据存储目录
mkdir -p /var/lib/redis

关键配置文件修改(/etc/redis/redis.conf):
# 使用 vim 或其他编辑器修改配置文件
vim /etc/redis/redis.conf

需要修改的重要配置项:

  1. 网络配置

    # 允许远程访问(注释掉或修改为)
    # bind 127.0.0.1
    bind 0.0.0.0
    

  2. 安全配置

    # 关闭保护模式
    protected-mode no# 设置密码(生产环境必须)
    requirepass your_strong_password
    

  3. 运行模式

    # 以守护进程方式运行
    daemonize yes
    

  4. 日志配置

    # 指定日志文件路径
    logfile "/var/log/redis/redis-server.log"
    

  5. 数据持久化(根据需求配置):

    # RDB 持久化配置
    save 900 1
    save 300 10
    save 60 10000# AOF 持久化配置(可选)
    appendonly yes
    appendfsync everysec
    

启动 Redis 服务:
# 创建日志目录
mkdir -p /var/log/redis
chown -R redis:redis /var/log/redis# 启动 Redis 服务
redis-server /etc/redis/redis.conf# 验证服务状态
ps aux | grep redis
netstat -tulnp | grep 6379

连接测试:
# 本地连接测试
redis-cli -h 127.0.0.1 -p 6379 -a your_password ping# 远程连接测试(从其他服务器)
redis-cli -h your_server_ip -p 6379 -a your_password ping

设置开机自启(可选):
# 创建 systemd 服务文件
vim /etc/systemd/system/redis.service

添加以下内容:

[Unit]
Description=Redis In-Memory Data Store
After=network.target[Service]
User=redis
Group=redis
ExecStart=/usr/local/bin/redis-server /etc/redis/redis.conf
ExecStop=/usr/local/bin/redis-cli shutdown
Restart=always[Install]
WantedBy=multi-user.target

然后执行:

# 重新加载 systemd
systemctl daemon-reload# 启用开机自启
systemctl enable redis# 启动服务
systemctl start redis# 检查状态
systemctl status redis

2.2 Java 开发环境配置

客户端选择说明

Java 操作 Redis 主要有两种主流客户端:

  1. Jedis

    • 官方推荐的轻量级客户端
    • 直接操作 Redis 命令
    • 支持连接池
    • 适合简单场景和性能要求高的场景
  2. Redisson

    • 基于 Netty 实现
    • 提供分布式对象和服务
    • 内置分布式锁、原子操作等高级功能
    • 适合复杂分布式场景

2.2.1 Maven 依赖配置

Jedis 依赖配置(推荐使用连接池):
<!-- Jedis 核心依赖 -->
<dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId><version>4.3.1</version>
</dependency><!-- 连接池依赖(必须) -->
<dependency><groupId>org.apache.commons</groupId><artifactId>commons-pool2</artifactId><version>2.11.1</version>
</dependency><!-- 可选:JSON 序列化支持 -->
<dependency><groupId>com.google.code.gson</groupId><artifactId>gson</artifactId><version>2.9.0</version>
</dependency>

Redisson 依赖配置:
<!-- Redisson 核心依赖 -->
<dependency><groupId>org.redisson</groupId><artifactId>redisson</artifactId><version>3.17.6</version>
</dependency><!-- 可选:配置支持(如 JSON/YAML 配置文件) -->
<dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>2.13.3</version>
</dependency>

版本选择建议:
  • 生产环境建议使用稳定版本
  • 可访问 Maven 中央仓库(https://mvnrepository.com/)查询最新版本
  • 注意版本兼容性:
    • Jedis 4.x 支持 Redis 6.x
    • Redisson 3.x 支持 Redis 5.x+

开发环境验证

创建简单的测试类验证环境是否配置成功:

import redis.clients.jedis.Jedis;public class RedisTest {public static void main(String[] args) {// 创建连接Jedis jedis = new Jedis("localhost", 6379);jedis.auth("your_password");  // 如果有密码// 测试连接System.out.println("连接状态:" + jedis.ping());// 基本操作jedis.set("test_key", "Hello Redis");System.out.println("获取值:" + jedis.get("test_key"));// 关闭连接jedis.close();}
}

IDE 配置建议

  1. IntelliJ IDEA

    • 确保 Maven 项目正确导入依赖
    • 安装 Redis 插件(如 "Redis" 插件)方便调试
  2. Eclipse

    • 使用 M2Eclipse 插件管理依赖
    • 可安装 Redis 客户端插件(如 "RedisClipse")

生产环境注意事项

  1. 连接池配置

    • 必须使用连接池管理连接
    • 推荐配置参数:
      JedisPoolConfig poolConfig = new JedisPoolConfig();
      poolConfig.setMaxTotal(128);       // 最大连接数
      poolConfig.setMaxIdle(32);         // 最大空闲连接
      poolConfig.setMinIdle(8);          // 最小空闲连接
      poolConfig.setTestOnBorrow(true);  // 获取连接时测试
      

  2. 安全建议

    • 不要将密码硬编码在代码中
    • 使用配置中心或环境变量管理敏感信息
    • 启用 SSL/TLS 加密传输(Redis 6+支持)
  3. 性能优化

    • 批量操作使用 pipeline
    • 大 value 考虑分片存储
    • 合理选择序列化方式

三、Jedis 客户端:Java 操作 Redis 的基础实现

3.1 基本使用:单实例连接

步骤 1:创建 Jedis 实例并连接 Redis

import redis.clients.jedis.Jedis;public class JedisBasicDemo {public static void main(String[] args) {// 1. 创建Jedis实例(参数说明:Redis地址、端口、连接超时时间(毫秒)、密码)// 注意:如果Redis服务器在同一台机器,地址可以是"localhost"Jedis jedis = new Jedis("192.168.1.100", 6379, 5000); try {// 2. 验证密码(若Redis未设置密码,可跳过此步骤)// 如果密码错误,会抛出redis.clients.jedis.exceptions.JedisDataExceptionjedis.auth("your_password");// 3. 测试连接// ping()返回"PONG"表示连接正常System.out.println("Redis连接状态:" + jedis.ping()); // 期望输出:PONG// 4. 操作String类型数据jedis.set("username", "zhangsan"); // 存入数据,默认永不过期// 带过期时间的设置(单位秒)jedis.setex("temp_token", 3600, "abcd1234"); String username = jedis.get("username"); // 获取数据System.out.println("获取到的用户名:" + username); // 输出:zhangsan// 5. 操作Hash类型数据// 存储用户信息jedis.hset("user:1001", "name", "lisi");jedis.hset("user:1001", "age", "25");jedis.hset("user:1001", "gender", "male");// 获取Hash中所有字段和值(返回Map<String,String>)System.out.println("用户1001信息:" + jedis.hgetAll("user:1001"));// 输出:{name=lisi, age=25, gender=male}// 获取单个字段System.out.println("用户年龄:" + jedis.hget("user:1001", "age")); // 输出:25} catch (Exception e) {e.printStackTrace();} finally {// 6. 关闭连接(重要!避免连接泄漏)if (jedis != null) {jedis.close();}}}
}

注意事项

  1. 线程安全性

    • Jedis实例是非线程安全的,每个线程应该使用自己的Jedis实例
    • 在高并发场景中直接使用单例连接会导致线程安全问题
  2. 性能考量

    • 每次操作都需要创建和关闭TCP连接,频繁操作时会产生较大性能开销
    • 实测表明,在100次连续操作中,单实例连接方式耗时是连接池方式的10倍以上
  3. 实际应用建议

    • 开发环境或简单脚本可以使用单实例连接
    • 生产环境必须使用连接池(JedisPool)管理连接
    • 对于长时间运行的任务,建议使用try-with-resources确保连接关闭
  4. 异常处理

    • 网络中断时可能抛出JedisConnectionException
    • 认证失败抛出JedisDataException
    • 建议对关键操作添加重试机制

3.2 进阶使用:Jedis 连接池

步骤 1:配置 Jedis 连接池

import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;public class JedisPoolDemo {// 初始化Jedis连接池(使用静态代码块确保只初始化一次)private static JedisPool jedisPool;static {// 1. 配置连接池参数GenericObjectPoolConfig<Jedis> poolConfig = new GenericObjectPoolConfig<>();// 关键参数配置(根据实际业务需求调整)poolConfig.setMaxTotal(100);       // 最大连接数(根据Redis服务器配置调整)poolConfig.setMaxIdle(20);         // 最大空闲连接数(建议设为MaxTotal的20%-50%)poolConfig.setMinIdle(5);          // 最小空闲连接数(保持一定数量的热连接)poolConfig.setBlockWhenExhausted(true); // 连接耗尽时是否阻塞等待(建议true)poolConfig.setMaxWaitMillis(3000); // 获取连接最大等待时间(毫秒)poolConfig.setTestOnBorrow(true);  // 获取连接时是否测试有效性(建议true,但会有性能损耗)poolConfig.setTestOnReturn(false); // 归还连接时是否测试(建议false)poolConfig.setTestWhileIdle(true); // 空闲时是否测试(建议true)// 2. 初始化连接池String redisHost = "192.168.1.100";int redisPort = 6379;int timeout = 5000; // 连接超时时间(毫秒)String password = "your_password";jedisPool = new JedisPool(poolConfig, redisHost, redisPort, timeout, password);}// 从连接池获取Jedis实例public static Jedis getJedis() {return jedisPool.getResource();}// 测试连接池使用public static void main(String[] args) {// 使用try-with-resources语法自动管理连接try (Jedis jedis = getJedis()) {// 1. 操作String类型jedis.set("product:1001", "iPhone 13");System.out.println("商品1001名称:" + jedis.get("product:1001"));// 2. 操作List类型(模拟消息队列)jedis.lpush("message:queue", "msg1", "msg2", "msg3"); // 左推(入队)// 右拉(出队),0表示无限期阻塞等待String msg = jedis.brpop(0, "message:queue").get(1); System.out.println("消费的消息:" + msg);// 3. 批量操作(使用pipeline提升性能)long start = System.currentTimeMillis();for (int i = 0; i < 1000; i++) {jedis.set("key_" + i, "value_" + i);}long end = System.currentTimeMillis();System.out.println("普通操作耗时:" + (end - start) + "ms");} catch (Exception e) {e.printStackTrace();}}
}

核心优势

  1. 性能优化

    • 连接复用减少TCP三次握手开销
    • 实测在1000次操作中,连接池方式比单实例快8-10倍
    • 支持Pipeline批量操作,进一步提升吞吐量
  2. 资源管理

    • 自动维护连接池中的活跃连接数
    • 可配置连接最大存活时间(maxAge)避免长时间使用的连接出现问题
    • 通过evictor线程定期检测和清理无效连接
  3. 生产环境配置建议

    // 典型生产环境配置
    poolConfig.setMaxTotal(200);       // 根据QPS估算,一般(平均QPS*平均耗时(ms))/1000
    poolConfig.setMaxIdle(50);         // 根据业务波动情况调整
    poolConfig.setMinIdle(10);         // 保持一定热连接
    poolConfig.setMaxWaitMillis(1000); // 根据系统容忍度设置
    poolConfig.setTestOnBorrow(true);  // 确保获取的连接可用
    poolConfig.setTimeBetweenEvictionRunsMillis(30000); // 30秒检测一次
    

  4. 监控指标

    • 可通过JMX监控连接池状态
    • 关键指标:活动连接数、空闲连接数、等待获取连接的线程数等
  5. 最佳实践

    • 每个应用使用独立的连接池
    • 根据业务类型(读写比例、命令复杂度)配置不同连接池
    • 定期监控连接池状态,及时调整参数

3.3 Jedis 操作核心数据结构

(1)Set 类型(无序、唯一)

try (Jedis jedis = JedisPoolDemo.getJedis()) {// 1. 添加元素(自动去重)jedis.sadd("tags:java", "spring", "mybatis", "redis");jedis.sadd("tags:java", "spring"); // 重复元素不会被添加// 2. 获取所有元素(无序)System.out.println("Java相关标签:" + jedis.smembers("tags:java")); // 输出可能是:[redis, spring, mybatis]// 3. 判断元素是否存在System.out.println("是否包含spring:" + jedis.sismember("tags:java", "spring")); // 输出:true// 4. 移除元素jedis.srem("tags:java", "mybatis");// 5. 计算集合大小System.out.println("标签数量:" + jedis.scard("tags:java")); // 输出:2// 6. 集合运算jedis.sadd("tags:backend", "spring", "django", "redis");// 交集System.out.println("交集:" + jedis.sinter("tags:java", "tags:backend"));// 输出:[spring, redis]// 并集System.out.println("并集:" + jedis.sunion("tags:java", "tags:backend"));// 输出:[spring, redis, django]// 差集(tags:java有而tags:backend没有的)System.out.println("差集:" + jedis.sdiff("tags:java", "tags:backend"));// 输出:[]
}

(2)Sorted Set 类型(有序、唯一,基于分数排序)

try (Jedis jedis = JedisPoolDemo.getJedis()) {// 1. 添加元素(如果成员已存在,则更新分数)jedis.zadd("rank:java", 95, "zhangsan");jedis.zadd("rank:java", 88, "lisi");jedis.zadd("rank:java", 92, "wangwu");jedis.zadd("rank:java", 90, "lisi"); // 更新lisi的分数// 2. 按分数升序获取(0到-1表示全部)System.out.println("升序排名:" + jedis.zrange("rank:java", 0, -1));// 输出:[lisi, wangwu, zhangsan]// 3. 按分数降序获取(带分数)Set<Tuple> results = jedis.zrevrangeWithScores("rank:java", 0, -1);results.forEach(tuple -> System.out.println(tuple.getElement() + ":" + tuple.getScore()));// 输出:// zhangsan:95.0// wangwu:92.0// lisi:90.0// 4. 获取分数System.out.println("lisi的分数:" + jedis.zscore("rank:java", "lisi"));// 输出:90.0// 5. 获取排名(从0开始)System.out.println("zhangsan的降序排名:" + jedis.zrevrank("rank:java", "zhangsan"));// 输出:0// 6. 范围查询(80<=score<=90)System.out.println("80-90分的学生:" + jedis.zrangeByScoreWithScores("rank:java", 80, 90));// 输出:[lisi:90.0, wangwu:92.0](注意包含边界)// 7. 原子操作:增加分数jedis.zincrby("rank:java", 5, "lisi"); // lisi分数+5System.out.println("新分数:" + jedis.zscore("rank:java", "lisi"));// 输出:95.0
}

(3)其他数据结构操作

// Geo地理位置操作
try (Jedis jedis = JedisPoolDemo.getJedis()) {// 添加地理位置(经度、纬度、名称)jedis.geoadd("cities", 116.404, 39.915, "beijing");jedis.geoadd("cities", 121.474, 31.230, "shanghai");// 计算两地距离(单位:km)Double dist = jedis.geodist("cities", "beijing", "shanghai", GeoUnit.KM);System.out.println("北京到上海距离:" + dist + "km");// 查找附近位置List<GeoRadiusResponse> nearby = jedis.georadiusByMember("cities", "beijing", 1000, GeoUnit.KM);nearby.forEach(r -> System.out.println(r.getMemberByString()));
}// HyperLogLog基数统计
try (Jedis jedis = JedisPoolDemo.getJedis()) {// 添加元素(用于统计不重复元素数量)for (int i = 0; i < 10000; i++) {jedis.pfadd("visitors", "user_" + i);jedis.pfadd("visitors", "user_" + (i % 5000)); // 50%重复}// 获取基数估计值System.out.println("独立访客数:" + jedis.pfcount("visitors"));// 输出约7500(误差率约0.81%)
}

四、Redisson 客户端:Java 操作 Redis 的高级实现

Redisson是基于Jedis的封装,不仅提供了更简洁易用的API,还实现了大量Redis官方推荐的分布式特性。相比原生Jedis,Redisson具有以下核心优势:

  1. 线程安全:内置连接池管理,彻底解决Jedis线程安全问题
  2. 丰富特性:支持分布式锁(可重入锁、公平锁、联锁等)、分布式集合(Set、Map、List)、延迟队列、布隆过滤器等20+种分布式对象
  3. 协议支持:完整支持Redis 5.0+的所有命令和数据类型
  4. 容错机制:自动重连、故障转移、读写分离等企业级特性

4.1 Redisson客户端初始化

Redisson支持多种Redis部署模式,每种模式的配置方式如下:

单点模式配置示例

import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;/*** Redisson客户端工厂类(推荐单例模式)* 典型应用场景:Spring Bean管理、静态工具类等*/
public class RedissonClientFactory {// 使用volatile保证多线程可见性private static volatile RedissonClient redissonClient;// 双重检查锁实现线程安全初始化public static RedissonClient getRedissonClient() {if (redissonClient == null) {synchronized (RedissonClientFactory.class) {if (redissonClient == null) {// 1. 创建配置构建器Config config = new Config();// 2. 配置单点模式(生产环境建议使用YAML/JSON配置文件)config.useSingleServer().setAddress("redis://192.168.1.100:6379") // 支持rediss://协议(SSL加密).setPassword("your_password")             // 密码特殊字符需URL编码.setDatabase(0)                           // 默认DB索引.setConnectionPoolSize(100)               // 最大连接数(默认64).setConnectionMinimumIdleSize(10)         // 最小空闲连接(默认10).setConnectTimeout(5000)                  // 连接超时(毫秒).setIdleConnectionTimeout(30000)          // 空闲连接超时.setRetryAttempts(3)                      // 命令重试次数.setRetryInterval(1000);                  // 重试间隔// 3. 创建客户端实例(实际会启动连接池和定时任务)redissonClient = Redisson.create(config);}}}return redissonClient;}// 优雅关闭方法public static void shutdown() {if (redissonClient != null) {redissonClient.shutdown();}}
}

其他部署模式配置示例

哨兵模式
config.useSentinelServers().addSentinelAddress("redis://sentinel1:26379").addSentinelAddress("redis://sentinel2:26379").setMasterName("mymaster");

集群模式
config.useClusterServers().addNodeAddress("redis://node1:6379").addNodeAddress("redis://node2:6379").setScanInterval(2000); // 集群状态扫描间隔

最佳实践建议

  1. 连接池配置:根据QPS调整连接数(建议公式:最大连接数 = QPS × 平均响应时间(秒))
  2. 超时设置:网络不稳定的环境适当增大超时时间
  3. 资源释放:应用关闭时调用shutdown()释放资源
  4. 配置分离:生产环境建议使用外部配置文件(JSON/YAML格式)

4.2 Redisson 操作核心数据结构

Redisson 是一个基于 Redis 的 Java 客户端,它通过 RedissonClient 提供了对应 Redis 数据结构的封装类,使得操作 Redis 更加面向对象和简洁。Redisson 支持 Redis 的所有核心数据结构,包括 String、Hash、List、Set 和 Sorted Set 等,每种数据结构都有对应的 Java 接口实现。

(1)String 类型(RBucket)

RBucket 是 Redisson 对 Redis String 类型的封装,提供了一系列操作字符串的方法。RBucket 支持设置过期时间,适用于缓存场景。

import org.redisson.api.RBucket;
import org.redisson.api.RedissonClient;public class RedissonStringDemo {public static void main(String[] args) {// 获取 RedissonClient 实例RedissonClient redissonClient = RedissonClientFactory.getRedissonClient();try {// 获取 RBucket 对象(对应 Redis 的 String 类型)RBucket<String> usernameBucket = redissonClient.getBucket("username");// 存入数据,并设置过期时间为 3600 秒(1小时)usernameBucket.set("zhangsan", 3600);// 获取数据String username = usernameBucket.get();System.out.println("用户名:" + username); // 输出:zhangsan// 判断 key 是否存在System.out.println("是否存在:" + usernameBucket.isExists()); // 输出:true// 删除数据usernameBucket.delete();System.out.println("删除后是否存在:" + usernameBucket.isExists()); // 输出:false} finally {// 关闭客户端(若为长期运行的服务,无需频繁关闭)redissonClient.shutdown();}}
}

(2)Hash 类型(RMap)

RMap 是 Redisson 对 Redis Hash 类型的封装,提供类似 Java Map 的操作方法,适用于存储对象或结构化数据。

import org.redisson.api.RMap;
import org.redisson.api.RedissonClient;public class RedissonHashDemo {public static void main(String[] args) {RedissonClient redissonClient = RedissonClientFactory.getRedissonClient();try {// 获取 RMap 对象(对应 Redis 的 Hash 类型)RMap<String, String> userMap = redissonClient.getMap("user:1001");// 存入数据userMap.put("name", "lisi");userMap.put("age", "25");userMap.put("gender", "male");// 获取数据System.out.println("用户姓名:" + userMap.get("name")); // 输出:lisiSystem.out.println("用户所有信息:" + userMap); // 输出:{name=lisi, age=25, gender=male}// 删除字段userMap.remove("gender");System.out.println("删除 gender 后的信息:" + userMap); // 输出:{name=lisi, age=25}// 判断字段是否存在System.out.println("是否包含 age:" + userMap.containsKey("age")); // 输出:true} finally {// 关闭客户端redissonClient.shutdown();}}
}

(3)List 类型(RList)

RList 是 Redisson 对 Redis List 类型的封装,支持双向添加、删除和获取元素,适用于队列或栈的场景。

import org.redisson.api.RList;
import org.redisson.api.RedissonClient;public class RedissonListDemo {public static void main(String[] args) {RedissonClient redissonClient = RedissonClientFactory.getRedissonClient();try {// 获取 RList 对象(对应 Redis 的 List 类型)RList<String> messageList = redissonClient.getList("message:queue");// 添加元素(右添加,等同于 RPUSH)messageList.add("msg1");// 左添加(等同于 LPUSH)messageList.add(0, "msg0");// 获取指定索引元素System.out.println("索引 0 的元素:" + messageList.get(0)); // 输出:msg0// 获取所有元素System.out.println("所有消息:" + messageList); // 输出:[msg0, msg1]// 移除并返回最后一个元素(等同于 RPOP)String lastMsg = messageList.remove(messageList.size() - 1);System.out.println("移除的最后一条消息:" + lastMsg); // 输出:msg1// 获取列表长度System.out.println("列表长度:" + messageList.size()); // 输出:1} finally {redissonClient.shutdown();}}
}

(4)Set 类型(RSet)

RSet 是 Redisson 对 Redis Set 类型的封装,支持元素的添加、删除和集合运算,适用于标签或唯一值存储场景。

import org.redisson.api.RSet;
import org.redisson.api.RedissonClient;public class RedissonSetDemo {public static void main(String[] args) {RedissonClient redissonClient = RedissonClientFactory.getRedissonClient();try {// 获取 RSet 对象(对应 Redis 的 Set 类型)RSet<String> tagSet = redissonClient.getSet("tags:java");// 添加元素tagSet.add("spring");tagSet.add("mybatis");tagSet.add("redis");// 判断元素是否存在System.out.println("是否包含 spring:" + tagSet.contains("spring")); // 输出:true// 获取所有元素System.out.println("Java 标签集合:" + tagSet); // 输出:[spring, mybatis, redis]// 创建另一个 SetRSet<String> backendTagSet = redissonClient.getSet("tags:backend");backendTagSet.add("spring");backendTagSet.add("django");backendTagSet.add("redis");// 计算并返回交集System.out.println("Java 与 Backend 标签交集:" + tagSet.retainAll(backendTagSet));// 输出:[spring, redis](retainAll 方法会保留交集元素并返回)// 获取集合大小System.out.println("交集大小:" + tagSet.size()); // 输出:2} finally {redissonClient.shutdown();}}
}

(5)Sorted Set 类型(RSortedSet)

RSortedSet 是 Redisson 对 Redis Sorted Set 类型的封装,支持按分数排序和范围查询,适用于排行榜或优先级队列场景。

import org.redisson.api.RSortedSet;
import org.redisson.api.RedissonClient;
import org.redisson.api.ScoredEntry;
import java.util.Collection;public class RedissonSortedSetDemo {public static void main(String[] args) {RedissonClient redissonClient = RedissonClientFactory.getRedissonClient();try {// 获取 RSortedSet 对象(对应 Redis 的 Sorted Set 类型)RSortedSet<String> rankSet = redissonClient.getSortedSet("rank:java");// 添加元素(指定分数)rankSet.add(95, "zhangsan");rankSet.add(88, "lisi");rankSet.add(92, "wangwu");// 按分数升序获取元素System.out.println("成绩升序排名:" + rankSet); // 输出:[lisi, wangwu, zhangsan]// 按分数降序获取元素(含分数)Collection<ScoredEntry<String>> scoredEntries = rankSet.entryRangeReversed(0, -1);for (ScoredEntry<String> entry : scoredEntries) {System.out.println(entry.getValue() + ":" + entry.getScore());// 输出:// zhangsan:95.0// wangwu:92.0// lisi:88.0}// 获取指定元素的排名(降序)System.out.println("zhangsan 的排名:" + rankSet.revRank("zhangsan")); // 输出:0// 获取分数范围内的元素Collection<String> range = rankSet.valueRange(90, true, 95, true);System.out.println("分数在 90~95 之间的学生:" + range); // 输出:[wangwu, zhangsan]} finally {redissonClient.shutdown();}}
}

4.3 Redisson 高级特性:分布式锁与延迟队列

4.3.1 分布式锁(RLock)

在分布式系统中,多个服务实例可能同时操作同一资源(如订单创建、库存扣减等场景),分布式锁可避免并发问题,保证数据一致性。Redisson 的RLock实现了与Java的ReentrantLock类似的可重入锁特性,且支持自动过期释放机制,无需手动处理死锁问题。

核心特性
  1. 可重入性:同一线程可以多次获取同一把锁
  2. 自动续期:通过watchdog机制自动延长锁持有时间
  3. 公平锁支持:可配置为公平锁模式
  4. 锁等待超时:避免线程无限期等待
  5. 锁释放保障:通过finally块确保锁释放
典型应用场景
  • 秒杀系统中的库存扣减
  • 支付系统中的订单状态变更
  • 分布式定时任务调度
  • 重要数据的并发修改
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import java.util.concurrent.TimeUnit;public class RedissonDistributedLockDemo {// 锁的key命名规范:业务类型:业务ID(如订单创建锁)private static final String LOCK_KEY = "lock:order:create"; public static void createOrder(RedissonClient redissonClient) {// 1. 获取分布式锁实例(非阻塞)RLock lock = redissonClient.getLock(LOCK_KEY);try {// 2. 尝试获取锁(参数说明)// - waitTime: 获取锁的最大等待时间(建议设置短于业务超时时间)// - leaseTime: 锁自动释放时间(业务完成前应足够长)// - TimeUnit: 时间单位boolean isLocked = lock.tryLock(3, 30, TimeUnit.SECONDS);if (isLocked) {try {// 3. 加锁成功,执行业务逻辑System.out.println(Thread.currentThread().getName() + "获取分布式锁成功,开始创建订单...");// 模拟业务处理耗时(如数据库操作、远程调用等)Thread.sleep(5000);System.out.println("订单创建完成!");} finally {// 4. 确保业务完成后再释放锁if (lock.isHeldByCurrentThread()) {lock.unlock();System.out.println("分布式锁已释放!");}}} else {// 5. 获取锁失败处理(可记录日志、返回友好提示或重试)System.out.println("获取分布式锁失败,请稍后再试!");// 可抛出特定异常或返回错误码}} catch (InterruptedException e) {Thread.currentThread().interrupt();System.err.println("线程被中断:" + e.getMessage());} catch (Exception e) {System.err.println("业务处理异常:" + e.getMessage());}}public static void main(String[] args) {RedissonClient redissonClient = RedissonClientFactory.getRedissonClient();// 模拟3个并发线程尝试获取锁for (int i = 0; i < 3; i++) {new Thread(() -> createOrder(redissonClient), "Thread-"+i).start();}// 注意:实际应用中RedissonClient应该是单例长期存在// redissonClient.shutdown();}
}

最佳实践
  1. 锁命名规范:使用业务相关的有意义的名称,如lock:order:{orderId}
  2. 锁超时设置:leaseTime应大于预估的业务处理时间
  3. 异常处理:确保在finally块中释放锁
  4. 避免长时间持有锁:锁持有时间应尽量短
  5. 重试机制:获取锁失败时应有合理的重试策略

4.3.2 延迟队列(RDelayedQueue)

延迟队列可实现"消息延迟一段时间后再被消费"的场景,如订单超时未支付自动取消、定时任务触发、预约提醒等。Redisson的RDelayedQueue基于Redis的Sorted Set实现,提供了简单易用的API,避免了自行实现延迟队列的复杂性。

核心特性
  1. 精确延时:支持毫秒级延迟精度
  2. 持久化存储:消息不会因服务重启而丢失
  3. 高可用:基于Redis集群保证可靠性
  4. 多消费者支持:多个服务实例可以同时消费
  5. 消息顺序保障:按延迟时间顺序消费消息
典型应用场景
  • 电商订单超时自动取消(30分钟未支付)
  • 会议开始前15分钟提醒
  • 延时任务调度
  • 红包24小时未领取自动退回
import org.redisson.api.RBlockingQueue;
import org.redisson.api.RDelayedQueue;
import org.redisson.api.RedissonClient;
import java.util.concurrent.TimeUnit;public class RedissonDelayedQueueDemo {// 延迟队列命名规范:业务类型:业务目的private static final String DELAY_QUEUE_KEY = "delay:queue:order"; private static final String BLOCKING_QUEUE_KEY = "blocking:queue:order";/*** 生产者:添加延迟消息* @param redissonClient Redisson客户端* @param message 消息内容(建议包含业务ID)* @param delayTime 延迟时间(单位:秒)*/public static void produceDelayMessage(RedissonClient redissonClient, String message, long delayTime) {// 1. 获取阻塞队列(底层实际存储结构)RBlockingQueue<String> blockingQueue = redissonClient.getBlockingQueue(BLOCKING_QUEUE_KEY);// 2. 创建延迟队列(与阻塞队列关联)try (RDelayedQueue<String> delayedQueue = redissonClient.getDelayedQueue(blockingQueue)) {// 3. 添加延迟消息delayedQueue.offer(message, delayTime, TimeUnit.SECONDS);System.out.println("[" + System.currentTimeMillis() + "] 添加延迟消息:" + message + ",延迟时间:" + delayTime + "秒");} // 自动关闭延迟队列(Java 7+ try-with-resources)}/*** 消费者:持续消费延迟消息* @param redissonClient Redisson客户端*/public static void consumeDelayMessage(RedissonClient redissonClient) {// 1. 获取阻塞队列(与生产者使用的相同)RBlockingQueue<String> blockingQueue = redissonClient.getBlockingQueue(BLOCKING_QUEUE_KEY);System.out.println("消费者启动,等待消费延迟消息...");while (!Thread.currentThread().isInterrupted()) {try {// 2. 阻塞获取消息(当有消息到期时自动唤醒)String message = blockingQueue.take();// 3. 处理消息long currentTime = System.currentTimeMillis();System.out.println("[" + currentTime + "] 消费延迟消息:" + message);// 根据消息内容处理业务if (message != null && message.startsWith("order:")) {String[] parts = message.split(":");if (parts.length >= 2) {String orderId = parts[1];System.out.println("处理订单超时:订单" + orderId + "超时未支付,已自动取消!");// 实际业务中此处会调用订单服务取消订单}}} catch (InterruptedException e) {Thread.currentThread().interrupt();System.err.println("消费者线程被中断");break;} catch (Exception e) {System.err.println("消费消息异常:" + e.getMessage());// 可添加重试或错误处理逻辑}}}public static void main(String[] args) {RedissonClient redissonClient = RedissonClientFactory.getRedissonClient();// 启动消费者线程(实际应用中通常是独立服务)Thread consumerThread = new Thread(() -> consumeDelayMessage(redissonClient));consumerThread.setDaemon(true);consumerThread.start();// 模拟生产3个订单延迟消息produceDelayMessage(redissonClient, "order:10001", 5);  // 5秒后超时produceDelayMessage(redissonClient, "order:10002", 10); // 10秒后超时produceDelayMessage(redissonClient, "order:10003", 15); // 15秒后超时// 保持主线程运行(演示用)try {Thread.sleep(20000);} catch (InterruptedException e) {Thread.currentThread().interrupt();}// 实际应用中不需要关闭(长期运行的服务)// redissonClient.shutdown();}
}

最佳实践
  1. 消息设计:消息内容应包含足够业务信息(如订单ID)
  2. 异常处理:消费者需处理各种异常情况
  3. 幂等性:消息处理应保证幂等,防止重复消费
  4. 监控报警:对消费延迟和堆积情况监控
  5. 资源释放:正确关闭延迟队列实例
性能优化建议
  1. 批量消费:可适当批量获取消息减少网络开销
  2. 分区设计:大流量场景可按业务ID分多个队列
  3. 消费并行度:根据业务需求调整消费者数量
  4. 消息压缩:大消息可考虑压缩后再存储

五、Spring Boot 整合 Redis:企业级项目实战

在 Spring Boot 项目中,可通过 spring-boot-starter-data-redis 快速整合 Redis,简化配置和开发。该 starter 默认使用 Spring Data Redis 抽象层,支持 Jedis、Lettuce(Spring Boot 2.x 默认客户端)等底层客户端。Spring Data Redis 提供了统一的 API 来操作 Redis,支持多种数据结构的操作,包括字符串、哈希、列表、集合等。

5.1 环境配置

5.1.1 添加 Maven 依赖

pom.xml 中添加以下依赖:

<!-- Spring Boot Redis Starter -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId><!-- 排除默认的Lettuce客户端(若需使用Jedis,可添加此配置) --><!-- <exclusions><exclusion><groupId>io.lettuce</groupId><artifactId>lettuce-core</artifactId></exclusion></exclusions> -->
</dependency><!-- 若排除Lettuce后,需添加Jedis依赖 -->
<!-- <dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId>
</dependency> --><!-- Spring Boot Web Starter(用于演示接口) -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId>
</dependency>

说明:

  1. spring-boot-starter-data-redis 是 Spring Boot 提供的 Redis 启动器,自动配置了 Redis 相关的 Bean。
  2. 默认使用 Lettuce 客户端,如需切换为 Jedis,需排除 Lettuce 并添加 Jedis 依赖。
  3. spring-boot-starter-web 用于演示 RESTful 接口,实际项目中根据需求添加。
5.1.2 配置 application.yml

src/main/resources/application.yml 中添加 Redis 相关配置:

spring:redis:host: 192.168.1.100 # Redis服务地址,生产环境建议使用域名或内网IPport: 6379 # Redis端口,默认6379password: your_password # Redis密码(若未设,可省略)database: 0 # Redis数据库索引(0-15),默认0timeout: 5000ms # 连接超时时间lettuce: # Lettuce客户端配置(若使用Jedis,需改为jedis配置)pool: # 连接池配置max-active: 100 # 最大连接数,默认8max-idle: 20 # 最大空闲连接数,默认8min-idle: 5 # 最小空闲连接数,默认0max-wait: 3000ms # 连接池耗尽时的最大等待时间,默认-1(无限等待)

配置项详解:

  1. hostport:Redis 服务器地址和端口,必填项。
  2. password:如果 Redis 设置了密码,需配置此项。
  3. database:Redis 默认有 16 个数据库(0-15),可通过此配置选择使用的数据库。
  4. timeout:连接 Redis 的超时时间,建议设置为 5000ms 以上。
  5. lettuce.pool:Lettuce 连接池配置,适用于高并发场景:
    • max-active:最大连接数,根据业务量调整。
    • max-idlemin-idle:空闲连接数,避免频繁创建和销毁连接。
    • max-wait:获取连接的最大等待时间,避免长时间阻塞。

Jedis 配置示例(如需切换):

spring:redis:host: 192.168.1.100port: 6379jedis:pool:max-active: 100max-idle: 20min-idle: 5max-wait: 3000ms

生产环境建议:

  1. 使用连接池以提高性能。
  2. 密码和敏感信息建议通过环境变量或配置中心管理。
  3. 对于集群或哨兵模式,需额外配置。

5.2 核心 API:RedisTemplate 与 StringRedisTemplate

Spring Data Redis 提供了两种主要的模板类用于操作 Redis,它们各自适用于不同的使用场景:

StringRedisTemplate

StringRedisTemplate 是专门用于操作 String 类型数据的模板类,具有以下特点:

  1. 序列化方式:默认使用 StringRedisSerializer,所有键和值都以 UTF-8 编码的字符串形式存储
  2. 优势
    • 完全避免中文乱码问题
    • 存储的数据可直接在 Redis CLI 中查看
    • 性能较高,适用于简单字符串操作
  3. 限制
    • 只能操作字符串类型的数据
    • 无法直接存储 Java 对象

RedisTemplate

RedisTemplate 是通用模板类,支持所有 Redis 数据结构,但需要注意:

  1. 默认序列化问题
    • 默认使用 JdkSerializationRedisSerializer
    • 会导致存储的数据带有"xac\xed\x00\x05t\x00"等序列化前缀
    • 存储的数据在 Redis CLI 中不可读
  2. 适用场景
    • 需要操作复杂数据结构(List, Set, ZSet等)
    • 需要直接存储 Java 对象
    • 需要自定义序列化方式

5.2.1 配置 RedisTemplate(自定义序列化)

为优化 RedisTemplate 的默认行为,推荐配置自定义序列化:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;@Configuration
public class RedisConfig {@Beanpublic RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();// 设置连接工厂(必需)redisTemplate.setConnectionFactory(redisConnectionFactory);// Key序列化配置(String类型)StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();redisTemplate.setKeySerializer(stringRedisSerializer);redisTemplate.setHashKeySerializer(stringRedisSerializer);// Value序列化配置(JSON格式)GenericJackson2JsonRedisSerializer jackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer();redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);// 初始化模板(必需)redisTemplate.afterPropertiesSet();return redisTemplate;}
}

配置说明:

  1. 键序列化使用 StringRedisSerializer,保证键的可读性
  2. 值序列化使用 Jackson,支持复杂对象存储
  3. 必须调用 afterPropertiesSet() 完成初始化

5.2.2 StringRedisTemplate 使用示例

StringRedisTemplate 的常见操作示例:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.*;import java.util.Map;
import java.util.concurrent.TimeUnit;@RestController
@RequestMapping("/redis/string")
public class StringRedisController {@Autowiredprivate StringRedisTemplate stringRedisTemplate;// 存储String数据(带过期时间)@PostMapping("/set")public String setString(@RequestParam String key,@RequestParam String value,@RequestParam(required = false, defaultValue = "3600") long timeout) {stringRedisTemplate.opsForValue().set(key, value, timeout, TimeUnit.SECONDS);return "存储成功!Key:" + key + ",Value:" + value;}// 获取String数据@GetMapping("/get/{key}")public String getString(@PathVariable String key) {String value = stringRedisTemplate.opsForValue().get(key);return "获取到的数据:Key:" + key + ",Value:" + (value == null ? "无" : value);}// 存储Hash数据@PostMapping("/hash/set")public String setHash(@RequestParam String key,@RequestParam Map<String, String> hashMap) {stringRedisTemplate.opsForHash().putAll(key, hashMap);return "Hash数据存储成功!Key:" + key;}// 获取Hash数据@GetMapping("/hash/get/{key}")public Map<Object, Object> getHash(@PathVariable String key) {return stringRedisTemplate.opsForHash().entries(key);}// 设置过期时间@PostMapping("/expire")public Boolean expireKey(@RequestParam String key, @RequestParam long timeout) {return stringRedisTemplate.expire(key, timeout, TimeUnit.SECONDS);}
}

5.2.3 RedisTemplate 使用示例(操作对象)

定义实体类:

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {private Long id;private String name;private Integer age;private String gender;
}

对象操作示例:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.*;import java.util.concurrent.TimeUnit;@RestController
@RequestMapping("/redis/object")
public class ObjectRedisController {@Autowiredprivate RedisTemplate<String, Object> redisTemplate;// 存储User对象@PostMapping("/user/set")public String setUser(@RequestBody User user,@RequestParam(required = false, defaultValue = "86400") long timeout) {String key = "user:" + user.getId();redisTemplate.opsForValue().set(key, user, timeout, TimeUnit.SECONDS);return "User对象存储成功!Key:" + key + ",User:" + user;}// 获取User对象@GetMapping("/user/get")public User getUser(@RequestParam Long userId) {String key = "user:" + userId;return (User) redisTemplate.opsForValue().get(key);}// 删除User对象@PostMapping("/user/delete")public String deleteUser(@RequestParam Long userId) {String key = "user:" + userId;Boolean isDeleted = redisTemplate.delete(key);return isDeleted ? "User对象删除成功!Key:" + key : "删除失败,Key不存在";}
}

操作说明:

  1. 对象存储会自动使用配置的 Jackson 序列化器
  2. 获取时会自动反序列化为 Java 对象
  3. 建议使用"类型:id"的键命名规范(如"user:123")

5.3 Spring Boot Redis缓存注解:简化缓存开发

Spring Boot提供了@Cacheable@CachePut@CacheEvict等缓存注解,通过声明式的方式快速实现缓存功能,无需手动调用RedisTemplate。这些注解底层基于Spring Cache抽象,可以与多种缓存实现(如Redis、EhCache等)无缝集成,大大简化了缓存开发流程。

5.3.1 开启缓存注解支持

在Spring Boot启动类上添加@EnableCaching注解:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;@SpringBootApplication
@EnableCaching // 开启缓存注解支持,自动配置CacheManager
public class RedisSpringBootApplication {public static void main(String[] args) {SpringApplication.run(RedisSpringBootApplication.class, args);}
}

注意事项

  1. 确保项目中已引入spring-boot-starter-cachespring-boot-starter-data-redis依赖
  2. 在application.properties/yml中配置Redis连接信息
  3. 默认情况下会自动配置RedisCacheManager

5.3.2 常用缓存注解详解

(1)@Cacheable:查询缓存

当方法被调用时,先从缓存中查询数据。若缓存存在,则直接返回缓存数据;若缓存不存在,则执行方法逻辑,并将结果存入缓存。

import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;@Service
public class UserService {/*** 根据用户ID查询用户信息* @param userId 用户ID* @return 用户对象* * 缓存配置说明:* - value="userCache": 指定缓存名称(相当于Redis中的key前缀)* - key="#userId": 动态生成缓存key(使用SpEL表达式获取方法参数)* - unless="#result == null": 当方法返回null时不缓存结果*/@Cacheable(value = "userCache", key = "#userId", unless = "#result == null")public User getUserById(Long userId) {// 模拟从数据库查询数据(实际开发中替换为DAO层调用)System.out.println("从数据库查询用户,userId:" + userId);return new User(userId, "张三", 25, "男");}
}

典型应用场景

  • 高频访问但数据变化不频繁的查询,如商品详情、用户信息等
  • 复杂计算结果的缓存,如报表数据、统计结果等
(2)@CachePut:更新缓存

执行方法逻辑后,将结果存入缓存(无论缓存是否存在,都会覆盖原有缓存),常用于数据新增或更新场景。

import org.springframework.cache.annotation.CachePut;
import org.springframework.stereotype.Service;@Service
public class UserService {/*** 保存或更新用户信息* @param user 用户对象* @return 更新后的用户对象* * 缓存配置说明:* - value="userCache": 缓存名称* - key="#user.id": 使用用户ID作为缓存key* - unless="#result == null": 更新失败不缓存*/@CachePut(value = "userCache", key = "#user.id", unless = "#result == null")public User saveOrUpdateUser(User user) {// 模拟数据库新增/更新操作System.out.println("数据库新增/更新用户,user:" + user);return user; // 将更新后的用户对象存入缓存}
}

使用建议

  1. 方法返回值必须是需要缓存的对象
  2. 确保key与查询方法@Cacheable的key一致
  3. 适用于insert和update操作
(3)@CacheEvict:删除缓存

执行方法逻辑后,删除指定缓存,常用于数据删除场景。

import org.springframework.cache.annotation.CacheEvict;
import org.springframework.stereotype.Service;@Service
public class UserService {/*** 删除用户* @param userId 用户ID* * 缓存配置说明:* - value="userCache": 缓存名称* - key="#userId": 指定要删除的缓存key* - allEntries=false: 默认只删除指定key的缓存*/@CacheEvict(value = "userCache", key = "#userId", allEntries = false)public void deleteUser(Long userId) {// 模拟数据库删除操作System.out.println("数据库删除用户,userId:" + userId);}/*** 清空用户缓存* * 缓存配置说明:* - value="userCache": 缓存名称* - allEntries=true: 删除该缓存名称下的所有缓存*/@CacheEvict(value = "userCache", allEntries = true)public void clearUserCache() {System.out.println("清空用户缓存");}
}

删除策略选择

  1. 单条删除:使用key指定要删除的缓存项
  2. 批量删除:设置allEntries=true清空整个缓存区域
  3. 可在方法执行前删除(beforeInvocation=true)
(4)@Caching:组合缓存注解

当一个方法需要同时使用多个缓存注解(如同时更新和删除缓存)时,可使用@Caching组合注解。

import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Caching;
import org.springframework.stereotype.Service;@Service
public class UserService {/*** 更新用户信息并清空用户列表缓存* @param user 用户对象* @return 更新后的用户对象*/@Caching(put = {// 更新用户缓存@CachePut(value = "userCache", key = "#user.id")},evict = {// 删除用户列表缓存(假设存在用户列表缓存)@CacheEvict(value = "userListCache", allEntries = true)})public User updateUserAndClearListCache(User user) {// 模拟数据库更新操作System.out.println("更新用户并清空用户列表缓存,user:" + user);return user;}
}

适用场景

  1. 需要同时操作多个缓存区域
  2. 需要同时执行put和evict操作
  3. 复杂的缓存策略组合

六、Redis 进阶:持久化、高可用与性能优化

在实际生产环境中,仅掌握基础使用还不够,还需深入理解 Redis 的持久化机制、高可用架构配置及性能优化技巧,确保 Redis 服务稳定高效运行。本章将详细介绍这些关键内容。

6.1 Redis 持久化机制

Redis 提供两种持久化方式,可根据业务需求选择单独使用或组合使用,确保数据安全性和系统可靠性。

6.1.1 RDB(Redis Database):快照持久化

原理:在指定时间间隔内,将 Redis 内存中的数据生成二进制快照文件(.rdb 文件)并保存到磁盘。这种机制类似于数据库的全量备份。

触发方式

  1. 手动触发

    • save命令:阻塞 Redis 主线程直到快照完成,期间无法处理其他请求(生产环境不推荐)
    • bgsave命令:Redis 会 fork 一个子进程进行快照操作,主进程继续提供服务(推荐方式)
  2. 自动触发

    • 通过 redis.conf 配置文件中的 save 指令设置触发条件
    • 当 Redis 正常关闭时(shutdown命令)也会自动执行 RDB 持久化

典型配置示例

# 900秒(15分钟)内至少1个key变化,触发RDB
save 900 1
# 300秒(5分钟)内至少10个key变化,触发RDB
save 300 10
# 60秒内至少10000个key变化,触发RDB
save 60 10000# RDB文件名称
dbfilename dump.rdb
# 工作目录(保存RDB文件和AOF文件的目录)
dir /var/lib/redis

优缺点分析

  • 优点
    • 快照文件体积小,占用空间少
    • 恢复速度快,特别适合大规模数据恢复
    • 对 Redis 性能影响小,适合定期备份
  • 缺点
    • 可能丢失最后一次快照后的所有数据(如 Redis 异常崩溃时)
    • 当数据量很大时,fork 子进程的过程可能耗时较长,导致短暂的服务停顿

6.1.2 AOF(Append Only File):日志持久化

原理:将 Redis 的每一条写命令(如 SET、HSET)以协议文本格式追加到 AOF 日志文件中,Redis 重启时通过重新执行日志中的命令来重建数据。

AOF 工作流程

  1. 命令执行后写入 AOF 缓冲区
  2. 根据配置的刷盘策略将缓冲区内容同步到磁盘
  3. 定期进行 AOF 重写(rewrite)压缩文件体积

配置详解

# 开启AOF(默认关闭,需手动设置为yes)
appendonly yes# AOF文件名称
appendfilename "appendonly.aof"# AOF刷盘策略(关键配置)
# everysec:每秒刷盘一次(推荐,平衡性能和数据安全性)
# always:每执行一条命令刷盘一次(数据零丢失,性能较差)
# no:由操作系统决定刷盘时机(性能最好,数据丢失风险最高)
appendfsync everysec# AOF重写触发条件
# 当前AOF文件大小比起最后一次重写时的大小增长率达到100%时触发
auto-aof-rewrite-percentage 100
# AOF文件最小重写大小,避免文件很小就重写
auto-aof-rewrite-min-size 64mb# 加载AOF时发现文件末尾不完整时的处理方式
aof-load-truncated yes

AOF 重写机制

  1. 原理:创建一个新的 AOF 文件,包含重建当前数据集所需的最少命令集
  2. 触发方式:
    • 自动触发:根据 auto-aof-rewrite-percentage 和 auto-aof-rewrite-min-size 配置
    • 手动触发:执行BGREWRITEAOF命令

优缺点分析

  • 优点
    • 数据安全性高,最多丢失1秒数据(everysec配置下)
    • AOF 文件可读性强,便于问题排查
    • 通过重写机制可以压缩文件大小
  • 缺点
    • AOF 文件通常比 RDB 文件大
    • 恢复速度比 RDB 慢
    • 在高写入负载下,AOF 可能影响 Redis 性能

6.1.3 RDB 与 AOF 组合使用

生产环境中推荐同时开启 RDB 和 AOF,充分发挥两者的优势:

  1. 数据恢复策略

    • 日常恢复:优先使用 AOF 恢复(数据更完整)
    • 大规模数据恢复:若 AOF 文件过大,先使用 RDB 快速恢复基础数据,再通过 AOF 补充后续数据
  2. 典型配置组合

    • RDB 配置为每天全量备份一次
    • AOF 配置为每秒刷盘
    • 定期将 RDB 文件和 AOF 文件备份到异地
  3. 注意事项

    • Redis 4.0+ 支持混合持久化(aof-use-rdb-preamble),AOF 文件前半部分是 RDB 格式,后半部分是 AOF 格式,可以结合两者优点
    • 需要监控磁盘空间使用情况,避免持久化文件占满磁盘

6.2 Redis 高可用配置

为避免 Redis 单点故障,需搭建高可用架构,确保服务持续可用。Redis 提供了多种高可用方案,适用于不同场景。

6.2.1 主从复制(Master-Slave)

原理:将一台 Redis 服务器(Master)的数据同步到多台 Redis 服务器(Slave),实现数据冗余和读写分离。

核心特性

  • 异步复制:Slave 异步从 Master 同步数据
  • 读写分离:Master 处理写请求,Slave 处理读请求
  • 级联复制:Slave 可以作为其他 Slave 的 Master

配置步骤(以 1 主 2 从为例)

  1. 准备 3 台 Redis 服务器(假设IP为192.168.1.100-102)
  2. 配置 Master(192.168.1.100): 确保 redis.conf 中有以下配置:
    # 如果需要密码认证
    requirepass your_password
    

  3. 配置 Slave(192.168.1.101 和 192.168.1.102):
    # 指定Master的地址和端口
    replicaof 192.168.1.100 6379# 若Master设置了密码,需配置认证密码
    masterauth your_password# 可选:设置Slave只读
    replica-read-only yes
    

  4. 验证主从状态:
    • 在 Master 上执行info replication查看 Slave 连接信息
    • 在 Slave 上执行info replication查看复制状态

主从复制过程

  1. Slave 启动后向 Master 发送 SYNC 命令
  2. Master 执行 BGSAVE 生成 RDB 文件并发送给 Slave
  3. Master 将生成 RDB 期间的新命令缓存起来,之后发送给 Slave
  4. 之后 Master 将所有写命令异步发送给 Slave

注意事项

  • 复制延迟问题:网络延迟或 Slave 负载过高可能导致数据不一致
  • 数据过期问题:Redis 的过期策略在主从模式下有特殊处理
  • 复制中断处理:可通过replica-serve-stale-data yes配置Slave在断开连接时是否继续提供服务

6.2.2 哨兵模式(Sentinel)

原理:在主从复制的基础上,增加哨兵进程监控节点状态,实现自动故障转移。

哨兵核心功能

  1. 监控:持续检查 Master 和 Slave 是否正常运行
  2. 通知:当监控的 Redis 实例出现问题时,可以通过API通知管理员
  3. 自动故障转移:当 Master 不可用时,自动选举新的 Master
  4. 配置提供者:为客户端提供最新的 Master 地址

配置步骤(3个哨兵节点)

  1. 准备哨兵配置文件 sentinel.conf:
    port 26379
    sentinel monitor mymaster 192.168.1.100 6379 2
    sentinel auth-pass mymaster your_password
    sentinel down-after-milliseconds mymaster 30000
    sentinel parallel-syncs mymaster 1
    sentinel failover-timeout mymaster 180000
    

  2. 启动哨兵:
    redis-sentinel /path/to/sentinel.conf
    

  3. 验证哨兵状态:
    redis-cli -p 26379 info sentinel
    

故障转移流程

  1. 哨兵检测到 Master 不可达(超过 down-after-milliseconds 时间)
  2. 哨兵集群选举领导者哨兵(需要至少 quorum 数量的哨兵同意)
  3. 领导者哨兵从 Slave 中选择新的 Master(基于优先级、复制偏移量等)
  4. 将其他 Slave 重新配置为复制新的 Master
  5. 通知客户端配置变更

客户端访问哨兵模式的最佳实践

  1. 客户端应连接哨兵获取当前 Master 地址
  2. 客户端应订阅哨兵的+switch-master事件处理主从切换
  3. 建议使用支持哨兵的 Redis 客户端库(如Jedis、Lettuce)

6.2.3 Redis Cluster(集群)

原理:将数据分片存储到多个节点,每个节点负责一部分哈希槽(共16384个槽),同时每个分片可配置主从复制。

集群特性

  • 数据分片:自动将数据分布到多个节点
  • 高可用:每个分片可以配置主从复制
  • 线性扩展:可通过增加节点扩展集群容量和性能
  • 客户端路由:客户端可直接连接正确的节点

配置步骤(3主3从集群)

  1. 准备6台服务器(192.168.1.100-105),确保端口6379和16379开放
  2. 每个节点的 redis.conf 配置:
    cluster-enabled yes
    cluster-config-file nodes-6379.conf
    cluster-node-timeout 15000
    cluster-announce-ip 192.168.1.100 # 每台填写自己的IP
    cluster-announce-port 6379
    cluster-announce-bus-port 16379
    

  3. 启动所有节点后,创建集群:
    redis-cli --cluster create \192.168.1.100:6379 192.168.1.101:6379 192.168.1.102:6379 \192.168.1.103:6379 192.168.1.104:6379 192.168.1.105:6379 \--cluster-replicas 1
    

  4. 验证集群状态:
    redis-cli -c -h 192.168.1.100 -p 6379 cluster info
    redis-cli -c -h 192.168.1.100 -p 6379 cluster nodes
    

数据分片原理

  1. Redis 使用 CRC16(key) mod 16384 计算 key 所属的槽位
  2. 每个节点负责一部分连续的槽位
  3. 客户端可以缓存槽位到节点的映射关系

集群管理命令

  • cluster meet:将节点加入集群
  • cluster addslots:分配槽位给节点
  • cluster replicate:设置节点为从节点
  • cluster failover:手动触发故障转移

集群扩展操作

  1. 添加新节点:
    redis-cli --cluster add-node new_host:new_port existing_host:existing_port
    

  2. 迁移槽位:
    redis-cli --cluster reshard host:port
    

  3. 删除节点:
    redis-cli --cluster del-node host:port node_id
    

6.3 Redis 性能优化建议

6.3.1 硬件优化

内存优化

  • 为 Redis 分配足够的内存,避免频繁交换
  • 使用maxmemory限制 Redis 最大内存使用量
  • 考虑使用大页内存(transparent huge pages)

CPU优化

  • Redis 6.0+ 支持多线程IO(I/O threading),可配置:
    io-threads 4
    io-threads-do-reads yes
    

  • 在高并发场景下,可考虑使用多个 Redis 实例分担负载

磁盘优化

  • 使用 SSD 存储持久化文件
  • 确保磁盘有足够的写入带宽
  • 对于 AOF,可以考虑使用no-appendfsync-on-rewrite yes减少重写时的磁盘压力

6.3.2 配置优化

内存管理配置

# 内存淘汰策略(当内存达到maxmemory时)
maxmemory-policy allkeys-lru# 设置最大内存(例如4GB)
maxmemory 4gb# 避免内存碎片
activedefrag yes

网络配置优化

# 提高TCP连接队列大小
tcp-backlog 511# 客户端空闲超时时间
timeout 0 # 0表示不超时# 最大客户端连接数
maxclients 10000

持久化优化

# 禁用持久化(仅用于纯缓存场景)
save ""
appendonly no# 如果使用RDB,适当调整save参数
save 3600 1  # 1小时内至少有1个变化则保存

6.3.3 开发优化

键设计原则

  1. 使用简洁但有意义的键名,如user:1000:profile
  2. 避免使用大键(如包含百万元素的hash)
  3. 为键设置合理的过期时间

命令优化

  1. 使用批量操作命令:
    • MSET替代多个SET
    • HMGET替代多个HGET
  2. 使用管道(pipeline)减少网络往返:
    pipeline = redis.pipeline()
    pipeline.set('key1', 'value1')
    pipeline.set('key2', 'value2')
    pipeline.execute()
    

  3. 避免阻塞命令:
    • 避免在生产环境使用KEYS,改用SCAN
    • 谨慎使用FLUSHALL/FLUSHDB

客户端优化

  1. 使用连接池管理连接
  2. 合理设置连接池大小
  3. 实现客户端缓存(Redis 6.0+的客户端缓存功能)

监控与调优

  1. 定期检查info命令输出
  2. 监控慢查询(slowlog get
  3. 使用redis-benchmark进行性能测试
  4. 考虑使用Redis的Lua脚本减少网络开销
http://www.dtcms.com/a/561838.html

相关文章:

  • 官方网站是什么意思免费链接生成器
  • 增加网站访客珠宝首饰商城网站建设
  • 网络通信的奥秘:TCP与UDP详解(三)
  • 理财网站开发最近中国新闻
  • 详解网络安全免杀对抗:攻防的猫鼠游戏
  • 【开题答辩全过程】以 高考志愿分析系统为例,包含答辩的问题和答案
  • ESP-IDF基础入门(2)
  • 中国建设官方网站首页网上商城推广方案
  • 网站建设必须安装程序天眼查公司信息查询
  • 织梦网站首页是哪个文件网站手机访问跳转代码
  • 博弈dp|凸包|math分类
  • 网站浏览器兼容性问题wordpress手机端网站
  • 中国建设银行预约网站xampp做网站
  • VS2019+CUDA 编译通过但有错误提示
  • 有哪些做问卷调查挣钱的网站单页 网站模板
  • 承德网站制作数据库营销案例
  • 32位汇编:实验9分支程序结构使用
  • Kanass实践指南(3) - 开发团队如何通过kanass有效管控开发任务
  • 基于双向时序卷积网络与双向门控循环单元(BiTCN-BiGRU)混合模型的时间序列预测(Matlab源码)
  • 电子商务网站建设 精品课wordpress主题缓存
  • 站建设 app开发网站网站建设中怎么添加源码
  • qq推广网站建立企业网站 优帮云
  • 【AHE数据集】历史/未来全球网格热排放数据 PF-AHF
  • phy降速自愈到100M重试流程分析
  • Spring+LangChain4j工程搭建
  • Raft协议
  • 快速建设网站视频教程网站内容质量
  • 建设银行网站登不上牛商网做网站的思路
  • C语言程序代码(四)
  • 仿射变换的图像配准技术