Redis入门(1)

一、学习目标

  1. Redis 5种核心数据类型(string/hash/list/set/sorted set)的增删改查全操作,理解每种类型的适用场景
  2. Redis 单机、多实例、集群的部署与运维(含常见问题排查)
  3. Redis 在 JavaEE 项目中的实战应用(Jedis 原生调用、Spring 整合、缓存设计)
  4. Redis 高级特性(持久化、主从复制、集群)的原理与配置,解决数据安全和高可用问题
  5. 从 NoSQL 基础到 Redis 底层逻辑的完整知识链,能独立设计 Redis 缓存方案

二、NoSQL 基础:为什么需要 Redis?

在学 Redis 之前,我们先搞懂:什么是 NoSQL?它和我们熟悉的 MySQL(关系型数据库)有啥区别?

1. 什么是 NoSQL?

NoSQL = Not Only SQL(不只是 SQL),是一类非关系型数据库的总称。它不是要替代关系型数据库,而是作为补充,解决关系型数据库的"短板":

  • 关系型数据库(MySQL):适合结构化数据、复杂查询(联表)、事务一致性,但高并发、大数据量场景下性能拉胯(比如百万用户同时访问的电商网站)
  • NoSQL 数据库:适合非结构化/半结构化数据、高并发读写、海量数据存储,特点是高性能、高可用、可扩展

2. NoSQL 四大分类

分类 核心特点 代表产品 适用场景 优点 缺点
键值(Key-Value) 存储键值对,value 是字符串/二进制数据 Redis、Tokyo Cabinet 缓存、会话存储、计数器 查询速度极快(O(1))、支持高并发 数据无结构化,无法做复杂查询
列存储 按列簇存储,同一列数据存在一起 HBase、Cassandra 分布式文件系统、大数据分析(如日志存储) 查找速度快、可扩展性强(横向扩容) 功能局限,不适合复杂查询
文档型 value 是结构化文档(JSON/XML) MongoDB、CouchDB Web 应用(存储用户信息、文章内容) 数据结构灵活(字段可增删)、易读写 查询性能一般,无统一查询语法
图形(Graph) 存储节点和边的关系(如社交网络) Neo4j、Infinite Graph 社交网络、推荐系统(如好友关系、商品关联) 擅长复杂关系查询(如"好友的好友") 分布式集群难实现,大数据量下性能下降

3. Redis 为什么脱颖而出?

Redis 属于键值存储型 NoSQL,但它不止于此------支持多种数据类型、持久化、主从复制、集群,还能做缓存、消息队列、排行榜等,是"全能型选手"。其核心优势:

  • 基于内存操作,读写速度极快(读 11 万次/秒,写 8 万次/秒)
  • 支持 5 种核心数据类型,适配多种场景
  • 单线程模型(避免线程切换开销)+ IO 多路复用(高效处理并发连接)
  • 支持持久化(数据不丢失)、主从复制(高可用)、集群(横向扩容)
  • 跨平台、开源、社区活跃,企业级应用广泛(微博、知乎、GitHub 等)

三、Redis 前世今生

  • 2008 年:意大利创业公司 Merzia 开发实时统计系统 LLOOGG,创始人 Salvatore Sanfilippo 对 MySQL 性能不满
  • 2009 年:Salvatore 亲自开发 Redis(Remote Dictionary Server),专门适配 LLOOGG;同年开源,Pieter Noordhuis 加入共同开发
  • 2010 年:VMware 赞助 Redis 开发,两位核心开发者全职投入
  • 2012 年:Redis 用户量爆发,Hacker News 调查显示 12% 企业在使用
  • 至今:Redis 已成为最流行的 NoSQL 数据库,广泛用于缓存、消息队列、分布式锁等场景

四、Redis 安装与环境配置(CentOS7.5 实战)

Redis 基于 C 语言开发,推荐在 Linux 环境运行(Windows 版本功能不全,不推荐生产使用)。以下是详细安装步骤,含问题排查:

1. 前置环境准备

(1)安装 GCC 编译环境

Redis 源码需要 GCC 编译,先检查是否安装:

bash 复制代码
gcc --version  # 若显示版本号则已安装,否则执行下面命令
yum install -y gcc-c++  # 安装 GCC(CentOS 7 自带 yum 包管理器)

问题排查:若 yum 安装失败(如网络问题),可更换国内源(阿里源、网易源):

bash 复制代码
# 备份原 yum 源
mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.bak
# 下载阿里源(CentOS7)
wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/CentOS-7.repo
# 清理缓存并更新
yum clean all && yum makecache
(2)下载 Redis 源码

推荐版本:3.0(企业稳定版)或 4.0(功能更全),官网下载地址:

bash 复制代码
# 切换到 /usr/local 目录(习惯存放软件)
cd /usr/local
# 下载 Redis 4.0.14(推荐,兼容性好)
wget http://download.redis.io/releases/redis-4.0.14.tar.gz
# 若没有 wget,先安装:yum install -y wget

2. 解压与编译安装

bash 复制代码
# 解压源码包
tar -zxvf redis-4.0.14.tar.gz
# 进入解压目录
cd redis-4.0.14
# 编译(生成可执行文件)
make
# 安装到指定目录(/usr/local/redis,方便管理)
make PREFIX=/usr/local/redis install

验证安装 :进入 /usr/local/redis/bin 目录,若有以下文件则安装成功:

  • redis-server:Redis 服务端程序
  • redis-cli:Redis 客户端程序
  • redis-benchmark:性能测试工具
  • redis-check-aof:AOF 日志修复工具
  • redis-check-dump:RDB 快照修复工具
  • redis-sentinel:哨兵(集群高可用工具)

3. 配置文件优化(redis.conf)

Redis 配置文件在源码目录(/usr/local/redis-4.0.14/redis.conf),需拷贝到安装目录并修改关键配置:

