Redis 笔记 04:集群、缓存、最佳实践
2022/7/4 2:22:27
本文主要是介绍Redis 笔记 04:集群、缓存、最佳实践,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
Redis 笔记 04:集群、缓存、最佳实践
这是本人根据黑马视频学习 Redis 的相关笔记,系列文章导航:《Redis设计与实现》笔记与汇总
集群架构
单点 Redis 的问题
持久化
RDB
AOF
AOF
: Append Only File。Redis 处理的每一个写命令都会记录在 AOF 文件中,可以看作是命令日志文件。
两者对比
主从
搭建主从架构
目的是在同一台虚拟机中创建 3 个 redis 实例,模拟主从集群,如下:
IP | PORT | 角色 |
---|---|---|
192.168.137.112 | 7001 | master |
192.168.137.112 | 7002 | slave |
192.168.137.112 | 7003 | slave |
-
创建三个目录,分别存放一份配置文件,例如:
├── 7001 │ └── redis.conf ├── 7002 │ └── redis.conf └── 7003 └── redis.conf 3 directories, 3 files
-
修改每个配置文件的端口和工作目录,可以用 sed 快速完成:
sed -i -e 's/6379/7001/g' -e 's/dir .\//dir \/tmp\/7001\//g' 7001/redis.conf sed -i -e 's/6379/7002/g' -e 's/dir .\//dir \/tmp\/7002\//g' 7002/redis.conf sed -i -e 's/6379/7003/g' -e 's/dir .\//dir \/tmp\/7003\//g' 7003/redis.conf
-
修改每个实例的声明 IP:
虚拟机本身有多个 IP,为了避免将来混乱,我们需要在 redis.conf 文件中指定每一个实例的绑定 ip 信息,格式如下:# redis实例的声明 IP replica-announce-ip 192.168.150.101
每个目录都要改,我们一键完成修改(在/tmp 目录执行下列命令):
# 逐一执行 sed -i '1a replica-announce-ip 192.168.137.112' 7001/redis.conf sed -i '1a replica-announce-ip 192.168.137.112' 7002/redis.conf sed -i '1a replica-announce-ip 192.168.137.112' 7003/redis.conf # 或者一键修改 printf '%s\n' 7001 7002 7003 | xargs -I{} -t sed -i '1a replica-announce-ip 192.168.137.112' {}/redis.conf
-
如果主节点设置了密码,则要在从节点的配置文件中添加如下配置:
masterauth [主节点密码]
当然练习的时候也可以把密码都删除了
-
启动:
# 第1个 redis-server 7001/redis.conf # 第2个 redis-server 7002/redis.conf # 第3个 redis-server 7003/redis.conf
-
设置主从关系:
在从节点上:
slaveof <master-ip> <master-port> # 或 5.0 之后 replicaof <master-ip> <master-port>
-
一键暂停:
printf '%s\n' 7001 7002 7003 | xargs -I{} -t redis-cli -p {} shutdown
可以看一下上图中的标记,日志记录了两者之间进行同步的信息
全量同步
主从第一次同步为 全量同步:
Master 如何判断 Slave 是否是第一次来同步数据?
有几个概念,可以作为判断依据:
- Replication Id:简称 replid,是数据集的标记,id 一致则说明是同一数据集。每一个 master 都有唯一的 replid,slave 则会继承 master 节点的 replid
- offset:偏移量,随着记录在 repl_baklog 中的数据增多而逐渐增大。slave 完成同步时也会记录当前同步的 offset。如果 slave 的 offset 小于 master 的 offset,说明 slave 数据落后于 master,需要更新。
master 判断一个节点是否是第一次同步的依据,就是看 replid 是否一致。
增量同步
同步的优化
哨兵
理论
哨兵的作用如下:
- 监控:Sentinel 会不断检查您的 master 和 slave 是否按预期工作
- 自动故障恢复:如果 master 故障,Sentinel 会将一个 slave 提升为 master。当故障实例恢复后也以新的 master 为主
- 通知:Sentinel 充当 Redis 客户端的服务发现来源,当集群发生故障转移时,会将最新信息推送给 Redis 的客户端
主观下线和客观下线:
Sentinel 基于心跳机制监测服务状态,每隔 1 秒向集群的每个实例发送 ping 命令:
•主观下线:如果某 sentinel 节点发现某实例未在规定时间响应,则认为该实例主观下线。
•客观下线:若超过指定数量(quorum)的 sentinel 都认为该实例主观下线,则该实例客观下线。quorum 值最好超过 Sentinel 实例数量的一半。
如何选举新的 master?
如何实现故障转移?
搭建哨兵集群
三个 sentinel 实例信息如下:
节点 | IP | PORT |
---|---|---|
s1 | 192.168.150.101 | 27001 |
s2 | 192.168.150.101 | 27002 |
s3 | 192.168.150.101 | 27003 |
-
创建目录
# 进入/tmp目录 cd /tmp # 创建目录 mkdir s1 s2 s3
-
在每个目录下创建配置文件,注意修改端口号(第一行)和文件夹路径(最后一行):
port 27001 sentinel announce-ip 192.168.150.101 sentinel monitor mymaster 192.168.150.101 7001 2 sentinel down-after-milliseconds mymaster 5000 sentinel failover-timeout mymaster 60000 dir "/tmp/s1"
-
逐个启动
redis-sentinel sentinel.conf
测试
- 先开 3 个终端,用来启动 3 个 redis
- 再开 3 个终端,用来启动 3 个 sentinel
- 把 7001 的 redis 挂掉
- 观察
- 再把 7001 恢复
sentinel
的日志:
redis-server
原来的从节点的日志:
7001 恢复后,自己变成从节点
RedisTemplate使用
分片集群
搭建分片集群
这里我们会在同一台虚拟机中开启 6 个 redis 实例,模拟分片集群,信息如下:
IP | PORT | 角色 |
---|---|---|
192.168.150.101 | 7001 | master |
192.168.150.101 | 7002 | master |
192.168.150.101 | 7003 | master |
192.168.150.101 | 8001 | slave |
192.168.150.101 | 8002 | slave |
192.168.150.101 | 8003 | slave |
-
创建文件夹
# 进入/tmp目录 cd /tmp # 删除旧的,避免配置干扰 rm -rf 7001 7002 7003 # 创建目录 mkdir 7001 7002 7003 8001 8002 8003
-
准备一个配置文件
port 6379 # 开启集群功能 cluster-enabled yes # 集群的配置文件名称,不需要我们创建,由redis自己维护 cluster-config-file /tmp/6379/nodes.conf # 节点心跳失败的超时时间 cluster-node-timeout 5000 # 持久化文件存放目录 dir /tmp/6379 # 绑定地址 bind 0.0.0.0 # 让redis后台运行 daemonize yes # 注册的实例ip replica-announce-ip 192.168.150.101 # 保护模式 protected-mode no # 数据库数量 databases 1 # 日志 logfile /tmp/6379/run.log
-
将这个文件拷贝到每个目录下
# 进入/tmp目录 cd /tmp # 执行拷贝 echo 7001 7002 7003 8001 8002 8003 | xargs -t -n 1 cp redis.conf
-
修改每个目录下的 redis.conf,将其中的 6379 修改为与所在目录一致
# 进入/tmp目录 cd /tmp # 修改配置文件 printf '%s\n' 7001 7002 7003 8001 8002 8003 | xargs -I{} -t sed -i 's/6379/{}/g' {}/redis.conf
-
启动
# 进入/tmp目录 cd /tmp # 一键启动所有服务 printf '%s\n' 7001 7002 7003 8001 8002 8003 | xargs -I{} -t redis-server {}/redis.conf
-
创建集群
redis-cli --cluster create --cluster-replicas 1 192.168.150.101:7001 192.168.150.101:7002 192.168.150.101:7003 192.168.150.101:8001 192.168.150.101:8002 192.168.150.101:8003
redis-cli --cluster
或者./redis-trib.rb
:代表集群操作命令create
:代表是创建集群--replicas 1
或者--cluster-replicas 1
:指定集群中每个 master 的副本个数为 1,此时节点总数 ÷ (replicas + 1)
得到的就是 master 的数量。因此节点列表中的前 n 个就是 master,其它节点都是 slave 节点,随机分配到不同 master
-
查看集群状态
redis-cli -p 7001 cluster nodes
-
测试(集群测试时,要添加上
-c
参数)redis-cli -c -p 7001
-
关闭
printf '%s\n' 7001 7002 7003 8001 8002 8003 | xargs -I{} -t redis-cli -p {} shutdown
散列插槽
原理方面可以参考 一致性哈希算法
Redis 会把每一个 master 节点映射到 0~16383 共 16384 个插槽(hash slot)上,查看集群信息时就能看到:
Redis 会根据 key 的有效部分计算插槽值,分两种情况:
- key 中包含"{}",且“{}”中至少包含 1 个字符,“{}”中的部分是有效部分
- key 中不包含“{}”,整个 key 都是有效部分
案例:
>SET num 10那么就根据
num
计算,利用CRC16算法得到一个hash值,然后对16384取余,得到的结果就是slot值,然后根据slot值确定要把数据插入到哪个 Redis 中
集群伸缩
主要是增加新的节点,给它分配槽,或者删除节点的操作。
添加节点:
redis-cli --cluster add-node 192.168.150.101:7004 192.168.150.101:7001
通过命令查看集群状态:
redis-cli -p 7001 cluster nodes
默认是 master 节点,不分配插槽,因此没有任何数据可以存储到 7004 上
转移插槽:
[root@localhost] redis-cli --cluster reshard 192.168.137.112:7001
然后依次按照提示输入:
- 要移动多少
- 哪个 node 来接收(id)
- 让哪个 node 来提供
- 确认要转移吗
故障转移
- 当一个 master 宕机后,slave 节点会代替他成为 master
- 宕机后的节点启动后变成 slave 节点
手动故障转移:
利用cluster failover
命令可以手动让集群中的某个 master 宕机,切换到执行 cluster failover 命令的这个 slave 节点,实现无感知的数据迁移。
这种 failover 命令可以指定三种模式:
- 缺省:默认的流程,如图 1~6 歩
- force:省略了对 offset 的一致性校验
- takeover:直接执行第 5 歩,忽略数据一致性、忽略 master 状态和其它 master 的意见
例如,让 slave 变成 master,可以在 master 上执行:
RedisTemplate访问
RedisTemplate 底层同样基于 lettuce 实现了分片集群的支持,而使用的步骤与哨兵模式基本一致:
1)引入 redis 的 starter 依赖
2)配置分片集群地址
3)配置读写分离
与哨兵模式相比,其中只有分片集群的配置方式略有差异,如下:
spring: redis: cluster: nodes: - 192.168.150.101:7001 - 192.168.150.101:7002 - 192.168.150.101:7003 - 192.168.150.101:8001 - 192.168.150.101:8002 - 192.168.150.101:8003
多级缓存
JVM进程缓存
项目案例
导入数据库
创建 MySQL 数据库
docker run \ -p 3306:3306 \ --name mysql \ -v $PWD/conf:/etc/mysql/conf.d \ -v $PWD/logs:/logs \ -v $PWD/data:/var/lib/mysql \ -e MYSQL_ROOT_PASSWORD=123 \ --privileged \ -d \ mysql
在/tmp/mysql/conf 目录添加一个 my.cnf 文件,作为 mysql 的配置文件:
# 创建文件 touch /tmp/mysql/conf/my.cnf
文件的内容如下:
[mysqld] skip-name-resolve character_set_server=utf8 datadir=/var/lib/mysql server-id=1000
重新启动一下 mysql
导入 SQL 数据
导入项目代码(略)
导入前端代码(略)
认识Caffeine
简单使用的案例:
public class CaffeineTest { @Test void testBasicOps() { Cache<String, String> cache = Caffeine.newBuilder().build(); cache.put("CAT", "TOM"); String gf = cache.getIfPresent("CAT"); System.out.println(gf); String dog = cache.get("DOG", key -> "WangWang"); System.out.println(dog); } }
三种缓存驱逐策略:
实现进程缓存
config/CaffeineConfig
:
@Configuration public class CaffeineConfig { @Bean public Cache<Long, Item> itemCache() { return Caffeine.newBuilder() .initialCapacity(100) .maximumSize(10_000) .build(); } @Bean public Cache<Long, ItemStock> stockCache() { return Caffeine.newBuilder() .initialCapacity(100) .maximumSize(10_000) .build(); } }
Controller:
:
@Autowired private Cache<Long, Item> itemCache; @Autowired private Cache<Long, ItemStock> stockCache; @GetMapping("/{id}") public Item findById(@PathVariable("id") Long id){ return itemCache.get(id, key -> itemService.query() .ne("status", 3).eq("id", key) .one()); } @GetMapping("/stock/{id}") public ItemStock findStockById(@PathVariable("id") Long id){ return stockCache.get(id, key -> stockService.getById(key)); }
OpenResty
Lua语法
此部分可以参考:Lua基础教程笔记
安装
官网:OpenResty® - 中文官方站
OpenResty® 的目标是让你的Web服务直接跑在 Nginx 服务内部,充分利用 Nginx 的非阻塞 I/O 模型,不仅仅对 HTTP 客户端请求,甚至于对远程后端诸如 MySQL、PostgreSQL、Memcached 以及 Redis 等都进行一致的高性能响应。
-
安装开发库
yum install -y pcre-devel openssl-devel gcc --skip-broken
-
安装 OpenResty 仓库
# 如果以前安装过 yum-utils 则跳过 # yum install -y yum-utils yum-config-manager --add-repo https://openresty.org/package/centos/openresty.repo
-
安装
yum install -y openresty
-
安装 opm 工具
yum install -y openresty-opm
-
添加到环境变量
vi /etc/profile # 添加如下内容 export NGINX_HOME=/usr/local/openresty/nginx export PATH=${NGINX_HOME}/sbin:$PATH source /etc/profile
安装完成后:
其实是在 NGINX 的基础上做了增强,可以用之前学的 nginx 命令对其进行控制
这里有一份简化的配置文件:
#user nobody; worker_processes 1; error_log logs/error.log; events { worker_connections 1024; } http { include mime.types; default_type application/octet-stream; sendfile on; keepalive_timeout 65; server { listen 8081; server_name localhost; location / { root html; index index.html index.htm; } error_page 500 502 503 504 /50x.html; location = /50x.html { root html; } } }
快速入门
让 OpenResty 可以处理请求:
nginx.conf
配置文件:
自定义脚本 nginx/lua/item.lua
:
ngx.say('{"name":"商品", "price":"100"}')
如果运行nginx失败,参考 解决 openresty Nginx 重启报错
请求参数处理
这里拿第一种情况做一个测试:
nginx.conf
:
location ~ /api/item/(\d+) { default_type application/json; content_by_lua_file lua/item.lua; }
item.lua
:
-- 获取路径参数 local id = ngx.var[1] ngx.say('{"name":'.. id ..', "price":"100"}')
查询Tomcat
关于如何检查防火墙的状态,可以用
nmap
等工具关于如何关闭防火墙,可以参考 打开或关闭 Microsoft Defender 防火墙
首先修改一下 nginx 的配置:
location /item { proxy_pass http://192.168.137.1:8081; }
封装一个方法,用来进行通用的网络请求的发送:
openresty/lualib/common.js
:
-- 封装函数,发送http请求,并解析响应 local function read_http(path, params) local resp = ngx.location.capture(path,{ method = ngx.HTTP_GET, args = params, }) if not resp then -- 记录错误信息,返回404 ngx.log(ngx.ERR, "http not found, path: ", path , ", args: ", args) ngx.exit(404) end return resp.body end -- 将方法导出 local _M = { read_http = read_http } return _M
然后编写具体的逻辑代码:
-- 导入 common 函数库 local common = require('common') local read_http = common.read_http -- 导入cjson模块 local cjson = require('cjson') -- 获取路径参数 local id = ngx.var[1] -- 查询商品信息 local itemJSON = read_http("/item/".. id, nil) -- 查询库存信息 local stockJSON = read_http("/item/stock/"..id, nil) -- JSON 转换 lua 的 table local item = cjson.decode(itemJSON) local stock = cjson.decode(stockJSON) -- 组合数据 item.stock = stock.stock item.sold = stock.sold -- item 序列化为 json 返回结果 ngx.say(cjson.encode(item))
Tomcat 负载均衡:
实际应用场景中一般是用集群模式启动,所以我们模拟多台 Tomcat:
配置 nginx.conf
:
(部分)
upstream tomcat-cluster { hash $request_uri; server 192.168.137.1:8081; server 192.168.137.1:8082; } server { location /item { proxy_pass http://tomcat-cluster; } }
Redis缓存预热
目的:让 OpenResty 优先查询 redis,然后再查询 tomcat
-
启动 redis (视频这里用了 docker 运行 redis)
docker run --name redis -p 6379:6379 -d redis redis-server --appendonly yes
-
在项目中引入依赖:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
-
配置 redis 地址
spring: redis: host: 192.168.137.112 password: abc123
-
编写初始化类
实现了InitializingBean
的类会在属性注入后执行,以实现预热的效果@Component public class RedisHandler implements InitializingBean { @Autowired private StringRedisTemplate redisTemplate; @Autowired private IItemService itemService; @Autowired private IItemStockService stockService; @Autowired private static final ObjectMapper MAPPER = new ObjectMapper(); @Override public void afterPropertiesSet() throws Exception { List<Item> itemList = itemService.list(); List<ItemStock> stockLi = stockService.list(); for (Item item : itemList) { String s = MAPPER.writeValueAsString(item); redisTemplate.opsForValue().set("item:" + item.getId(), s); } for (ItemStock item : stockLi) { String s = MAPPER.writeValueAsString(item); redisTemplate.opsForValue().set("item:stock:" + item.getId(), s); } } }
查询Redis缓存
首先需要能够连接上 redis:
openResty/lualib/common.lua
:添加如下功能
-- 导入 Redis local redis = require('resty.redis') -- 初始化 redis local red = redis:new() red:set_timeouts(1000, 1000, 1000) -- 关闭redis连接的工具方法,其实是放入连接池 local function close_redis(red) local pool_max_idle_time = 10000 -- 连接的空闲时间,单位是毫秒 local pool_size = 100 --连接池大小 local ok, err = red:set_keepalive(pool_max_idle_time, pool_size) if not ok then ngx.log(ngx.ERR, "Put into redis connection Pool failed: ", err) end end -- 查询redis的方法 ip和port是redis地址,key是查询的key local function read_redis(ip, port, key) -- 获取一个连接 local ok, err = red:connect(ip, port) if not ok then ngx.log(ngx.ERR, "Connect redis Failed : ", err) return nil end red:auth('abc123') -- 查询redis local resp, err = red:get(key) -- 查询失败处理 if not resp then ngx.log(ngx.ERR, "Query Redis Failed: ", err, ", key = " , key) end --得到的数据为空处理 if resp == ngx.null then resp = nil ngx.log(ngx.ERR, "Query Redis Empty, key = ", key) end close_redis(red) return resp end -- 将方法导出 local _M = { read_redis = read_redis } return _M
- 关于如果redis没有密码,则无需
red:auth
那一行代码,参考 openresty 前端开发入门四之Redis篇- 关于日志的功能,参考 日志输出 - OpenResty 实践
openResty/nginx/lua/item.lua
:查询脚本的封装:
-- 封装查询函数 function read_data(key, path, param) local resp = read_redis("127.0.0.1", 6379, key) if not resp then -- 查询 http ngx.log(ngx.INFO, "redis 查询失败 , key : " , key) resp = read_http(path, param) end return resp end
具体的查询:
-- 查询商品信息 local itemJSON = read_data("item:"..id, "/item/".. id, nil) -- 查询库存信息 local stockJSON = read_data("item:stock:"..id, "/item/stock/"..id, nil)
Nginx本地缓存
-- 封装查询函数 function read_data(key, path, param, expire) -- 查询本地缓存 local resp = item_cache:get(key) if not resp then ngx.log(ngx.ERR, "Local Query Failed, key:", key) resp = read_redis("127.0.0.1", 6379, key) if not resp then -- 查询 http ngx.log(ngx.ERR, "redis Query Failed, key : " , key) resp = read_http(path, param) end -- 把数据写入缓存 item_cache:set(key, resp, expire) end return resp end
缓存同步
数据同步策略
缓存数据同步的常见方式有三种:
- 设置有效期:给缓存设置有效期,到期后自动删除。再次查询时更新
- 优势:简单、方便
- 缺点:时效性差,缓存过期之前可能不一致
- 场景:更新频率较低,时效性要求低的业
- 同步双写:在修改数据库的同时,直接修改缓存
- 优势:时效性强,缓存与数据库强一致
- 缺点:有代码侵入,耦合度高;
- 场景:对一致性、时效性要求较高的缓存数据
- 异步通知:修改数据库时发送事件通知,相关服务监听到通知后修改缓存数据
- 优势:低耦合,可以同时通知多个缓存服务
- 缺点:时效性一般,可能存在中间不一致状态
- 场景:时效性要求一般,有多个服务需要同步
Canal介绍
Canal安装与使用
暂略,可以参考网络文章
最佳实践
Redis键值设计
优雅的key结构
查看编码方式:
> object encoding XXX
拒绝BigKey
查看内存占用:
> MEMORY USAGE name
一般用长度等来估计大小
危害:
-
网络阻塞
对 BigKey 执行读请求时,少量的 QPS 就可能导致带宽使用率被占满,导致 Redis 实例,乃至所在物理机变慢 -
数据倾斜
BigKey 所在的 Redis 实例内存使用率远超其他实例,无法使数据分片的内存资源达到均衡
-
Redis阻塞
对元素较多的 hash、list、zset 等做运算会耗时较旧,使主线程被阻塞 -
CPU压力
对 BigKey 的数据序列化和反序列化会导致 CPU 的使用率飙升,影响 Redis 实例和本机其它应用
公
寻找 BigKey:
- redis-cli --bigkeys
利用 redis-cli 提供的--bigkeys 参数,可以遍历分析所有 key,并返回 Key 的整体统计信息与每个数据的 Top1 的 big key - scan扫描
自己编程,利用 scan 扫描 Redis 中的所有 key,利用 strlen、hlen 等命令判断 key 的长度(此处不建议使用 MEMORY USAGE) - 第三方工具
利用第三方工具,如Redis-Rdb-Tools
分析 RDB 快照文件,全面分析内存使用情况 - 网络监控
自定义工具,监控进出 Redis 的网络数据,超出预警值时主动告警
如何删除:
BigKey 内存占用较多,即便时删除这样的 key 也需要耗费很长时间,导致 Redis 主线程阻塞,引发一系列问题。
- redis 3.0及以下版本
如果是集合类型,则遍历 BigKey 的元素,先逐个删除子元素,最后删除 BigKey - Redis 4.0以后
Redis 在 4.0 后提供了异步删除的命令:unlink
选择合适的数据结构
批处理优化
- M 操作具备原子性
- Pipeline 不具备原子性
- Spring 有封装模板,一般用并行 slot
服务端优化
持久化配置
Redis 的持久化虽然可以保证数据安全,但也会带来很多额外的开销,因此持久化请遵循下列建议:
-
用来做缓存的 Redis 实例尽量不要开启持久化功能
-
建议关闭 RDB 持久化功能,使用 AOF 持久化
-
利用脚本定期在 slave 节点做 RDB,实现数据备份
-
设置合理的 rewrite 阈值,避免频繁的 bgrewrite
-
配置 no-appendfsync-on-rewrite = yes,禁止在 rewrite 期间做 aof,避免因 AOF 引起的阻塞
部署有关建议:
- Redis 实例的物理机要预留足够内存,应对 fork 和 rewrite
- 单个 Redis 实例内存上限不要太大,例如 4G 或 8G。可以加快 fork 的速度、减少主从同步数据迁移压力
- 不要与 CPU 密集型应用部署在一起
- 不要与高硬盘负载应用一起部署。例如:数据库、消息队列
慢查询问题
命令与安全
参考这篇文章:Redis未授权访问配合SSH key文件利用分析
为了避免这样的漏洞,这里给出一些建议:
- Redis 一定要设置密码
- 禁止线上使用下面命令: keys、flushall、flushdb、config set 等命令。可以利用 rename-command 禁用
- bind:限制网卡,禁止外网网卡访问
- 开启防火墙
- 不要使用 Root 账户启动 Redis
- 尽量不使用默认的端口
内存分配
> client list
集群最佳实践
一个配置:
集群带宽问题:
集群虽然具备高可用特性,能实现自动故障恢复,但是如果使用不当,也会存在一些问题:
- 集群完整性问题
- 集群带宽问题
- 数据倾斜问题客户端性能问题
- 命令的集群兼容性问题 lua 和事务问题
建议:单体Redis(主从Redis)已经能达到万级别的QPS,并且也具备很强的高可用特性。如果主从能满足业务需求的情况下,尽量不搭建Redis集群。
这篇关于Redis 笔记 04:集群、缓存、最佳实践的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-08阿里云Redis项目实战入门教程
- 2024-11-08阿里云Redis资料:新手入门与初级使用指南
- 2024-11-08阿里云Redis教程:新手入门及实用指南
- 2024-11-07阿里云Redis学习入门:新手必读指南
- 2024-11-07阿里云Redis学习入门:从零开始的操作指南
- 2024-11-07阿里云Redis学习:初学者指南
- 2024-11-06阿里云Redis入门教程:轻松搭建与使用指南
- 2024-11-02Redis项目实战:新手入门教程
- 2024-10-22Redis入门教程:轻松掌握数据存储与操作
- 2024-10-22Redis缓存入门教程:快速掌握Redis缓存基础知识