引言
在微服务架构中,服务拆分导致系统复杂度呈指数级增长。据统计,高并发场景下直接访问数据库的请求响应时间可能增加300%-500%,而合理的缓存设计可将接口响应速度提升5-10倍。缓存作为分布式系统的"速度调节器",已成为现代微服务架构中不可或缺的关键组件。
一、 为什么需要缓存?
-
性能加速:将热点数据存储在内存中,访问速度比传统机械硬盘快10^5倍
-
流量削峰:抵御突发流量,某电商大促期间缓存承担了92%的请求
-
成本优化:1GB内存成本约0.3元/小时,而相同容量SSB的成本是其3倍
-
高可用保障:在数据库故障时提供降级保护
二、 主流缓存方案对比
缓存类型 | 吞吐量 | 数据结构 | 持久化 | 集群方案 | 适用场景 |
---|---|---|---|---|---|
Redis | 100,000 QPS | 丰富 | 支持 | Codis/Cluster | 分布式缓存、会话共享 |
Memcached | 200,000 QPS | 单一 | 不支持 | 一致性Hash | 简单KV缓存 |
-
数据类型支持:
- Redis支持多种数据结构,包括String、List、Set、Zset、Hash、Bitmap等,适合需要复杂数据操作的场景。
- Memcached仅支持简单的key-value数据类型,适用于需要大量简单键值存储的场景。
-
持久化:
- Redis支持RDB和AOF两种持久化方式,可以配置数据备份和恢复,适合需要数据持久化的场景。
- Memcached不支持数据持久化,重启后数据会丢失,适合对数据持久性要求不高的场景。
-
性能和稳定性:
- Redis在6.0版本之前使用单线程模型,适合IO密集型操作,但在处理计算密集型任务时可能会成为瓶颈。6.0版本后支持多线程,提升了处理复杂任务的能力1。
- Memcached采用多线程模型,适合多核处理,但在某些操作上需要加锁,可能会影响性能。
三、 Java微服务缓存选型建议
- Redis适用于需要复杂数据结构、高可用性和数据持久化的场景。例如,需要存储复杂对象、支持高并发读写、需要数据备份和恢复的场景3。
- Memcached适用于纯key-value存储、高并发访问且对数据一致性要求不高的场景。例如,大规模的在线交易系统、需要快速读写大量数据的场景3。
在Java生态中,Redis以63.7%的采用率位居榜首(2023年JVM生态报告),其优势在于:
-
支持String/Hash/List等6种数据结构
-
提供RDB+AOF双重持久化方案
-
原生支持Cluster集群模式
-
丰富的扩展模块(RediSearch/BloomFilter等)
四、redis搭建
1. 单机模式部署
- linux安装gcc(c,c++编译器)用来编译redis
yum install gcc
- 下载redis到opt目錄
cd /opt
wget http://download.redis.io/releases/redis-6.2.6.tar.gz
-- 这个网络不通的话 去
wget https://gitee.com/Hellohanli/redis/blob/master/redis-6.2.6.tar.gz
- 解压压缩包
tar -zxvf redis-6.2.6.tar.gz
- 编译
# 在解压目录下执行
make
- 安装到指定目录
make PREFIX=/usr/local/redis install
- 增加目录
#配置文件存放路径
mkdir /usr/local/redis/conf
#数据存放目录
mkdir /usr/local/redis/data
#日志存放目录
mkdir /usr/local/redis/logs
#拷贝 配置文件到安装目录
cp /opt/redis-6.2.2/redis.conf /usr/local/redis/conf/redis.conf
- 修改安装目录下的配置文件,修改指定的内容即可,
vi /usr/local/redis/conf/redis.conf
#用于指定本机网卡对应的IP地址,如果是默认 127.0.0.1 的话,则只有本机可以访问。如果注释掉则表示客户端可以通过本机所有网卡进来,如果这样设置,且没有设置 requirepass 密码, redis 默认开始了保护模式 protected-mode,则其它客户端都可以连接,但是一发送命令就会失败
bind 192.168.86.140
#数据存放目录
dir /usr/local/redis/data
#是否后台运行
daemonize yes
#日志文件
logfile "/usr/local/redis/logs/redis.log"
#设置客户端连接后进行任何其他操作前需要使用的密码 requirepass pass
requirepass pass
- 启动 redis-server
cd /usr/local/redis/bin
./redis-server ../conf/redis.conf
- 检查
ps -ef | grep redis
./redis-cli -h 127.0.0.1 -p 6379
2.集群模式部署
2.1. 环境准备
-
三台服务器:假设三台服务器的IP分别为:
-
192.168.1.101
(主节点) -
192.168.1.102
(从节点1) -
192.168.1.103
(从节点2)
-
-
Redis版本:Redis 6.x 或更高版本。
-
端口规划:
-
Redis 主从节点使用
6379
端口。 -
哨兵节点使用
26379
端口。
-
2.2. 安装Redis
在三台服务器上分别安装Redis。
2.2.1 安装依赖
# apt:是用于 Debian、Ubuntu # yum:是用于 Red Hat、CentOS、Fedora 根据系统选择 sudo apt-get update sudo apt-get install build-essential tcl
2.2.2 下载并编译Redis
# 下载压缩包 wget http://download.redis.io/releases/redis-6.2.6.tar.gz # 解压压缩包 tar xzf redis-6.2.6.tar.gz # 进入目录 cd redis-6.2.6 #编译 make #安装 sudo make install
2.3. 配置主从节点
2.3.1 主节点配置(192.168.1.101)
编辑配置文件 /etc/redis/6379.conf
:
# 用于指定本机网卡对应的IP地址 bind 192.168.1.101 port 6379 #是否后台运行 daemonize yes #日志文件 logfile "/var/log/redis/redis.log" dir /var/lib/redis
启动Redis:
redis-server /etc/redis/6379.conf
2.3.2 从节点1配置(192.168.1.102)
编辑配置文件 /etc/redis/6379.conf
:
bind 192.168.1.102 port 6379 daemonize yes logfile "/var/log/redis/redis.log" dir /var/lib/redis slaveof 192.168.1.101 6379
启动Redis:
redis-server /etc/redis/6379.conf
2.3.3 从节点2配置(192.168.1.103)
编辑配置文件 /etc/redis/6379.conf
:
bind 192.168.1.103 port 6379 daemonize yes logfile "/var/log/redis/redis.log" dir /var/lib/redis slaveof 192.168.1.101 6379
启动Redis:
redis-server /etc/redis/6379.conf
2.4. 配置哨兵节点
在三台服务器上分别配置哨兵。
2.4.1 哨兵配置文件(以192.168.1.101为例)
创建哨兵配置文件 /etc/redis/sentinel.conf
:
port 26379 #设置为后台运行 daemonize yes #日志存放目录 logfile "/var/log/redis/sentinel.log" #数据存放目录 dir /var/lib/redis #监控名为 mymaster 的主节点,仲裁参数为 2,一般是哨兵个数的一半+1(类似于zookeeper的过半写成功机制) sentinel monitor mymaster 192.168.1.101 6379 2 #主机多长时间连接不上,哨兵就判定他挂了,单位毫秒 sentinel down-after-milliseconds mymaster 5000 #这个命令用于设置在主节点被判定为不可用之后,进行故障转移所需的超时时间。 sentinel failover-timeout mymaster 10000 #这个命令用于设置在进行故障转移时,同时进行同步的从节点数量1表示在进行故障转移时只允许一个从节点进行同步 sentinel parallel-syncs mymaster 1
-
mymaster
:主节点名称。 -
2
:至少需要2个哨兵同意才能触发故障转移。
2.4.2 启动哨兵
在三台服务器上分别启动哨兵:
redis-sentinel /etc/redis/sentinel.conf
2.5. 验证集群状态
2.5.1 检查主从复制
在主节点(192.168.1.101)上执行:
redis-cli -h 192.168.1.101 -p 6379 info replication
输出应显示两个从节点(192.168.1.102
和 192.168.1.103
)。
2.5.2 检查哨兵状态
在任意一台服务器上执行:
redis-cli -h 192.168.1.101 -p 26379 info sentinel
输出应显示哨兵监控的主节点和从节点信息。
2.6. 测试故障转移
2.6.1 模拟主节点宕机
在主节点(192.168.1.101)上停止Redis:
redis-cli -h 192.168.1.101 -p 6379 shutdown
2.6.2 检查故障转移
在哨兵节点上执行:
redis-cli -h 192.168.1.102 -p 26379 sentinel get-master-addr-by-name mymaster
输出应显示新的主节点(如 192.168.1.102
或 192.168.1.103
)。
2.6.3 恢复原主节点
重新启动原主节点(192.168.1.101):
redis-server /etc/redis/6379.conf
它会自动成为新主节点的从节点。
2.7. 总结
-
主从复制:实现数据同步。
-
哨兵机制:实现高可用和自动故障转移。
-
故障转移:主节点宕机后,哨兵会自动选举新的主节点。
通过以上步骤,你已经成功部署了一个Redis集群(一主两从三哨兵)。
五、項目使用
一主一从一哨兵的部署架构虽然在简单的测试环境中可能会使用,但在生产环境中不太合理。这种架构存在单点故障的风险,因为如果主节点或哨兵节点发生故障,整个系统可能会变得不可用。
在生产环境中,建议至少部署三个或以上的哨兵实例,并且主节点和从节点也最好是多个。这样可以增加系统的可用性和容错能力,确保系统在面临故障时仍然能够继续提供服务。
以下是一个更具可靠性的Redis Sentinel部署示例:
- 3个以上的哨兵实例:确保有足够的哨兵实例来达成共识,以便进行故障检测和自动故障转移。
- 多个主节点和从节点:通过部署多个主节点和从节点,可以提高系统的可扩展性和容错能力。
- 合理的故障转移配置:设置合理的故障转移超时时间、quorum值和故障转移执行的超时时间,以确保系统能够及时做出正确的故障转移决策。
六、问题解决
1.主从复制的时候,master 正常写入但是 salve 读取不到
- 检查一下 master 的 bind 设置(用于指定本机网卡对应的IP地址 )
- 检查一下 slave 的 masterauth 是否正确
- 服务器的端口是否开通,防火墙是否拦截了
2. Connection Refused
- 错误描述:客户端连接 Redis 时,出现 Connection Refused 报错。
- 问题原因:
Redis服务没有启动。
Redis配置文件 redis.conf 中的 bind 或 protected-mode 设置导致连接被拒绝。
Redis设置了密码,但客户端未提供。
- 解决方法:
使用 redis-server 启动Redis服务,确保其正常运行。
检查 redis.conf 文件中 bind 配置,确保服务绑定到正确的IP地址(例如可以设置为 0.0.0.0 以允许所有IP访问)。
如果Redis启用了密码,客户端连接时需要提供密码:redis-cli -a your_password。
3.频繁报OOM
当redis占用内存超过或持平了服务器的内存上限,就会出现这个错误,新手尤其容易犯这类错误。正确的做法是设置缓存上限,并且搭配内存回收策略使用,下附常用回收策略:
#设置最大缓存,单位bytes,下面这条是设置最大缓存1G maxmemory 1073741824 #回收策略 #默认,到达上限时抛出异常 maxmemory-policy noeviction #淘汰使用频率最低的键,包括持久键 maxmemory-policy allkeys-lru #淘汰使用频率最低的键,不包括持久键 maxmemory-policy volatile-lru #随机淘汰,包括持久键 maxmemory-policy allkeys-random #随机淘汰,不包括持久键 maxmemory-policy volatile-random #淘汰有过期时间的键,距离原本过期时间最近的优先 maxmemory-policy volatile-ttl
4.redis设置后数据库压力不减
- 缓存服务如果没有任何异常,那么有可能是缓存穿透。
- 什么是缓存穿透呢?简单来说就是有人老是去请求不存在的数据。
- 那什么是不存在的数据?假设数据库中用户id是从10000开始的,现在有人频繁去请求用户id为500的数据,这个id为500的数据就是不存在的数据。因为它不可能存在于数据库。
- 这时候会怎么样呢?系统先查询缓存,查不到所以去查了数据库,也没有查到,所以直接返回空。但是下一次请求还是要走一遍缓存+数据库。这个时候只要一段简单的脚本就可以对数据库造成压力。
- 解决方法很简单:当数据库查不到数据时,先设置一个null键入缓存,之后就任他刷。一般情况下会给这个null值设置一个过期时间。
5.redis主从同步延迟
在Redis中,主从同步延迟是一个常见的问题,特别是在高负载或网络延迟的环境下。这种延迟可能会导致数据在不同节点之间的不一致。以下是一些解决和优化Redis主从同步延迟的方法:
1. 优化网络环境
-
确保网络带宽和低延迟:使用高速网络连接,并尽量减少网络延迟。
-
使用更近的物理位置:如果可能,将主节点和从节点部署在地理位置更近的地方,以减少网络延迟。
2. 调整Redis配置
-
repl-backlog-size:增加复制积压缓冲区的大小。这个缓冲区用于主从节点断线重连后的数据同步。例如,设置
repl-backlog-size
为更大的值可以减少重新同步的需求。
repl-backlog-size 1073741824 # 例如设置为1GB
-
repl-disable-tcp-nodelay:设置为
no
可以减少数据在网络上的延迟,但可能会轻微增加CPU使用率。repl-disable-tcp-nodelay no
3. 减少主节点负载
-
优化查询:确保对Redis的查询是高效的,避免在大批量数据上执行复杂操作。
-
使用更快的硬件:增加主节点的CPU和内存资源可以显著提高处理速度。
-
读写分离:对于读操作非常频繁的应用,可以考虑设置多个从节点来分担读请求,从而减轻主节点的压力。
4. 使用更高效的同步策略
-
增量复制:确保使用增量复制而不是全量复制,这可以通过调整
slave-serve-stale-data
配置实现。设置为yes
可以在从节点落后时继续提供服务。slave-serve-stale-data yes