bash 复制代码
# 进入安装目录,创建 conf 文件夹存放配置文件
cd /usr/local/redis
mkdir conf
# 拷贝配置文件
cp /usr/local/redis-4.0.14/redis.conf /usr/local/redis/conf/
# 编辑配置文件(用 vim)
vim /usr/local/redis/conf/redis.conf
关键配置项(必改)
配置项 默认值 修改后值 说明
daemonize no yes 后台运行(否则关闭 SSH 窗口,Redis 服务就停了)
bind 127.0.0.1 0.0.0.0 允许远程访问(默认只允许本机连接,开发/测试环境需修改)
port 6379 6379(可改) 服务端口(默认 6379,若启动多实例需修改)
requirepass 自定义密码 (可选)设置访问密码(生产环境必设,避免未授权访问),如:requirepass 123456
dir ./ /usr/local/redis/data 数据存储目录(持久化文件、日志文件存放位置),需提前创建:mkdir /usr/local/redis/data
logfile /usr/local/redis/logs/redis.log 日志文件路径,需提前创建:mkdir /usr/local/redis/logs

vim 操作技巧 :按 / 输入配置项名称(如 daemonize),按 n 查找下一个,修改后按 Esc,输入 :wq 保存退出。

4. Redis 服务启动与停止(核心操作)

(1)启动 Redis 服务
bash 复制代码
# 方式 1:指定配置文件启动(推荐,明确使用哪个配置)
/usr/local/redis/bin/redis-server /usr/local/redis/conf/redis.conf

# 方式 2:进入 bin 目录启动(简化路径)
cd /usr/local/redis/bin
./redis-server ../conf/redis.conf

验证启动成功

bash 复制代码
# 查看 Redis 进程
ps aux | grep redis
# 若显示类似以下内容,说明启动成功:
# root      1234  0.0  0.1 141848  2048 ?        Ssl  10:00   0:00 ./redis-server 0.0.0.0:6379
(2)停止 Redis 服务(安全方式,避免数据丢失)
bash 复制代码
# 方式 1:通过客户端发送 shutdown 命令(推荐)
/usr/local/redis/bin/redis-cli -h 127.0.0.1 -p 6379 -a 123456 shutdown save
# 参数说明:-h 主机IP,-p 端口,-a 密码(若未设密码可省略),save 表示停止前持久化数据

# 方式 2:若未设密码,简化命令
/usr/local/redis/bin/redis-cli shutdown save

禁止强制停止 :不要用 kill -9 进程号,会导致未持久化的数据丢失!

(3)启动多个 Redis 实例(同一服务器)

场景:开发/测试环境需要多个 Redis 服务(如一个用于缓存,一个用于消息队列),通过不同端口区分:

方法 1:启动时指定端口(临时方案)

bash 复制代码
# 启动第一个实例(端口 6379,用默认配置)
/usr/local/redis/bin/redis-server /usr/local/redis/conf/redis.conf
# 启动第二个实例(端口 6380,指定配置文件或命令行参数)
/usr/local/redis/bin/redis-server /usr/local/redis/conf/redis.conf --port 6380

方法 2:创建多配置目录(推荐,生产级)

bash 复制代码
# 1. 创建以端口命名的目录(清晰区分)
mkdir -p /usr/local/redis/6379 /usr/local/redis/6380
# 2. 拷贝 bin 和 conf 到对应目录
cp -r /usr/local/redis/bin /usr/local/redis/6379/
cp -r /usr/local/redis/conf /usr/local/redis/6379/
cp -r /usr/local/redis/bin /usr/local/redis/6380/
cp -r /usr/local/redis/conf /usr/local/redis/6380/
# 3. 修改 6380 目录的配置文件(port 改为 6380,dir 改为 /usr/local/redis/6380/data)
vim /usr/local/redis/6380/conf/redis.conf
# 4. 分别启动两个实例
cd /usr/local/redis/6379/bin && ./redis-server ../conf/redis.conf
cd /usr/local/redis/6380/bin && ./redis-server ../conf/redis.conf

5. Redis 客户端连接(操作 Redis 的入口)

Redis 自带客户端 redis-cli,用于发送命令操作 Redis 服务端,支持本地和远程连接。

(1)本地连接(同一服务器)
bash 复制代码
# 简化命令(默认连接 127.0.0.1:6379)
/usr/local/redis/bin/redis-cli
# 若设了密码,连接后需认证
127.0.0.1:6379> auth 123456  # 输入密码,返回 OK 则认证成功
(2)远程连接(Windows/Mac 连接 Linux 服务器)
bash 复制代码
# 命令格式:redis-cli -h 服务器IP -p 端口 -a 密码
redis-cli -h 192.168.0.209 -p 6379 -a 123456

Windows 客户端推荐 :除了命令行 redis-cli,还可以用可视化工具(如 Redis Desktop Manager、Another Redis Desktop Manager),操作更直观:

  1. 下载安装工具(官网或 GitHub 下载)
  2. 新建连接:输入服务器 IP、端口、密码,点击连接即可
(3)客户端常用测试命令
命令 功能 示例 返回值
ping 测试连接是否正常 192.168.0.209:6379> ping PONG(正常)
set key value 设置键值对 192.168.0.209:6379> set name zhangsan OK
get key 获取键对应的值 192.168.0.209:6379> get name "zhangsan"
keys * 查看所有键(生产环境慎用,会阻塞服务) 192.168.0.209:6379> keys * 所有键列表
exists key 判断键是否存在 192.168.0.209:6379> exists name 1(存在)/0(不存在)
del key 删除键 192.168.0.209:6379> del name 1(成功)/0(失败)

五、Redis 多数据库(了解即可,不推荐使用)

Redis 实例默认包含 16 个数据库(下标 0-15),类似 MySQL 的多个数据库,但功能非常简陋,不推荐在实际开发中使用。

1. 切换数据库

bash 复制代码
192.168.0.209:6379> select 1  # 切换到 1 号数据库(默认 0 号)
OK
192.168.0.209:6379[1]>  # 提示符后加 [1],表示当前在 1 号库

2. 数据库特性

  • 每个数据库独立存储数据,切换后看不到其他库的键
  • 不支持修改数据库名称,只能通过下标切换
  • 关键坑点:flushall 命令会清空所有数据库 的数据(不管当前在哪个库),而 flushdb 只清空当前数据库数据

