# 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')

# TODO

  • 事务 与 MySQL事务区别

# 主从复制 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组,三个分片

# 高性能

  1. 将16384个槽位,均匀分布到多个分片节点中
  2. 存数据时,将key做crc16(key),然后和16384进行取模,得出槽位置(0 - 16383之间)
  3. 根据计算得出的槽位值,找到相应的分片节点的主节点,存储到相应的槽位上
  4. 如果客户端当时连接的节点不是将来要存储的分片节点,分片集群会将客户端连接切换至真正存储节点进行数据存储

问答:为什么是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

# 相关链接

上次更新: 11/14/2022, 8:54:42 PM