# Day17 Redis 从入门到精通
# 本节关键词
安装启动
持久化 RDB、AOF
丰富的数据类型
哨兵模式 Sentinel
集群 Cluster
# 源码安装
# 下载解压
cd /data
wget http://download.redis.io/releases/redis-6.0.6.tar.gz
tar xzf redis-6.0.6.tar.gz
# 编译
yum -y install gcc automake autoconf libtool make
mv redis-6.0.6 redis
cd redis
make
# 环境变量
vim /etc/profile
export PATH=/data/redis/src:$PATH
source /etc/profile
# 启动服务
redis-server &
# 连接测试
[root@ni-ning redis]# redis-cli
127.0.0.1:6379> set num 100
OK
127.0.0.1:6379> get num
"100"
127.0.0.1:6379>
# 配置启动
mkdir /data/6379
cat > /data/6379/redis.conf <<EOF
daemonize yes
port 6379
logfile /data/6379/redis.log
dir /data/6379
dbfilename dump.rdb
bind 127.0.0.1
requirepass 123456
EOF
# 启动redis
redis-server /data/6379/redis.conf
# 关闭redis
redis-cli shutdown
# bind requirepass
redis-cli -h remote_ip -p 6379
[root@ni-ning 6379]# redis-cli
127.0.0.1:6379> ping
(error) NOAUTH Authentication required.
127.0.0.1:6379> auth 123456
OK
127.0.0.1:6379>
# 查看与设置 配置 支持tab
CONFIG GET requ*
config set requirepass 123
# 持久化
Reids持久化,内存数据保存到磁盘
# RDB 持久化
可以在指定的时间间隔内生成数据集的 时间点快照(point-in-time snapshot)
- 优点:速度快,适用于用作备份,主从复制也是基于RDB持久化功能实现的
- 缺点:会有数据丢失
核心参数配置
vim /data/6379/redis.conf
dir /data/6379
dbfilename dump.rdb
save 900 1
save 300 10
save 60 10000
配置分别表示:
- 900秒(15分钟)内有1个更改
- 300秒(5分钟)内有10个更改
- 60秒内有10000个更改
手动执行备份
save | bgsave
# AOF 持久化
记录服务器执行的所有写操作命令,并在服务器启动时,通过重新执行这些命令来还原数据集 AOF文件中的命令全部以Redis协议的格式来保存,新命令会被追加到文件的末尾
- 优点:可以最大程度保证数据不丢
- 缺点:日志记录量级比较大
核心参数配置
appendonly yes
appendfsync everysec
appendfsync always
appendfsync no
配置分别表示:
- yes 是否打开aof日志功能
- everysec 每秒写一次
- always 每个命令立即同步
- no 写入工作交个操作系统,由操作系统判断缓冲大小,统一写入aof
问答:Redis 持久化有哪些?有什么区别
- rdb:基于快照的持久化,速度更快,一般用作备份,主从复制也是依赖rdb持久化功能
- aof:以追加的方式记录redis操作日志,可以最大程度保证redis数据安全,类似mysql的binlog
# 通用命令
# 查询已存在的所有键的名字,大批量键时不推荐使用
keys *
keys a*
exists key # 是否存在
del key # 删除 key
expire/pexpire key # 以秒/毫秒设定生存时间
ttl/pttl key # 以秒/毫秒返回生存时间
persist key # 取消生存时间设置
rename old new # 改键名
# 丰富的数据类型
- 有序集合 zset
可以从设计接口的角度理解
1. 新增或修改记录
- 存储格式为有序字典
- 排序规则为成员member对应分值score,即sort.key=lambda item: item['member'],默认从小到大
- 新增 conn.zadd('zset-key', score, member)
- 重置分值信息 再次调用 conn.zadd 即可
- 递增修改分值 conn.zincrby('zset-key', increment, member)
2. 获取单个记录信息
- 获取单个分值 conn.zscore('zset-key', member)
- 获取成员数量 conn.zcard('zset-key')
- 获取成员排名
* 默认正序 conn.zrank('zset-key', member)
* 手动倒序 conn.zrevrank('zset-key', member)
3. 获取批量记录信息
- 输入:按照排名区间、按照分值区间、排序方式
- 输出:成员或成员+分值 withscores=False or True
- 按照排名区间
* 从小到大 conn.zrange('zset-key', start, stop)
* 从大到小 conn.zrevrange('zset-key', start, stop)
- 按照分值区间
* 从小到大 conn.zrangebyscore('zset-key', min, max)
* 从大到小 conn.zrevrangebyscore('zset-key', max, min)
- 统计分值区间成员个数 conn.zcount('zset-key', min, max)
4. 删除记录
- 删除当个记录 conn.zrem('zset-key', member)
- 删除批量记录,根据检索条件范围不同
* 按照排名 conn.zremrangebyrank('zset-key', start, stop)
* 按照分值 conn.zremrangebyscore('zset-key', min, max)
5. 集合运算,"存储结果"版本
- conn.zinterstore('dest-key', ['zset-key1', 'zset-key2'], aggregate='SUM MIN MAX')
- conn.zunionstore('dest-key', ['zset-key1', 'zset-key2'], aggregate='SUM MIN MAX')
# 主从复制 Master-Replica
原理:
- 副本库通过
slaveof <master_ip> 6379
命令,连接主库,并发送SYNC
给主库 - 主库收到
SYNC
,会立即触发BGSAVE
,后台保存RDB
,并发送给副本库 - 副本库收到后立即应用
RDB
快照 - 主库会陆续将中间产生的新的操作,保存并发送给副本库
- 到此,主复制集就正常工作了
- 再此以后,主库只要发生新的操作,都会以命令传播的形式自动发送给副本库
- 所有复制信息,通过
info
查看,即使重启任何节点,主从关系依然都在 ? - 中间关系链断开时,从库发送
PSYNC(PSYNC2)
,同步缺失部分数据
主从数据一致性保证 配置
min-slaves-to-write 1
min-slaves-max-lag 3
- min-slaves-to-write 至少有1台slave同步数据后,才正常响应
- min-slaves-max-lag 超时时间
问答:主库是否需要开启持久化?
如果不开启持久化,有可能重启操作,造成所有主从数据丢失
# 主从复制实现
# 三个redis实例
# 主节点 6380
# 从节点 6381 6382
mkdir /data/638{0..2}
# 配置文件
cat > /data/6380/redis.conf <<EOF
daemonize yes
port 6380
pidfile /data/6380/redis.pid
loglevel notice
logfile /data/6380/redis.log
dbfilename dump.rdb
dir /data/6380
requirepass 123
masterauth 123
EOF
cat > /data/6381/redis.conf <<EOF
daemonize yes
port 6381
pidfile /data/6381/redis.pid
loglevel notice
logfile /data/6381/redis.log
dbfilename dump.rdb
dir /data/6381
requirepass 123
masterauth 123
EOF
cat > /data/6382/redis.conf <<EOF
daemonize yes
port 6382
pidfile /data/6382/redis.pid
loglevel notice
logfile /data/6382/redis.log
dbfilename dump.rdb
dir /data/6382
requirepass 123
masterauth 123
EOF
# 启动
redis-server /data/6380/redis.conf
redis-server /data/6381/redis.conf
redis-server /data/6382/redis.conf
# 开启主从 6381 6382
redis-cli -p 6381 -a 123 SLAVEOF 127.0.0.1 6380
redis-cli -p 6382 -a 123 SLAVEOF 127.0.0.1 6380
# 查看主从状态
redis-cli -p 6380 -a 123 info replication
redis-cli -p 6381 -a 123 info replication
redis-cli -p 6382 -a 123 info replication
# 解除主从
redis-cli -p 6381 -a 123 slaveof no one
redis-cli -p 6382 -a 123 slaveof no one
# 哨兵模式 Sentinel
- 单独 sentinel,监控 master
- 自动选主,切换(6381 slaveof no one)
- 2号库(6382)指向新主库(6381)
- 应用透明
- 自动处理故障节点
搭建 sentinel 过程
# 配置文件
mkdir /data/26380
cat > /data/26380/sentinel.conf <<EOF
port 26380
dir /data/26380
sentinel monitor mymaster 127.0.0.1 6380 1
sentinel down-after-milliseconds mymaster 5000
sentinel auth-pass mymaster 123
EOF
# 启动
redis-sentinel /data/26380/sentinel.conf &>/tmp/sentine.log &
# 测试
tail -f /tmp/sentine.log
redis-cli -p 6380 -a 123 shutdown
# 测试结果 6382成为主节点,6381指向新的主节点6382
# 如果重启 6380,也会指向新的主节点6382
# 集群 Cluster
- 推荐6个节点,所有主节点互相之间心跳监测
- Master-Slave共3组,三个分片
# 高性能
- 将16384个槽位,均匀分布到多个分片节点中
- 存数据时,将key做crc16(key),然后和16384进行取模,得出槽位置(0 - 16383之间)
- 根据计算得出的槽位值,找到相应的分片节点的主节点,存储到相应的槽位上
- 如果客户端当时连接的节点不是将来要存储的分片节点,分片集群会将客户端连接切换至真正存储节点进行数据存储
问答:为什么是16384个槽位?
这个作者好像说过,心跳包 1000个节点正好,减少网络流量
# 高可用
在搭建集群时,会为每个分片主节点,对应一个从节点,实现slaveof的功能,同时当主节点down,实现类型sentinel的自动failover功能
1. redis会有多组分片构成(3组)
2. redis cluster 使用固定个数的slot存储数据(共16384)
3. 每组分片分得 1/3 slot个数(0-5460, 5461-10922, 10923-16383)
4. 基于crc16(key) % 16384 ---> 值(槽位号)
# 搭建过程
mkdir /data/700{0..5}
# 配置文件
cat > /data/7000/redis.conf <<EOF
port 7000
daemonize yes
pidfile /data/7000/redis.pid
loglevel notice
logfile /data/7000/redis.log
dbfilename dump.rdb
dir /data/7000
protected-mode no
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
appendonly yes
EOF
cat > /data/7001/redis.conf <<EOF
port 7001
daemonize yes
pidfile /data/7001/redis.pid
loglevel notice
logfile /data/7001/redis.log
dbfilename dump.rdb
dir /data/7001
protected-mode no
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
appendonly yes
EOF
cat > /data/7002/redis.conf <<EOF
port 7002
daemonize yes
pidfile /data/7002/redis.pid
loglevel notice
logfile /data/7002/redis.log
dbfilename dump.rdb
dir /data/7002
protected-mode no
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
appendonly yes
EOF
cat > /data/7003/redis.conf <<EOF
port 7003
daemonize yes
pidfile /data/7003/redis.pid
loglevel notice
logfile /data/7003/redis.log
dbfilename dump.rdb
dir /data/7003
protected-mode no
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
appendonly yes
EOF
cat > /data/7004/redis.conf <<EOF
port 7004
daemonize yes
pidfile /data/7004/redis.pid
loglevel notice
logfile /data/7004/redis.log
dbfilename dump.rdb
dir /data/7004
protected-mode no
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
appendonly yes
EOF
cat > /data/7005/redis.conf <<EOF
port 7005
daemonize yes
pidfile /data/7005/redis.pid
loglevel notice
logfile /data/7005/redis.log
dbfilename dump.rdb
dir /data/7005
protected-mode no
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
appendonly yes
EOF
# 启动
redis-server /data/7000/redis.conf
redis-server /data/7001/redis.conf
redis-server /data/7002/redis.conf
redis-server /data/7003/redis.conf
redis-server /data/7004/redis.conf
redis-server /data/7005/redis.conf
# 查看节点情况
[root@ni-ning 7005]# ps aux | grep 'redis-server'
root 6640 0.0 0.5 267204 4768 ? Ssl 18:44 0:00 redis-server *:7000 [cluster]
root 6642 0.0 0.5 267204 4684 ? Ssl 18:44 0:00 redis-server *:7001 [cluster]
root 6644 0.0 0.5 267204 4688 ? Ssl 18:44 0:00 redis-server *:7002 [cluster]
root 6646 0.0 0.4 267204 4636 ? Ssl 18:44 0:00 redis-server *:7003 [cluster]
root 6652 0.0 0.5 267204 4764 ? Ssl 18:44 0:00 redis-server *:7004 [cluster]
root 6670 0.0 0.4 267204 4636 ? Ssl 18:44 0:00 redis-server *:7005 [cluster]
root 6678 0.0 0.0 221460 796 pts/1 R+ 18:44 0:00 grep --color=auto redis-server
# 将节点加入集群管理
redis-cli --cluster create \
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 \
--cluster-replicas 1
>>> Performing Cluster Check (using node 127.0.0.1:7000)
M: 88663f90ce9768ace4a1dc6513de737f57282436 127.0.0.1:7000
slots:[0-5460] (5461 slots) master
1 additional replica(s)
M: d7347188182e4ecad159c5173389e0e56c6ed108 127.0.0.1:7002
slots:[10923-16383] (5461 slots) master
1 additional replica(s)
S: 8e22dc5ad8bd7f5c798e2e057c356fd834093ae8 127.0.0.1:7003
slots: (0 slots) slave
replicates 144c3b0f11ccf27ab11be92e9d65e08038ff0412
S: 7519812fd955b0ca5bcc6f6b434a72070969dca2 127.0.0.1:7005
slots: (0 slots) slave
replicates 88663f90ce9768ace4a1dc6513de737f57282436
M: 144c3b0f11ccf27ab11be92e9d65e08038ff0412 127.0.0.1:7001
slots:[5461-10922] (5462 slots) master
1 additional replica(s)
S: 8383960f9e1408d41c06279e1291e7d82ce4527c 127.0.0.1:7004
slots: (0 slots) slave
replicates d7347188182e4ecad159c5173389e0e56c6ed108
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
# 查看节点状态
redis-cli -p 7000 cluster nodes | grep 'master'
redis-cli -p 7000 cluster nodes | grep 'slave'
# Reids的API支持
pip install redis
pip install redis-py-cluster
- base
import redis
r = redis.StrictRedis(host='127.0.0.1', port=6379, db=0)
r.set('key', 'vvvv')
print(r.get('key'))
- sentinel
from redis.sentinel import Sentinel
# 创建节点
sentinel = Sentinel([('127.0.0.1', 26380)], socket_timeout=0.1)
# 发现节点 sentinel 配置文件中设置 mymster
sentinel.discover_master('mymaster')
sentinel.discover_slaves('mymaster')
# 实现读写分离
m = sentinel.master_for('mymaster', socket_timeout=0.1, password='123')
s = sentinel.slave_for('mymaster', socket_timeout=0.1, password='123')
m.set('age', 18)
print(s.get('age'))
- cluster
from rediscluster import RedisCluster
startup_nodes = [
{'host': '127.0.0.1', 'port': 7000},
{'host': '127.0.0.1', 'port': 7001},
{'host': '127.0.0.1', 'port': 7002},
]
rc = RedisCluster(startup_nodes=startup_nodes, decode_responses=True)
rc.set('foo', 'bar')
print(rc.get('foo'))
# 问答相关
- 内存溢出控制策略
maxmemory
maxmemory-policy
当Redis所用内存达到上限时 会触发相应的溢出控制策略,具体受 maxmemory-policy 参数控制,支持6种
1. noeviction 默认策略
2. volatile-lru
3. allkeys-lru
4. allkeys-random
5. volatile-random
6. volatile-ttl
config set maxmeory-policy <policy>
- 其他相关
雪崩、击穿、穿透概念
如何查询大key
reids-rdb-tools reids-cli --bigkeys