3. 为什么不推荐使用多数据库?

  • 缺乏权限控制:无法给不同数据库设置不同密码
  • 数据隔离性差:flushall 容易误删所有数据
  • 运维不便:无法单独备份某个数据库,集群环境下不支持多数据库

推荐方案 :不同应用/模块使用不同的 Redis 实例(不同端口),而非同一实例的不同数据库。

六、Redis 核心数据类型(重中之重)

Redis 支持 5 种核心数据类型,每种类型都有特定的底层实现和适用场景,掌握它们是使用 Redis 的基础。

1. 字符串(string):最基础的键值对

(1)特点
  • 存储任意类型的二进制数据(字符串、数字、图片二进制等)
  • 最大存储容量:512MB
  • 底层实现:简单动态字符串(SDS),而非 C 语言原生字符串
(2)SDS 底层结构(为什么 Redis 字符串这么强?)

C 语言字符串的缺点:

  • 长度获取需要遍历(O(n) 时间)
  • 不支持二进制数据(遇到 \0 认为字符串结束)
  • 拼接/修改容易导致缓冲区溢出

Redis SDS 结构(sds.h 源码):

c 复制代码
struct sdshdr {
    unsigned int len;  // 字符串实际长度(O(1) 获取)
    unsigned int free; // 空闲空间(减少内存分配次数)
    char buf[];        // 存储字符串的缓冲区(二进制安全)
};

SDS 优势:

  • 二进制安全:buf 存储二进制数据,\0 只是普通字符,支持存储图片、视频等
  • 长度获取 O(1):直接读取 len 字段
  • 减少内存分配:free 字段记录空闲空间,拼接时先检查空闲空间,不够再扩容
(3)常用命令(详细示例+说明)
命令 功能 示例 返回值 注意事项
SET key value 设置键值对(覆盖已有键) SET name zhangsan OK 若键已存在,会覆盖原 value
SETNX key value 仅当键不存在时设置(分布式锁常用) SETNX name lisi(若 name 已存在) 0(失败) NX = Not eXists
GET key 获取键对应的值 GET name "zhangsan" 或 nil 键不存在返回 nil
APPEND key value 向字符串尾部追加内容 APPEND name " is 20 years old" 21(追加后总长度) 键不存在则创建,value 为追加的内容
STRLEN key 获取字符串长度 STRLEN name 10("zhangsan" 长度) 二进制数据也能正确获取长度
MSET key1 val1 key2 val2 批量设置键值对 MSET age 20 gender male OK 原子操作(要么全成功,要么全失败)
MGET key1 key2 批量获取键值对 MGET name age 1) "zhangsan" 2) "20" 键不存在返回 nil
INCR key 数字自增 1(仅支持整数 value) SET num 10 → INCR num 11 非数字 value 会报错
INCRBY key step 数字自增指定步长 INCRBY num 5 15 step 可以是负数(相当于自减)
DECR key 数字自减 1 DECR num 14
DECRBY key step 数字自减指定步长 DECRBY num 3 11
SETEX key sec value 设置键值对并指定过期时间(秒) SETEX code 60 123456 OK 过期后键自动删除
PSETEX key ms value 过期时间单位为毫秒 PSETEX code 3000 123456 OK 精确到毫秒的过期时间
(4)应用场景
  • 缓存:存储用户信息、商品详情(序列化后存 string)
  • 计数器:文章阅读量、商品库存、接口访问次数(INCR/DECR)
  • 验证码:存储手机验证码,设置过期时间(SETEX)
  • 分布式锁:SETNX + EXPIRE(防止死锁)

2. 哈希(hash):适合存储对象

(1)特点
  • 存储字段和字段值的映射(类似 Java 的 Map<String, String>)
  • 字段值只能是字符串,不能是其他类型(如 hash、list)
  • 适合存储对象(如用户、商品),可单独修改对象的某个字段,无需整体更新
(2)底层实现
  • 小数据量(字段少且值短):压缩列表(ziplist)→ 节省内存
  • 大数据量:哈希表(hashtable)→ 保证查询效率
(3)常用命令
命令 功能 示例 返回值
HSET key field value 设置哈希表的字段值 HSET user:1001 name zhangsan age 20 2(成功设置的字段数)
HGET key field 获取哈希表的字段值 HGET user:1001 name "zhangsan"
HMSET key field1 val1 field2 val2 批量设置字段值 HMSET user:1001 gender male addr beijing OK
HMGET key field1 field2 批量获取字段值 HMGET user:1001 name age 1) "zhangsan" 2) "20"
HGETALL key 获取哈希表所有字段和值 HGETALL user:1001 1) "name" 2) "zhangsan" 3) "age" 4) "20"
HEXISTS key field 判断字段是否存在 HEXISTS user:1001 name 1(存在)/0(不存在)
HSETNX key field value 仅当字段不存在时设置 HSETNX user:1001 name lisi 0(失败,name 已存在)
HDEL key field1 field2 删除字段 HDEL user:1001 addr 1(成功删除的字段数)
HKEYS key 获取所有字段名 HKEYS user:1001 1) "name" 2) "age"
HVALS key 获取所有字段值 HVALS user:1001 1) "zhangsan" 2) "20"
HLEN key 获取字段总数 HLEN user:1001 2
HINCRBY key field step 字段值自增指定步长(仅支持整数) HINCRBY user:1001 age 1 21
(4)应用场景
  • 存储对象:用户信息(id、name、age、addr)、商品信息(id、name、price、stock)
  • 优势:相比 string 序列化(如 JSON),hash 可以单独修改某个字段,节省带宽和内存

3. 列表(list):有序的字符串列表

(1)特点
  • 有序(插入顺序)、可重复
  • 支持两端插入/删除,中间查询较慢
  • 底层实现:双向链表(double linked list)→ 两端操作 O(1),中间操作 O(n)
