目录
简介
持久化
主从复制
高可用 Redis-Sentinel
.NET Core 开发
分布式 Redis-Cluster
配置说明
常见问题
简介
本节内容基于 CentOS 7.4.1708,Redis 3.2.12 环境实验.
Redis 是一个开源的高性能键值对数据库.
安装: yum install -y redis
特性:
高性能 Key-Value 服务器
多种数据结构
丰富功能
缓存(get|set)
计数器(incre)
消息队列(publish|subcrib)
高可用(v2.8 redis-sentinel)
分布式(v3.0 redis-cluster)
可执行文件:
redis-server: 服务端
redis-cli: 客户端
redis-benchmark: 性能测试工具
redis-check-aof:aof 修复工具
redis-check-dump:rdb 修复工具
redis-sentinel:sentinel 服务端
启动方式:
最简启动: 默认配置直接启动 redis-server
动态参数启动: 命令行指定配置启动 redis-server
配置文件启动(推荐): 指定配置文件启动 redis-server
启动验证:
- ps -ef|grep redis
- redis-cli -h locahost -p 6379 ping
由于 redis 是单线程的, 推荐在一台多核 CPU 机器上部署多个 redis 实例充分发挥.
持久化
redis 持久化支持 2 种:
RDB: 快照方式, 相当于 MySQL 中的 dump
AOF: 写日志方式, 相当于 MySQL 中的 binlog, 推荐使用
注意:
当同时开启 RDB 和 AOF 的时候, redis 启动的时候会读取 AOF 还原数据.
推荐: 关闭 RDB 持久化机制, 开启 AOF
RDB
RDB 是什么:
RDB 方式的持久化是通过快照 (snapshortting) 完成的, 当符合一定条件时 Redis 会自动将内存中所有数据完整的压缩存储到硬盘上.
RDB 开启条件由 2 个参数 时间 和 改动次数构成. 如: save 900 1
RDB 文件由 2 个参数 dir 和 dbfilename 分别指定目录 和 文件名
RDB 方式是 Redis 默认的持久化方式.
触发命令:
save 命令(阻塞)
bgsave 命令(fork 过程阻塞)
主要触发方式:
自动触发规则(内部调用 bgsave, 不推荐开启)
全量复制(内部调用 bgsave)
过程:
执行 save 或 bgsave 命令
生成新的 rdb 文件, 如: temp-36985.rdb
覆盖 rdb 文件, 如: dump-6379.rdb
优点:
启动速度快
占用空间小
缺点:
容易丢失数据
时间复杂度 O(n)
关闭 RDB 方式:
redis-cli config set save ""
注意:
RDB 并不能真正的关闭, 在主从复制时主从都会生成 RDB 文件
AOF
AOF 是什么:
AOF 是纯文本文件, 会记录 Redis 的每次改动命令(不记录查询).
AOF 开启条件: appendonly yes
AOF 文件由 2 个参数 dir 和 appendfilename 分别指定目录 和 文件名
AOF 方式 默认情况下 Redis 并没有开启.
由于每次改动都会记录, 产生 2 个问题:
每次改动都写入硬盘, 普通硬盘只能承受几百次 qps. 通过写入策略来调整
对同 1 个 key 执行几次操作就记录几次, 冗余量特别大. 通过 aof 文件重写来调整
AOF 文件有 3 种写入策略:
- always(每次写入都会 fsync 同步到硬盘)
- everysec(默认, 1s 写 1 次)
- no(并非不写, 交给系统控制预计 30s 写 1 次)
AOF 重写:
重写方式
手动执行 bgrewriteaof 命令
自动触发规则(通过指定最小 aof 文件和 aof 增长率来自动内部调用 bgrewriteaof)
过程
fork 出子进程
子进程执行 bgrewriteaof 命令
父进程将新接收的命令, 同时写到 aof 文件和 aof_rewrite_buffer 文件中.(在 aof 重写时, 可配置关闭 aof 写入)
子进程将 aof_rewrite_buffer 文件追加到新 aof 文件中.
覆盖旧的 aof 文件
注意:
采用 everysec 方式, 最多可能丢失 2s 的数据.
主从复制
为什么需要主从复制:
通过持久化保证 Redis 在服务器重启的情况下数据也不会丢失. 但数据在一台服务器上, 如果服务器的硬盘坏了, 也会导致数据丢失. 为了避免单点故障, Redis 提供了主从复制高可用方案.
主从复制结构:
1 个 master 可以有多个 slave
1 个 slave 只能有 1 个 master
数据流向单向 master -> slave
开启复制:
命令:--slaveof ip port
配置: slaveof ip port(默认配置都是 master)
关闭复制:
slaveof no one
复制类型:
全量复制(首次 或者 网络断开时间比较长)
部分复制(在网络抖动一定范围的情况下, v2.8 以上可配置复制缓存区 repl-backlog-size)
全量复制过程:
slave 节点 发起 psync runid offset:psync ? -1
master 节点 返回 fullresync runid offset
master 节点 bgsave 保存当前数据到 rdb
master 节点 在此期间接收到新的数据存储到 buffer 中
master 节点 send RDB,send buffer
slave 节点 flush old data
slave 节点 load RDB,load buffer
在 master 重启 (master 的 run_id 更新) 和 slave 重启 (slave 的 run_id 丢失) 时都会发生全量复制, 通过 info server 可以查看 run_id.
部分复制过程:
slave 节点 发起 psync runid offset
master 节点 确认 runid 和 offset 没问题后, 发送增量数据
slave 节点 接收同步数据.
当全量复制完成 或 网络抖动一定范围 时, master 相当于 slave 的 client 进行增量更新数据.
Redis Sentinel
Redis-Sentinel 是什么?
Redis-Sentinel 是 Redis 官方推荐的高可用性 (HA) 解决方案
Redis-Sentinel 本身也是一个独立运行的进程, 它能监控多个 master-slave 集群, 发现 master 宕机 后能进行自动故障转移.
sentinel 工作原理:
准备多个 Redis Sentinel 节点(建议至少 3 个节点, 避免单点故障)
多个 Sentinel 节点发现并确认 master 主观下线
超过 quorum 个 sentinel 判定确认 客观下线
选出 1 个 sentinel 节点作为领导
选出 1 个 slave 节点作为 master
切换 slave 节点的 master 为新的 master
通知 client 主从变化
等待故障的 master 复活成为新的 slave
Client 不直接连接 Redis 节点, 应该连接 Sentinel 节点获取 Redis Info
2 种下线判定:
sdown(subjectively down, 主观下线): 每个 sentinel 判定 redis 节点下线.
odown(objectively down, 客观下线): 超过 quorum 个 sentinel 判定 redis 节点下线.
启动方式:
- redis-sentinel /path/to/sentinel.conf
- redis-server /path/to/sentinel.conf --sentinel
三个定时任务:
每个 Sentinel 节点每秒通过 redis 的 __sentinel__:hello 发布一条消息, 宣布自己的存在. 同时也订阅来确定其他的 Sentinel 节点.
每个 Sentinel 节点每秒对其他 redis 节点执行 ping. 确定是否下线.
每个 Sentinel 节点每 10 秒 对 master 和 slave 执行 info, 确定 slaves.
配置模拟:
配置 Redis 开启主从复制
配置 Sentinel 监控主节点
- echo "停止当前所有 redis-server + redis-sentinel";
- ps -x | grep redis | grep -v grep | awk '{print $1}' | xargs -r kill
- echo "生成并启动 3 个 redis 配置";
- for port in 6379 6380 6381 ;do
- echo -e "daemonize yes\nport $port\npidfile /var/run/redis-$port.pid\nlogfile /var/log/redis/redis-$port.log\n"> /etc/redis/redis-$port.conf
- if [ $port != 6379 ];then
- echo "slaveof 127.0.0.1 6379">> /etc/redis/redis-$port.conf
- fi
- redis-server /etc/redis/redis-$port.conf
- done
- echo "生成并启动 3 个 redis-sentinel 配置";
- for port in 26379 26380 26381 ;do
- echo -e "daemonize yes\nport $port\ndir /tmp\nsentinel monitor mymaster 127.0.0.1 6379 2\nsentinel down-after-milliseconds mymaster 3000\nsentinel parallel-syncs mymaster 1\nsentinel failover-timeout mymaster 60000\nlogfile /var/log/redis/sentinel-$port.log\n"> /etc/redis/redis-sentinel-$port.conf
- redis-sentinel /etc/redis/redis-sentinel-$port.conf
- done
- echo "结束";
常用的 channel:
+switch-master: 切换主节点
+convert-to-slave: 切换从节点
+sdown: 主观下线
.NET Core 环境开发:
- dotnet add package StackExchange.Redis
- var options = new ConfigurationOptions()
- {
- CommandMap = CommandMap.Sentinel,
- EndPoints = { { "192.168.0.51", 26379}, {"192.168.0.51", 26381}, {"192.168.0.51", 26380} },
- AllowAdmin = true,
- TieBreaker = "",
- ServiceName = "mymaster",
- SyncTimeout = 5000
- };
- var sentinelConn = ConnectionMultiplexer.Connect(options);
- var master = sentinelConn.GetServer("192.168.0.51",26381).SentinelGetMasterAddressByName("mymaster");
- // ...
- var conn = ConnectionMultiplexer.Connect(master);
- sentinelConn.GetSubscriber().Subscribe("+switch-master", (channel, message) =>
- {
- // mymaster 192.168.0.51 6380 192.168.0.51 6381
- Console.WriteLine((string)message);
- // ...
- conn = ConnectionMultiplexer.Connect(ip);
- conn.GetDatabase().StringSet("hello","故障切换后值");
- });
- sentinelConn.GetSubscriber().Subscribe("+convert-to-slave", (channel, message) =>
- {
- // slave 192.168.0.51:6379 192.168.0.51 6379 @ mymaster 192.168.0.51 6380
- Console.WriteLine((string)message);
- });
- conn.GetDatabase().StringSet("hello","原始值");
注意:
所有 Sentinel 和 Redis 不能在同一个节点
Redis Cluster
实际上大部分场景下, Redis Sentinel 已经足够好. 请根据实际情况采用 Redis Cluster.
Redis Cluster 采用虚拟槽分区方式(16384 个虚拟槽).
原因:
需要更高的 qps(超过 10w/s)
需要更高的数据量(超过 500G)
需要更高的带宽(超过 1000M)
常用命令:
redis-cli -h localhost -p 6382 cluster info
: 查看集群基本信息
redis-cli -h localhost -p 6382 cluster slots
: 查看集群 slot 信息
redis-cli -h localhost -p 6382 cluster nodes
: 查看集群 node 信息
redis-cli -c:move 自动跳转执行
yum install -y redis-trib
: 官方提供了基于 ruby 的工具方便部署
搭建 Cluster 过程:
配置
- cluster-enabled:yes
- cluster-node-timeout 15000
- cluster-require-full-coverage no
- cluster-config-file node-${port}.conf
- meet
- redis-cli cluster meet ip port
分配槽(0-16383)
redis-cli cluster addslots {0....5461}
分配主从(node-id)
redis-cli cluster replicate {nodeid}
redis-cli 搭建:
- echo "停止当前所有 redis-server + redis-sentinel";
- mkdir /etc/redis
- ps -x | grep redis | grep -v grep | awk '{print $1}' | xargs -r kill
- sleep 1
- echo "启动 6 个 redis + meet";
- for port in 7000 7001 7002 7003 7004 7005;do
- echo -e "daemonize yes\nport $port\npidfile /var/run/redis-$port.pid\nlogfile /var/log/redis/redis-$port.log\ncluster-enabled yes\ncluster-config-file nodes-$port.conf\ncluster-require-full-coverage no"> /etc/redis/redis-$port.conf
- redis-server /etc/redis/redis-$port.conf
- done
- for port in 7000 7001 7002 7003 7004 7005;do
- redis-cli -p $port FLUSHALL
- redis-cli -p $port cluster reset soft
- if [ $port != 7000 ];then
- redis-cli -p 7000 cluster meet 127.0.0.1 $port
- fi
- done
- sleep 1
- echo "分配 16383 槽";
- redis-cli -p 7000 cluster addslots {0..5461}
- redis-cli -p 7001 cluster addslots {5462..10922}
- redis-cli -p 7002 cluster addslots {10922..16383}
- echo "配置 replication"
- redis-cli -p 7003 cluster replicate `redis-cli -p 7000 cluster nodes | grep myself | awk '{print $1}'`
- redis-cli -p 7004 cluster replicate `redis-cli -p 7001 cluster nodes | grep myself | awk '{print $1}'`
- redis-cli -p 7005 cluster replicate `redis-cli -p 7002 cluster nodes | grep myself | awk '{print $1}'`
redis-trib 搭建:
准备节点
使用 redis-trib 搭建
redis-trib create --replicas 1 127.0.0.1:7000 127.0.0.1:7001 127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005
redis-trib create 会自动 meet,addslots,replicate.
配置
查看去除注释的配置: cat /etc/redis.conf | grep -v '^#' | grep -v '^$'
设置配置: config set key value
查询所有配置: config get *
基础配置
配置项 | 默认值 | 推荐值 | 说明 |
---|---|---|---|
daemonize | no | yes(docker 环境例外) | 是否以守护进程方式启动 |
port | 6379 | - | redis 服务监听端口 |
pidfile | /var/run/redis.pid | /var/run/redis-{port}.pid | pid 文件 |
logfile | /var/log/redis/redis.log | /var/log/redis/redis-{port}.log | 日志文件名:redis 工作时产生的日志。 |
dir | /var/lib/redis | - | rdb 文件和 aof 文件目录。推荐使用大文件目录。(不指定则为当前目录) |
protected-mode | yes | - | 限制为 127.0.0.1 访问。启用条件:没有 bindIP 和 没有设置密码 |
RDB 配置
配置项 | 默认值 | 推荐值 | 说明 |
---|---|---|---|
dbfilename | dump.rdb | dump-{port}.rdb | rdb 文件名 |
rdbcompression | yes | yes | 压缩格式 |
stop-writes-on-bgsave-error | yes | yes | 出现错误时,停止新的写入 |
rdbchecksum | yes | yes | 数据完整性校验 |
AOF 配置
配置项 | 默认值 | 推荐值 | 说明 |
---|---|---|---|
appendonly | no | yes | 是否开启 aof 模式 |
appendfilename | "appendonly.aof" | "appendonly-{port}.aof" | aof 文件名 |
appendfsync | everysec | everysec | fsync 方式 |
no-appendfsync-on-rewrite | no(安全) | yes(高性能) | 在 aof 重写时,是否停止 fsync |
auto-aof-rewrite-min-size | 64mb | - | aof 文件重写的最小大小 |
auto-aof-rewrite-percentage | 100 | - | aof 文件增长率 |
aof-load-truncated | yes | yes | 当 aof 文件不完整的时候,将完整的部分加载 |
主从复制配置
配置项 | 默认值 | 推荐值 | 说明 |
---|---|---|---|
slowlog-max-len | 128 | 1000 | 慢查询队列长度 |
slowlog-log-slow-than | 10000 | 1000(qps1w) | 慢查询阈值 (单位:微秒) |
slaveof | ip port | - | 主从复制配置 |
slave-read-only | yes | yes | 从节点只读 |
repl-backlog-size | 1048576 | 10M | 复制缓存区,可以再原有基础上稍微增加 |
Sentinel 配置
配置项 | 默认值 | 推荐值 | 说明 |
---|---|---|---|
daemonize | no | yes | 是否以守护进程方式启动 |
port | 26379 | {port} | sentinel 监听端口 |
dir | /tmp | - | 工作目录 |
sentinel monitor | mymaster 127.0.0.1 6379 2 | - | odown(objectively down, 客观下线) 规则:masterName ip port quorum |
sentinel down-after-milliseconds | mymaster 30000 | - | sdown(subjectively down, 主观下线) 规则:masterName timeout(单位:毫秒) |
sentinel parallel-syncs | mymaster 1 | - | 并发同步数量 |
sentinel failover-timeout | mymaster 180000 | - | 多长时间内不再故障转移 (单位:毫秒) |
logfile | /var/log/redis/sentinel.log | /var/log/redis/sentinel-{port}.log | 日志文件 |
Cluster 配置
配置项 | 默认值 | 推荐值 | 说明 |
---|---|---|---|
cluster-enabled | no | yes | 开启 cluster 模式 |
cluster-node-timeout | 15000 | - | 故障转移时间,主观下线超时时间 |
cluster-config-file | nodes-{port}.conf | cluster 配置 | |
cluster-require-full-coverage | yes | no | cluster 所有节点全部在线才提供服务 |
常见问题
redis 是单线程吗? 为什么这么快?
redis 其实不是单线程(fsync,bgsave), 一次只能执行一条命令.
慢查询
查询慢查询队列: slowlog get
客户端请求的生命周期:
发送命令
排队
执行命令
返回结果
慢查询发送在第三个阶段(执行命令), 客户端超时不一定是慢查询.
fork
fork 本身是同步操作
内存越大耗时越长
info:latest_fork_usec
规避全量复制
首次全量复制: 不可避免
runid 不匹配: 故障转移
复制缓冲区不足: 配置 repl_backlog_size 调整大
常用命令
KEYS pattern : 查询 keys
DBSIZE : 查询所有键的数量
EXISTS key : 查询指定 key 是否存在
TYPE key : 查询 key 的类型
DEL key : 删除指定 key
INFO : 查看 server 信息如: INFO memory
INFO 信息:
used_memory redis 当前使用的内存总量
used_memory_rss redis 当前使用的内存总量(包含内存碎片)
used_memory_peak redis 使用的内存总量峰值
来源: https://www.cnblogs.com/neverc/p/9554498.html