(2)常用命令(重点:两端操作)
命令 功能 示例 返回值
LPUSH key value1 value2 向列表左侧(头部)插入元素 LPUSH list:1 1 2 3 3(列表长度)
RPUSH key value1 value2 向列表右侧(尾部)插入元素 RPUSH list:1 4 5 6 6(列表长度)
LPOP key 从列表左侧弹出元素(删除并返回) LPOP list:1 "3"
RPOP key 从列表右侧弹出元素 RPOP list:1 "6"
LRANGE key start stop 获取列表片段(start=0,stop=-1 表示所有) LRANGE list:1 0 -1 1) "2" 2) "1" 3) "4" 4) "5"
LLEN key 获取列表长度 LLEN list:1 4
LINDEX key index 获取指定索引的元素(0 开头,-1 结尾) LINDEX list:1 2 "4"
LSET key index value 修改指定索引的元素值 LSET list:1 2 44 OK
LREM key count value 删除前 count 个值为 value 的元素 LREM list:1 1 44 1(删除的元素数)
LTRIM key start stop 保留列表片段,删除其他元素 LTRIM list:1 0 2 OK
RPOPLPUSH source dest 从 source 列表右侧弹出元素,插入 dest 左侧 RPOPLPUSH list:1 list:2 "5"(弹出的元素)
(3)应用场景
  • 消息队列:LPUSH + RPOP(生产者左侧插入,消费者右侧弹出)
  • 最新消息列表:LPUSH 插入新消息,LRANGE 获取前 N 条(如朋友圈最新动态)
  • 在线好友列表:列表存储好友 ID,支持添加/删除好友
  • 任务队列:RPOPLPUSH 实现安全的任务处理(避免任务丢失)

4. 集合(set):无序的唯一集合

(1)特点
  • 无序、元素唯一(自动去重)
  • 支持集合运算(交集、并集、差集)
  • 底层实现:哈希表(hashtable)→ 查找、插入、删除 O(1)
(2)常用命令
命令 功能 示例 返回值
SADD key member1 member2 向集合添加元素 SADD set:1 a b c d 4(添加的元素数)
SMEMBERS key 获取集合所有元素 SMEMBERS set:1 1) "a" 2) "b" 3) "c" 4) "d"
SISMEMBER key member 判断元素是否在集合中 SISMEMBER set:1 a 1(存在)/0(不存在)
SREM key member1 member2 从集合删除元素 SREM set:1 d 1(删除的元素数)
SCARD key 获取集合元素个数 SCARD set:1 3
SINTER key1 key2 求两个集合的交集(共同元素) SADD set:2 b c e → SINTER set:1 set:2 1) "b" 2) "c"
SUNION key1 key2 求两个集合的并集(所有元素,去重) SUNION set:1 set:2 1) "a" 2) "b" 3) "c" 4) "e"
SDIFF key1 key2 求两个集合的差集(key1 有而 key2 没有) SDIFF set:1 set:2 1) "a"
SRANDMEMBER key [count] 随机获取集合中的元素 SRANDMEMBER set:1 2 1) "b" 2) "a"
SPOP key [count] 随机弹出集合中的元素(删除并返回) SPOP set:1 "c"
(3)应用场景
  • 去重:存储用户标签(如"运动""美食"),自动去重
  • 好友关系:存储用户的好友 ID,SINTER 求共同好友
  • 抽奖活动:SPOP 随机抽取中奖用户
  • 标签匹配:SUNION 合并多个标签的用户,SDIFF 筛选不包含某个标签的用户

5. 有序集合(sorted set/zset):有序的唯一集合

(1)特点
  • 元素唯一(去重)、有序(按分数排序)
  • 每个元素关联一个分数(score),按分数升序/降序排列
  • 底层实现:压缩列表(小数据量)+ 跳表(skiplist)+ 哈希表(大数据量)→ 兼顾排序和查询效率
(2)跳表(为什么 zset 排序这么快?)

跳表是一种有序数据结构,通过在原始链表上增加多层索引,实现快速查找(类似字典的目录):

  • 原始链表:查找元素 O(n)
  • 跳表:通过索引层"跳跃"查找,平均 O(log n),最坏 O(n)
  • Redis zset 用跳表保证排序,哈希表保证元素唯一性(快速判断元素是否存在)
(3)常用命令
命令 功能 示例 返回值
ZADD key score1 member1 score2 member2 向有序集合添加元素(分数+元素) ZADD rank:score 80 zhangsan 90 lisi 85 wangwu 3(添加的元素数)
ZSCORE key member 获取元素的分数 ZSCORE rank:score lisi "90"
ZRANGE key start stop [WITHSCORES] 按分数升序排列,获取片段 ZRANGE rank:score 0 -1 WITHSCORES 1) "zhangsan" 2) "80" 3) "wangwu" 4) "85" 5) "lisi" 6) "90"
ZREVRANGE key start stop [WITHSCORES] 按分数降序排列,获取片段 ZREVRANGE rank:score 0 -1 WITHSCORES 1) "lisi" 2) "90" 3) "wangwu" 4) "85" 5) "zhangsan" 6) "80"
ZRANGEBYSCORE key min max [WITHSCORES] 按分数范围筛选元素(升序) ZRANGEBYSCORE rank:score 80 90 WITHSCORES 同上
ZINCRBY key increment member 元素分数自增指定值(可负数) ZINCRBY rank:score 5 zhangsan "85"
ZCARD key 获取有序集合元素个数 ZCARD rank:score 3
ZCOUNT key min max 统计分数范围内的元素个数 ZCOUNT rank:score 80 85 2
ZRANK key member 获取元素的升序排名(0 开头) ZRANK rank:score zhangsan 0(第一名)
ZREVRANK key member 获取元素的降序排名(0 开头) ZREVRANK rank:score zhangsan 2(第三名)
ZREM key member1 member2 删除有序集合中的元素 ZREM rank:score wangwu 1(删除的元素数)
(4)应用场景
  • 排行榜:商品销量排行、用户积分排行、文章阅读量排行(ZREVRANGE)
  • 范围筛选:筛选分数在 80-90 分的学生(ZRANGEBYSCORE)
  • 延迟队列:分数存储时间戳,ZRANGEBYSCORE 获取到期任务(如订单超时未支付)

七、Redis 键的过期时间(缓存必备)

Redis 支持给键设置过期时间,到期后自动删除,非常适合缓存场景(如缓存商品详情,1 小时后过期)。

1. 常用命令

命令 功能 示例 返回值
EXPIRE key seconds 设置键的过期时间(秒) EXPIRE name 60 1(成功)/0(键不存在)
PEXPIRE key ms 设置过期时间(毫秒) PEXPIRE name 30000 1
TTL key 查看键的剩余过期时间(秒) TTL name 30(剩余 30 秒)/ -1(永不过期)/ -2(已过期)
PTTL key 查看剩余过期时间(毫秒) PTTL name 30000
PERSIST key 取消键的过期时间(永不过期) PERSIST name 1(成功)/0(键不存在或无过期时间)

2. 注意事项

  • 过期时间精确到毫秒(Redis 4.0+)
  • 键过期后,Redis 会自动删除(两种策略:惰性删除+定期删除):
    • 惰性删除:访问键时才检查是否过期,节省 CPU,可能浪费内存
    • 定期删除:每隔一段时间扫描部分过期键,平衡 CPU 和内存
  • 过期时间会随键的修改而重置:如 SET name zhangsan → EXPIRE name 60 → SET name lisi,此时 name 的过期时间消失(永不过期)

八、Java 操作 Redis(Jedis + Spring 整合)

Redis 支持多种编程语言客户端,Java 中最常用的是 Jedis(官方推荐),结合 Spring 可以更方便地集成到项目中。

1. Jedis 原生使用(基础)

(1)导入依赖(Maven)
xml 复制代码
<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>2.9.0</version> <!-- 稳定版本,兼容性好 -->
</dependency>
(2)两种连接方式
方式 1:单实例连接(不推荐,频繁创建/关闭连接,效率低)
java 复制代码
import redis.clients.jedis.Jedis;

public class JedisSingleTest {
    public static void main(String[] args) {
        // 1. 创建 Jedis 实例(参数:Redis 服务器 IP、端口)
        Jedis jedis = new Jedis("192.168.0.209", 6379);
        // 2. 若设了密码,认证
        jedis.auth("123456");
        try {
            // 3. 操作 Redis(如 set/get)
            jedis.set("name", "zhangsan");
            String name = jedis.get("name");
            System.out.println(name); // 输出:zhangsan
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 4. 关闭连接(必须关闭,否则资源泄露)
            jedis.close();
        }
    }
}
方式 2:连接池连接(推荐,复用连接,提高效率)
java 复制代码
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

public class JedisPoolTest {
    public static void main(String[] args) {
        // 1. 配置连接池参数
        JedisPoolConfig poolConfig = new JedisPoolConfig();
        poolConfig.setMaxTotal(30); // 最大连接数
        poolConfig.setMaxIdle(2);   // 最大空闲连接数(空闲时保留的连接)
        poolConfig.setTestOnBorrow(true); // 借连接时测试连接是否可用

        // 2. 创建连接池
        JedisPool jedisPool = new JedisPool(poolConfig, "192.168.0.209", 6379, 10000, "123456");
        // 参数说明:连接池配置、IP、端口、超时时间(10秒)、密码

        Jedis jedis = null;
        try {
            // 3. 从连接池获取连接
            jedis = jedisPool.getResource();
            // 4. 操作 Redis
            jedis.hset("user:1002", "name", "lisi");
            String userName = jedis.hget("user:1002", "name");
            System.out.println(userName); // 输出:lisi
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 5. 归还连接(关闭连接池会释放所有连接)
            if (jedis != null) {
                jedis.close(); // 实际是归还到连接池,而非关闭连接
            }
        }
    }
}

2. Spring 整合 Redis(企业级开发)

Spring 提供 spring-data-redis 模块,简化 Redis 操作,支持依赖注入、事务管理等。

(1)导入依赖(Maven)
xml 复制代码
<!-- Spring 核心依赖 -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-core</artifactId>
    <version>4.1.7.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>4.1.7.RELEASE</version>
</dependency>
<!-- Spring Data Redis -->
<dependency>
    <groupId>org.springframework.data</groupId>
    <artifactId>spring-data-redis</artifactId>
    <version>1.6.0.RELEASE</version>
</dependency>
<!-- Jedis -->
<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>2.9.0</version>
</dependency>
(2)Spring 配置文件(application.xml)
xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                           http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
                           http://www.springframework.org/schema/context
                           http://www.springframework.org/schema/context/spring-context-4.1.xsd">

    <!-- 扫描组件(如 Redis 工具类) -->
    <context:component-scan base-package="com.example.redis"/>

    <!-- 1. 配置 Jedis 连接池 -->
    <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
        <property name="maxTotal" value="50"/> <!-- 最大连接数 -->
        <property name="maxIdle" value="10"/>  <!-- 最大空闲连接数 -->
        <property name="minIdle" value="5"/>   <!-- 最小空闲连接数 -->
        <property name="testOnBorrow" value="true"/> <!-- 借连接时测试 -->
        <property name="testOnReturn" value="true"/> <!-- 还连接时测试 -->
    </bean>

    <!-- 2. 配置 Redis 连接工厂 -->
    <bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
        <property name="hostName" value="192.168.0.209"/> <!-- Redis 服务器 IP -->
        <property name="port" value="6379"/> <!-- 端口 -->
        <property name="password" value="123456"/> <!-- 密码 -->
        <property name="poolConfig" ref="jedisPoolConfig"/> <!-- 连接池配置 -->
        <property name="timeout" value="10000"/> <!-- 连接超时时间(毫秒) -->
    </bean>

    <!-- 3. 配置 RedisTemplate(核心操作类) -->
    <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
        <property name="connectionFactory" ref="jedisConnectionFactory"/>
        <!-- 配置序列化方式(默认 JdkSerializationRedisSerializer,可改为 StringRedisSerializer) -->
        <property name="keySerializer">
            <bean class="org.springframework.data.redis.serializer.StringRedisSerializer"/>
        </property>
        <property name="valueSerializer">
            <bean class="org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer"/>
        </property>
    </bean>
</beans>
(3)Redis 工具类(封装常用操作)
java 复制代码
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

@Component
public class RedisUtil {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    // 1. 操作 string 类型
    public void setString(String key, Object value) {
        ValueOperations<String, Object> operations = redisTemplate.opsForValue();
        operations.set(key, value);
    }

    public void setString(String key, Object value, long timeout) {
        ValueOperations<String, Object> operations = redisTemplate.opsForValue();
        operations.set(key, value, timeout, TimeUnit.SECONDS);
    }

    public Object getString(String key) {
        ValueOperations<String, Object> operations = redisTemplate.opsForValue();
        return operations.get(key);
    }

    // 2. 操作 hash 类型(其他类型类似,可自行扩展)
    public void setHash(String key, String hashKey, Object value) {
        redisTemplate.opsForHash().put(key, hashKey, value);
    }

    public Object getHash(String key, String hashKey) {
        return redisTemplate.opsForHash().get(key, hashKey);
    }

    // 3. 删除键
    public boolean deleteKey(String key) {
        return redisTemplate.delete(key);
    }

    // 4. 设置过期时间
    public boolean expireKey(String key, long timeout) {
        return redisTemplate.expire(key, timeout, TimeUnit.SECONDS);
    }
}
(4)测试 Spring 整合 Redis
java 复制代码
import com.example.redis.RedisUtil;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpringRedisTest {
    public static void main(String[] args) {
        // 1. 加载 Spring 配置文件
        ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
        // 2. 获取 RedisUtil 实例
        RedisUtil redisUtil = context.getBean(RedisUtil.class);

        // 3. 测试操作
        redisUtil.setString("spring:name", "redis", 60); // 设置过期时间 60 秒
        Object name = redisUtil.getString("spring:name");
        System.out.println(name); // 输出:redis

        redisUtil.setHash("spring:user", "name", "zhangsan");
        Object userName = redisUtil.getHash("spring:user", "name");
        System.out.println(userName); // 输出:zhangsan
    }
}

九、Redis 高级特性(数据安全与高可用)

1. 持久化(防止数据丢失)

Redis 数据默认存储在内存中,服务器重启(如断电、宕机)会导致数据丢失。持久化就是将内存中的数据同步到硬盘,重启后从硬盘恢复数据。

Redis 支持两种持久化方式:RDB 和 AOF,可单独使用或混合使用。

(1)RDB 持久化(默认开启)
原理:
  • 定期创建内存数据的快照(二进制文件,.rdb),保存到硬盘
  • 触发机制:
    • 自动触发:配置文件中 save 指令(如 save 900 1 表示 15 分钟内至少 1 个键修改,触发快照)
    • 手动触发:执行 SAVE(阻塞服务,不推荐)或 BGSAVE(后台执行,推荐)命令
配置(redis.conf):
ini 复制代码
# 自动触发快照条件(多个条件"或"关系)
save 900 1    # 900秒(15分钟)内至少1个键修改
save 300 10   # 300秒(5分钟)内至少10个键修改
save 60 10000 # 60秒内至少10000个键修改

# RDB 文件路径和名称
dir /usr/local/redis/data  # 文件存放目录
dbfilename dump.rdb        # 文件名
优缺点:
优点 缺点
快照文件体积小,恢复速度快(适合大规模数据) 数据丢失风险:两次快照之间的修改会丢失(如配置 15 分钟快照,宕机后丢失 15 分钟数据)
备份和迁移方便(复制 .rdb 文件即可) 触发快照时可能阻塞服务(BGSAVE 会 fork 子进程,消耗 CPU 和内存)
(2)AOF 持久化(需手动开启)
原理:
  • 记录每一条修改数据的命令(如 SET、HSET、LPUSH),追加到 AOF 文件(文本文件)
  • 重启时,Redis 重新执行 AOF 文件中的命令,恢复数据
配置(redis.conf):
ini 复制代码
appendonly yes  # 开启 AOF 持久化(默认 no)
appendfilename appendonly.aof  # AOF 文件名
dir /usr/local/redis/data  # 文件存放目录(与 RDB 相同)

# AOF 同步策略(重要,影响性能和数据安全性)
appendfsync everysec  # 每秒同步一次(推荐,平衡性能和安全)
# appendfsync always  # 每次命令都同步(最安全,性能最差)
# appendfsync no      # 由操作系统决定同步时机(性能最好,数据丢失风险最高)
AOF 文件重写:
  • 问题:AOF 文件会随命令增多而变大,恢复速度变慢
  • 解决:执行 BGREWRITEAOF 命令,Redis 会创建新的 AOF 文件,只保留恢复数据所需的最小命令集(如多次修改同一个键,只保留最终的 SET 命令)
  • 自动重写:配置文件中设置 auto-aof-rewrite-percentage 100(AOF 文件大小增长 100% 时触发)和 auto-aof-rewrite-min-size 64mb(AOF 文件最小 64MB 才触发)
优缺点:
优点 缺点
数据安全性高:最多丢失 1 秒数据(everysec 策略) AOF 文件体积大,恢复速度比 RDB 慢
支持秒级数据恢复 写入性能比 RDB 低(需记录命令并同步文件)
(3)混合持久化(Redis 4.0+ 支持)
  • 原理:结合 RDB 和 AOF 的优点,AOF 文件中包含 RDB 快照 + 后续的命令日志
  • 配置:aof-use-rdb-preamble yes(默认开启)
  • 优点:恢复速度快(RDB 快照)+ 数据安全(AOF 命令日志)
(4)持久化方案选择
  • 生产环境推荐:混合持久化(Redis 4.0+)
  • 数据安全性要求极高(如金融数据):AOF + 主从复制
  • 数据可以接受少量丢失(如缓存):RDB 或混合持久化

2. 主从复制(解决单点故障)

(1)什么是主从复制?
  • 主节点(master):写入数据,同步数据到从节点
  • 从节点(slave):只读数据,从主节点同步数据
  • 作用:
    • 数据备份:从节点是主节点的副本,防止主节点数据丢失
    • 读写分离:主节点写,从节点读,提高并发处理能力
    • 负载均衡:多个从节点分担读请求,减轻主节点压力
(2)主从复制原理(同步过程)
  1. 从节点连接主节点,发送 PSYNC 命令(部分同步)
  2. 主节点验证从节点的 runid(主节点唯一标识)和 offset(数据偏移量):
    • runid 不匹配(从节点第一次连接或主节点重启):执行全量同步(主节点生成 RDB 快照,发送给从节点,从节点加载快照后,主节点再发送后续命令)
    • runid 匹配但 offset 不一致:执行部分同步(主节点发送从节点缺失的命令日志)
  3. 同步完成后,主节点每执行一条修改命令,都会发送给从节点,保持数据同步
(3)主从配置(简单易懂)
主节点配置:
  • 无需特殊配置,确保主节点开启远程访问(bind 0.0.0.0),设好密码(若需)
从节点配置:
  1. 修改从节点的 redis.conf 文件:
ini 复制代码
slaveof 192.168.0.209 6379  # 主节点 IP 和端口
masterauth 123456           # 主节点密码(若主节点设了密码)
slave-read-only yes         # 从节点只读(默认 yes,生产环境推荐)
  1. 重启从节点,执行 info replication 命令验证:
bash 复制代码
192.168.0.210:6379> info replication
# Replication
role:slave  # 角色为从节点
master_host:192.168.0.209  # 主节点 IP
master_port:6379  # 主节点端口
master_link_status:up  # 连接状态正常
(4)主从复制注意事项
  • 主节点宕机后,从节点不会自动升级为主节点,需手动切换(或用哨兵模式)
  • 主从复制不会阻塞主节点,同步时主节点可正常处理写请求
  • 从节点可以作为其他从节点的主节点(级联复制),适合大规模集群

3. Redis 集群(解决高可用和扩容)

当单节点 Redis 性能不足(如并发过高)或存储容量不够时,需要搭建 Redis 集群(Redis Cluster)。

(1)集群核心特性
  • 分布式存储:数据分散存储在多个节点(主节点),每个主节点负责一部分哈希槽
  • 哈希槽:Redis 集群将 16384 个哈希槽(0-16383)分配给主节点,数据根据 CRC16(key) % 16384 计算哈希槽,存储到对应主节点
  • 高可用:每个主节点至少有一个从节点,主节点宕机后,从节点自动升级为主节点
  • 容错性:集群至少需要 3 个主节点,最多允许 (N-1)/2 个主节点宕机(N 为总主节点数)
(2)集群搭建步骤(CentOS7.5)
前置准备:安装 Ruby 环境(集群管理工具依赖)
bash 复制代码
# 1. 安装 Ruby
yum install -y ruby rubygems
# 2. 安装 Redis Ruby 客户端
gem install redis
# 若报错(Redis 4.0+ 需要 Ruby 2.2+),升级 Ruby:
# 安装 RVM(Ruby 版本管理器)
gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 7D2BAF1CF37B13E2069D6956105BD0E739499BDB
curl -sSL https://get.rvm.io | bash -s stable
source /etc/profile.d/rvm.sh
# 安装 Ruby 2.6.3 并设为默认
rvm install 2.6.3
rvm use 2.6.3 default
# 重新安装 Redis Ruby 客户端
gem install redis
步骤 1:创建集群节点目录
bash 复制代码
# 创建集群根目录
mkdir -p /usr/local/redis-cluster
# 创建 6 个节点目录(3 主 3 从,端口 7001-7006)
mkdir -p /usr/local/redis-cluster/{7001,7002,7003,7004,7005,7006}/{data,logs}
步骤 2:配置每个节点的 redis.conf
bash 复制代码
# 复制基础配置文件到每个节点目录
cp /usr/local/redis/conf/redis.conf /usr/local/redis-cluster/7001/
# 修改 7001 节点配置(其他节点类似,只需改 port 和目录)
vim /usr/local/redis-cluster/7001/redis.conf

关键配置(每个节点需修改 port、dir、logfile):

ini 复制代码
daemonize yes
bind 0.0.0.0
port 7001  # 每个节点端口不同(7001-7006)
dir /usr/local/redis-cluster/7001/data  # 数据目录
logfile /usr/local/redis-cluster/7001/logs/redis.log  # 日志目录
cluster-enabled yes  # 开启集群模式
cluster-config-file nodes-7001.conf  # 集群配置文件(自动生成)
cluster-node-timeout 15000  # 节点超时时间(15秒)
appendonly yes  # 开启 AOF 持久化
requirepass 123456  # 密码
masterauth 123456  # 主节点密码(从节点同步用)
步骤 3:启动所有节点
bash 复制代码
# 编写启动脚本(cluster-start.sh)
#!/bin/bash
for port in {7001..7006}; do
  /usr/local/redis/bin/redis-server /usr/local/redis-cluster/$port/redis.conf
done
# 赋予执行权限并运行
chmod +x cluster-start.sh
./cluster-start.sh
# 验证启动成功(查看 6 个 Redis 进程)
ps aux | grep redis-server
步骤 4:创建集群
bash 复制代码
# 进入 Redis 安装目录,执行集群创建命令
cd /usr/local/redis/bin
./redis-trib.rb create --replicas 1 192.168.0.209:7001 192.168.0.209:7002 192.168.0.209:7003 192.168.0.209:7004 192.168.0.209:7005 192.168.0.209:7006
  • --replicas 1:每个主节点分配 1 个从节点
  • 输入 yes 确认哈希槽分配
  • 成功后,集群会自动分配主从关系和哈希槽
步骤 5:验证集群
bash 复制代码
# 连接集群(-c 表示集群模式)
./redis-cli -c -h 192.168.0.209 -p 7001 -a 123456
# 查看集群信息
192.168.0.209:7001> cluster info
cluster_state:ok  # 集群状态正常
cluster_slots_assigned:16384  # 所有哈希槽已分配
# 查看节点信息
192.168.0.209:7001> cluster nodes
(3)Java 操作 Redis 集群(JedisCluster)
java 复制代码
import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.JedisCluster;
import redis.clients.jedis.JedisPoolConfig;

import java.util.HashSet;
import java.util.Set;

public class JedisClusterTest {
    public static void main(String[] args) {
        // 1. 配置连接池
        JedisPoolConfig poolConfig = new JedisPoolConfig();
        poolConfig.setMaxTotal(50);
        poolConfig.setMaxIdle(10);

        // 2. 集群节点集合
        Set<HostAndPort> nodes = new HashSet<>();
        nodes.add(new HostAndPort("192.168.0.209", 7001));
        nodes.add(new HostAndPort("192.168.0.209", 7002));
        nodes.add(new HostAndPort("192.168.0.209", 7003));
        nodes.add(new HostAndPort("192.168.0.209", 7004));
        nodes.add(new HostAndPort("192.168.0.209", 7005));
        nodes.add(new HostAndPort("192.168.0.209", 7006));

        // 3. 创建 JedisCluster 实例(自动负载均衡和故障转移)
        JedisCluster jedisCluster = new JedisCluster(nodes, 10000, 5000, 3, "123456", poolConfig);
        // 参数说明:节点集合、连接超时、读取超时、重试次数、密码、连接池配置

        // 4. 操作集群
        jedisCluster.set("cluster:name", "redis-cluster");
        String name = jedisCluster.get("cluster:name");
        System.out.println(name); // 输出:redis-cluster

        // 5. 关闭集群连接(释放资源)
        jedisCluster.close();
    }
}

十、Redis 常见配置参数说明(生产环境必备)

以下是 redis.conf 中常用的配置参数,根据实际需求调整:

配置项 默认值 说明 生产环境建议
daemonize no 是否后台运行 yes
bind 127.0.0.1 绑定的 IP 地址(0.0.0.0 允许所有地址访问) 0.0.0.0(开发/测试),具体 IP(生产)
port 6379 服务端口 自定义(如 6380),避免默认端口暴露
requirepass 访问密码 设复杂密码(如 16 位字母+数字+符号)
timeout 0 客户端连接超时时间(秒,0 表示无超时) 300(5 分钟)
loglevel notice 日志级别(debug/verbose/notice/warning) notice(生产),debug(开发)
logfile 日志文件路径 /usr/local/redis/logs/redis.log
dir ./ 数据存储目录(RDB/AOF 文件) /usr/local/redis/data
maxclients 10000 最大客户端连接数 根据服务器性能调整(如 5000)
maxmemory 0 最大使用内存(0 表示无限制) 设为服务器内存的 70%-80%(如 8GB 内存设 6GB)
maxmemory-policy volatile-lru 内存满时的淘汰策略(如 LRU 淘汰最近最少使用的键) volatile-lru(优先淘汰过期键)
appendonly no 是否开启 AOF 持久化 yes(生产)
appendfsync everysec AOF 同步策略 everysec
cluster-enabled no 是否开启集群模式 yes(集群环境)
cluster-node-timeout 15000 集群节点超时时间(毫秒) 15000(15 秒)

十一、Redis 常见问题与解决方案(实战必备)

1. 缓存穿透(查询不存在的键,导致请求直达数据库)

  • 原因:恶意请求查询不存在的键(如用户 ID=-1),Redis 缓存未命中,每次都访问数据库,导致数据库压力过大
  • 解决方案:
    • 缓存空值:查询不存在的键时,缓存空值(如 ""),设置短期过期时间(如 5 分钟)
    • 布隆过滤器:在 Redis 前拦截不存在的键,避免请求直达 Redis 和数据库

2. 缓存击穿(热点键过期,大量请求直达数据库)

  • 原因:某个热点键(如热门商品 ID)过期,此时大量请求同时访问,Redis 未命中,全部直达数据库
  • 解决方案:
    • 热点键永不过期:不设置过期时间,定期后台更新
    • 互斥锁:第一个请求查询数据库时,加锁(如 Redis SETNX),其他请求等待锁释放后从缓存获取数据
    • 预热缓存:系统启动时,提前加载热点键到缓存

3. 缓存雪崩(大量键同时过期,导致数据库压力骤增)

  • 原因:缓存中大量键设置了相同的过期时间,到期后同时失效,大量请求直达数据库
  • 解决方案:
    • 过期时间加随机值:如原本过期时间 1 小时,改为 1 小时 ± 10 分钟,避免同时过期
    • 分层缓存:不同层级的缓存设置不同的过期时间(如本地缓存 + Redis 缓存)
    • 服务熔断:数据库压力过大时,暂时拒绝请求,返回默认值

4. Redis 性能优化

  • 避免使用 keys * 命令(阻塞服务),用 scan 命令分批遍历键
  • 合理选择数据类型:如存储对象用 hash,不用 string 序列化
  • 控制键的大小:键名尽量简短(如 user:1001 而非 user_info_1001),值避免过大(如超过 100KB)
  • 开启持久化时,选择合适的同步策略(如 AOF everysec),避免影响性能
  • 集群环境下,均匀分配哈希槽,避免某个主节点负载过高

5. Redis 未授权访问漏洞

  • 风险:未设置密码、绑定 0.0.0.0,导致恶意攻击者访问 Redis,篡改数据或植入木马
  • 解决方案:
    • 设复杂访问密码(requirepass
    • 绑定具体的 IP 地址(bind 192.168.0.209),限制访问来源
    • 关闭不必要的命令(如 configflushall),通过 rename-command 重命名
相关推荐
罗汉松驻扎的工作基地2 小时前
sql server开启远程(适用于2014、2017和2008R2)
运维·服务器·数据库
myloveasuka2 小时前
汉明编码的最小距离、汉明距离
服务器·数据库·笔记·算法·计算机组成原理
Elastic 中国社区官方博客2 小时前
Elasticsearch:使用 `best_compression` 提升搜索性能
大数据·运维·数据库·elasticsearch·搜索引擎·全文检索
羱滒2 小时前
Docker Compose + Nginx + 后端服务运行环境搭建全流程指南(redis、mongdb、nginx、nacos-registry)
redis·nginx·docker·docker-compose
先跑起来再说2 小时前
Redis Stream 深入理解:它到底解决了什么问题
数据库·redis·缓存
小杰帅气2 小时前
Ext文件系统
数据库
Forget_85502 小时前
RHCE第八章:防火墙
linux·服务器·数据库
小北方城市网2 小时前
Spring Cloud Gateway 生产级实践:高可用架构、灰度发布与故障排查
spring boot·redis·分布式·缓存·架构·wpf