redis

26
  • 写在前面:
    感觉这是24年从0-1最完整的一个笔记了;

基础

  • 内存硬盘并行存储运行速度快 c开发 单线程 非关系型数据库
  • flushdb 删除当前库所有key
  • flushall 删除所有key

特性

1.源码高质量 运行速度快 c开发 单线程
2.丰富数据类型 字符串;哈希;列表;集合;有序集合
3.功能丰富;计时器---点赞、评论数 key过期功能--购物车订单 消息队列 发布订阅--聊天室
4.支持多语言客户端
5.支持数据持久化 可以混合持久化
6.内置高可用架构 主从 哨兵 集群

部署

mkdir -p /data/soft
mkdir -p /opt/redis_6379/{conf,logs,pid}
cd /data/soft
wget https://download.redis.io/releases/redis-5.0.7.tar.gz
yum install gcc make -y
tar zxvf redis-5.0.7.tar.gz -C /opt/
cd /opt/redis-5.0.7
make MALLOC=libc && make install
#不指定安装目录bin默认在/usr/local/bin
conf:
daemonize yes
bind 127.0.0.1 172.16.50.100
port 6379
pidfile /opt/redis_6379/pid/redis_6379.pid
logfile /opt/redis_6379/logs/redis_6379.log
#bin命令解释:
redis-benchmark   性能测试
redis-check-aof   检查aof数据状态
redis-check-rdb   检查rdb数据状态
redis-cli         客户端登陆状态
redis-sentinel    软链接到server
redis-server      哨兵 启动服务端

常见命令

redis-cli 登陆
redis-server .conf
redis-cli shutdown 关闭redis  直接kill会redis内数据丢失
redis-cli -h ip -a passwd -p port
dbsize 查看key数量
info 查看基本信息
info keyspace 直接加区块名也可以直接查看 (查看各个库数据)
select [0~15] 切换库  (默认登陆0库 每一个库存储自己独立的key)
exists key  查询key有多少个
del key 删除key
ttl name 查看过期倒计时 -1是永久 -2是已过期
EXPIRE key  180  定义key过期时间
type key  查看key类型 (string)
redis-cli --raw -n 15 dbsize 外部指定查询15库
redis-cli --scan --pattern '*2*' 通配符查找有2的key  #比keys *安全
config set requirepass password` 改密码命令 改成get查询密码 不过是临时的 永久的还是配置文件为主

数据类型

  • 默认16个库 0~15
  • 数据类型的区别在于值的表现

字符串(string)

set get 是定义字符串

set name zhaoxi
get name zhaoxi
set key value  (set name zhaoxi EX 60  60s后自动删除 
				set name test  会被覆盖
				set name 赵析 支持中文但是不建议 会返回16进制
				redis-cli --raw get name 源格式返回数据)
mset name 1 name2 2 name3 3 多个一起定义;多个一起查询同理 mget
incr key 自增   for i in {1..100}; do redis-cli incr dianzan; done
dncr key 自减
incrby key  自定义加多少
decrby key  自定义减多少
使用场景:
  • 数据缓存:经典用法,把经常要读取的mysql中的url、字符串等信息存储到redis中,redis作为缓存层,加速数据读取,mysql作为数据持久化层,降低mysql的访问压力;
  • 计数器:redis是单线程模式,一个命令结束才会执行下一条命令,因此可以实现计数器的作用,确保多线程访问redis的数据也能确保数据源的正确性;比如点赞、阅读量等;
  • 用作网站用户的登录会话存储:存储session,token等信息;

列表(list)

  • lpush lrange
  • 写入顺序是0-*** lrange key 1 3 是第二个到第四个
lpush name zhaoxi zhangsan
rpush name right 从右推入
lpop name 删除左边第一个并展示数据
rpop name 删除右边第一个并展示数据
del name 删除所有name中value
lindex name 2 展示左边第三个value
lrange name 0 -1  开头-结尾
lpush name 1 2 3 
lrange name 0 -1  是3 2 1  先进先出顺序 

![[Pasted image 20241014223348.png]]

使用场景:
  • 生产消费者模型 lpush写入数据 右边rpop取走数据

集合(set)

  • 集合数据类型能天然去重,在集合中查询也特别快
  • 集合成员是唯一的 是字符串(string)类型的无序集合,称之为容器的数据类型(可以放东西)
sadd name zhaoxi test
scard  name 查看多少个value
smembers name 查看value
sismember name zhaoxi 查看value是否存在 0不存在1存在
set 和 list 区别
  • list可以存储重复元素 set天然去重 可以存储订单号 身份证号等
  • list有序存储 set无序存储
并集 交集 差集
1.建两个测试key
2. sunion key key2 交集
3. sinter key key2 并集
4. sdiff key key2 差集
#语法 sdiff set1 set2 set1里有的但是set2没有的列出来
使用场景:
  • 用户收藏夹 利用set去重功能,实现不会重复收藏,重复点赞等

有序集合(zset)

  • 与set不同的是每个元素都会关联一个double类型的分数,redis通过分数来为集合中的成员进行从小到大排序
zadd key 分数 value 分数 value (比set多了个分数)创建有序集合,进行分数添加,也支持一次添加多个
zrange key 0 -1 列出zset元素 默认从小到大 后面跟withscores 携带分数
zcard key列出集合元素个数
zrem key value 删除某成员
zscore key value 查看某成员参数
zrank key value 查看成员索引下标 0开始
zrevrange key 0 -1 从大到小列出成员
zincrby key 分数 value 添加分数
使用场景:
  • 排行榜
  • 工资排行
  • 成绩排行

哈希(hash)

  • hash是一个string类型的key-value的映射表,hash特别适合用于存储对象
hset key key1 value1   hset stu name zhaoxi
hget key key1
hgetall key 取出所有
hdel key key1 删除
hmset 定义多个key1 value
hmget 查询多个key1的value
使用场景:
  • 用户信息表
    ![[Pasted image 20241014231512.png]]
redis预热mysql数据
1.docker run -d -p 3306:3306 --name mysql -e MYSQL_ROOT_PASSWORD="zhaoxi" mysql
2.docker exec -it mysql mysql -pzhaoxi

create database test;
create table test.t1(id int,name varchar(50),age int);
insert into test.t1 values(1,'name',18),(2,'ceshi',11),(3,'test',12);

mysql> select * from test.t1;
+------+-------+------+
| id   | name  | age  |
+------+-------+------+
|    1 | name  |   18 |
|    2 | ceshi |   11 |
|    3 | test  |   12 |
+------+-------+------+
3 rows in set (0.00 sec)
hset hash mysql中的数据 
唉没啥用 得用代码把mysql中的数据导入到redis中

持久化

  • redis是一个基于内存的数据库,如果服务宕机,内存中的数据就会丢失
  • redis持久化方案有四种:RDB、AOF、虚拟内存和DISKSTORE,官方明确支持的就只有RDB和AOF。

RDB持久化

概念:
  • RDB就是redis databases缩写 中文名为快照/内存快照
  • 优点:数据压缩、恢复速度快 适用于备份、全量复制场景
  • 缺点:非实时备份,可能导致数据丢失 每次调用bgsave都需要fork子进程 fork子进程属于重量级操作 频繁执行成本较高 rdb文件是二进制 没有可读性(strings dump文件可以查看 、),aof可以手动修改或者补全 版本兼容问题
  • 针对rdb不适合实时持久化问题,redis提供了aof持久化方式
实战:
  • 触发方式分为手动触发和自动触发。
  • 本质上都是执行了rdb数据快照命令,save和bgsave
  • save:阻塞当前redis,直到rdb过程完成为止,对于内存比较大的实例会造成长时间阻塞,线上生产不建议使用。
  • bgsave:redis进程执行fork操作创建子进程,rdb持久化过程由子进程负责,完成后自动结束,阻塞只发生在fork阶段,一般时间很短。
手动:
daemonize yes
bind 127.0.0.1 172.16.40.230
port 6379
dir /redis_db/   #注意这个  
pidfile /opt/redis_6379/pid/redis_6379.pid
logfile /opt/redis_6379/logs/redis_6379.log

redis-cli bgsave  就保存到/redis_db/下了 dump.rdb 名字不能乱改 否则conf要指定名字
  • 这时候redis-cli shutdown 数据就没了 这时候直接启动redis就会被读出来
自动:
  • conf配置了rdb参数后以下四个场景会出发持久化
  1. 主动执行bgsave
  2. 达到save 900 1 命令操作,900秒后执行bgsave
  3. 主从复制,默认是sdb数据同步
  4. 执行debug reload命令触发bgsave操作
  5. 执行shutdown也会出发
  • 执行kill -9不会触发
daemonize yes                                    
bind 127.0.0.1 192.168.32.106
port 6379                                                                pidfile /opt/redis_6379/pid/redis_6379.pid                               logfile /opt/redis_6379/logs/redis_6379.log                              dir /redis_db/
save 900 1 #900s内写入一个key时
save 300 10 #300s内写入10个key时
save 60  10000                                                           
dbfilename redis_dump.rdb                                                dir /redis_db/                                                           
stop-writes-on-bgsave-error yes #持久化出错住进城是否停止写入                rdbcompression yes #是否压缩
rdbchecksum yes  #导入时是否检查
  • 加入for写入1000个 900 1 不会触发 因为写条件达到了 但是900s还没到 要等到900s dump文件才会生成
  • 关于备份 向下兼容 3.x的dump5.x可以 5识别向下兼容 反之5.x的数据导入到3.x不行

AOF持久化

概念
  • redis是“写后”日志,redis先执行命令,把数据写入内存,然后记录日志;类似mysql的binlog。(ps:大部分数据库是写前日志,例如mysql,通过写前日志和两端提交,实现数据和逻辑的一致性)
  • 优点:安全,最多丢失1s数据,并且日志有可读性。
  • 缺点:文件较大,恢复较慢。
参数
  • everysec 每个命令执行完日志会写入aof文件的内存缓冲区,每隔一秒把缓冲区的日志写入到磁盘中 宕机会丢失1s内的数据
  • always 每个命令执行完日志立即同步到磁盘 性能影响很大 磁盘io扛不住
    ![[Pasted image 20241020195008.png]]
实战
daemonize yes
bind 127.0.0.1 192.168.32.106
port 6379
pidfile /opt/redis_6379/pid/redis_6379.pid
logfile /opt/redis_6379/logs/redis_6379.log
dir /redis_db/
#rdb
save 5  1000
dbfilename redis_dump.rdb
dir /redis_db/
#aof
appendonly yes
appendfilename "redis_appendonly.aof" 
appendfsync everysec
  • aof在启动redis时就会生成 如果aof丢失需要rdb恢复需要conf中关闭aof
  • rdb和aof结合优先用aof
AOF日志重写机制
  • 每条增删都会写到aof中 文件会越来越大 比如 写入10wkey 再删除5w key 就可以只保存5w key的命令 rdb就是这样子 快照 要对aof进行日志瘦身
  • 直接输入bgrewriteaof命令就行 文件大小肉眼可见的缩小 但是日志会变成二进制格式的 看不懂了 这个命令重写aof日志文件,以rdb+aof协议格式保存 也达到了日志瘦身的效果
  • ![[Pasted image 20241020221300.png]]
  • 上图就是bgrewriteaof后的就两行 第二行是rdb类型保存的 再写入就是aof格式的了
  • 混合持久化本质上就是日志重写

混合持久化

![[Pasted image 20241020224730.png]]
aof加载流程图
aof-use-rdb-preamble yes

  • 我的5.x版本默认开启 但还是建议写入到配置文件中
  • 混合日志上方为rdb协议数据 下面是新写入的rdb还没来得及同步的aof可阅读日志

安全

redis标准配置文件解读

bind 127.0.0.1  与下面的port建立socket连接入口
protected-mode yes  开启安全模式
port 6379 socket连接入口
tcp-backlog 511
timeout 0
tcp-keepalive 300
daemonize no
supervised no
pidfile /var/run/redis_6379.pid
loglevel notice
logfile ""
databases 16
always-show-logo yes
save 900 1                             ==rdb==
save 300 10
save 60 10000
stop-writes-on-bgsave-error yes
rdbcompression yes
rdbchecksum yes
dbfilename dump.rdb
dir ./
replica-serve-stale-data yes
replica-read-only yes
repl-diskless-sync no
repl-diskless-sync-delay 5
repl-disable-tcp-nodelay no
replica-priority 100
lazyfree-lazy-eviction no
lazyfree-lazy-expire no
lazyfree-lazy-server-del no
replica-lazy-flush no
appendonly no
appendfilename "appendonly.aof"
appendfsync everysec
no-appendfsync-on-rewrite no
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
aof-load-truncated yes
aof-use-rdb-preamble yes
lua-time-limit 5000
slowlog-log-slower-than 10000
slowlog-max-len 128
latency-monitor-threshold 0
notify-keyspace-events ""
hash-max-ziplist-entries 512
hash-max-ziplist-value 64
list-max-ziplist-size -2
list-compress-depth 0
set-max-intset-entries 512
zset-max-ziplist-entries 128
zset-max-ziplist-value 64
hll-sparse-max-bytes 3000
stream-node-max-bytes 4096
stream-node-max-entries 100
activerehashing yes
client-output-buffer-limit normal 0 0 0
client-output-buffer-limit replica 256mb 64mb 60
client-output-buffer-limit pubsub 32mb 8mb 60
hz 10
dynamic-hz yes
aof-rewrite-incremental-fsync yes
rdb-save-incremental-fsync yes

配置安全设置

改端口 密码 开启安全模式
bind 0.0.0.0 所有都能连
port 26379
requirepass zhaoxi
protected-mode yes
以指定redis用户运行
useradd -M -s /sbin/nologin redis
chown -R redis.redis /opt/redis_6379
chown -R redis.redis /usr/local/bin
chown -R redis.redis /redis_db
用systemd启动 写个systemd启动redis脚本 百度吧懒得写了
禁用危险命令
配置文件下面加下面这些 本质上就是重命名命令 让redis识别不到
rename-command flushdb asdkjhfkajsdhfuiweahnfiu
rename-command config ajkdslfhkaljsdhfkjas
eg:redis入侵攻击
  • 没啥意思 大概是反序列化漏洞 利用redis的aof持久化 把key value 公钥写入到/root/.ssh/下 然后免密登录

主从复制

  • redis提供了主从库模式,保证数据副本的一致,读写分离 从是只读的
  • 作用:
    1.数据冗余
    2.故障恢复
    3.负载均衡
    4.高可用基石

实现:

rsync -avz /opt/redis user@slave1:/opt/redis
rsync -avz /opt/redis user@slave2:/opt/redis

#两台slave conf中添加
replicaof master_ip master_port

redis-server redis.conf

#检查
info replication
主能读写 从只能读
127.0.0.1:6379> info replication
# Replication
role:master                        !!!!!!
connected_slaves:2             !!!!!!
slave0:ip=192.168.32.195,port=6379,state=online,offset=154,lag=0     !!!!!
slave1:ip=192.168.32.194,port=6379,state=online,offset=154,lag=0    !!!!!!
master_replid:a932d28c553408f2be3ec37126bca90650240413
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:154
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:154

哨兵

(sentinel)

功能:

  1. 监控
  2. 自动故障转移
  3. 配置提供者(集群入口 类似vip)
  4. 通知

实战:

daemonize yes
守护进程 在后台运行
# 哨兵监听的端口号
port 26379

# 指定要监控的主节点(<master_name> 自定义名称,<master_ip> 为主节
点 IP,<master_port> 为 Redis 端口)
sentinel monitor carl 192.168.32.106 6379 2

# 指定多少毫秒之后认为主节点不可用
sentinel down-after-milliseconds carl 1000

# 哨兵判断主节点不可用之后,多少毫秒之内重新选举主节点
sentinel failover-timeout carl 1000

# 指定有多少个哨兵认为主节点失效时,执行故障转移(至少为 2 个)
sentinel parallel-syncs carl 1

< 记得删除中文 三台机器都要>

daemonize yes
port 26379
sentinel monitor carl 192.168.32.106 6379 2
sentinel down-after-milliseconds carl 1000
sentinel failover-timeout carl 10000
sentinel parallel-syncs carl 1
redis-cli -p 26379 info sentinel  查看哨兵状态  注意是26379
pkill redis-server 别干掉哨兵进程 这时候看哨兵自动切换主节点

![[Pasted image 20241022000657.png]]

  • 故障转移成功
sentinel masters  
sentinel slaves carl 查看slave的状态
sentinel sentinels carl 查看所有
  • 哨兵会根据多个slave机器的配置高低、性能高低,ping pong的相应结果,判断出一个最优的slave 选举为新的master 实现自动的主从切换。
  • 先这样吧 主要先入门 明天学集群 20241022 0:14

集群

概念

分布式存储

Redis 集群通过将数据分散存储在多个节点上,解决了单节点内存容量的瓶颈问题。集群中的每个节点负责存储一部分数据,确保系统可以水平扩展(扩展节点以增加存储容量)。

水平扩展

Redis 集群支持水平扩展,意味着你可以通过增加节点的方式,提升集群的存储能力和处理能力。随着需求的增加,集群可以动态增加节点。

数据分片(Sharding)

Redis 集群将数据分片,每个节点只负责一部分哈希槽中的数据。分片的过程通过一致性哈希算法来完成,确保数据均匀分布到各个节点。

  • 哈希槽(Hash Slots):Redis 集群使用 16384 个哈希槽,每个键根据哈希值被分配到其中一个哈希槽中。
  • 一致性哈希:Redis 使用 CRC16 哈希算法生成键的哈希值,并将其映射到 16384 个哈希槽之一。
高可用性与自动故障转移

Redis 集群提供高可用性,通过主从复制和故障转移实现。当主节点故障时,从节点会自动提升为主节点,继续服务请求,避免集群出现单点故障。

  • 主从复制:每个主节点都有至少一个从节点作为备份,数据同步到从节点,确保数据冗余。
  • 选举机制:Redis 集群中的节点通过通信检测是否有主节点出现故障,并在必要时发起选举,从节点将被选为主节点。
集群通信协议

Redis 集群节点之间使用 Gossip 协议 进行通信,每个节点定期与其他节点交换信息,检测节点状态、故障等。

  • PongPing 消息用于节点间的心跳检测,确保集群的状态一致性。

工作原理

Redis 集群节点角色
  • 主节点(Master Node):存储实际数据,并处理客户端的读写请求。
  • 从节点(Slave Node):复制主节点的数据,当主节点故障时,从节点可以被提升为主节点。
哈希槽分配

Redis 集群将 16384 个哈希槽分配给不同的主节点。当一个键被写入时,Redis 根据键的哈希值决定该键应该存储到哪个主节点上。

读写请求路由
  • 客户端连接:Redis 集群使用智能客户端,客户端可以连接任意一个节点,并根据 MOVED 响应消息重新路由请求到正确的节点。
  • MOVED 响应:如果客户端向错误的节点发送请求,节点会返回 MOVED 消息,并告知客户端应该访问哪个节点。

实战

搭建
cluster-enabled yes 启用集群模式。   
cluster-config-file nodes.conf 保存集群节点信息。
cluster-node-timeout 5000 节点通信超时时间,单位为毫秒。
cluster-require-full-coverage yes 当部分哈希槽不可用时,是否让集群继续工作。

redis-cli --cluster create  所有参与地址 一般6个 三主三从 --cluster-replicas 1 为每个主分配1个从  0的话就是只有主
集群管理命令
CLUSTER NODES  查看集群中的节点信息。
CLUSTER INFO 获取集群状态信息,如已分配的哈希槽数量、集群状态等。
CLUSTER MEET <IP> <port> 手动将一个新节点加入集群。
CLUSTER FAILOVER 手动将从节点提升为主节点。
数据操作与分布

Redis 集群中的键值操作(如 SETGET)会根据哈希槽分布到不同的节点上。客户端在执行操作时,不需要关心具体的节点分布,集群会自动路由请求。
例子:
redis-cli -c -p 7000
SET key1 "value1"  # 自动分配到对应哈希槽
GET key1           # 通过路由找到存储 key1 的节点

集群的动态扩展
  • 添加节点:使用 redis-cli --cluster add-node 添加新的节点到集群中,然后使用 rebalance 命令重新分配哈希槽。
  • 移除节点:使用 redis-cli --cluster del-node 命令移除集群中的某个节点,确保数据在移除前已经被迁移到其他节点。
故障处理与恢复
  • 节点故障:当主节点发生故障时,集群会自动将从节点提升为主节点,继续处理请求。
  • 手动恢复:如果某个节点长时间不可用,可以通过重新加入节点或手动迁移数据的方式恢复。
监控 Redis 集群
  • 使用 redis-cli --cluster info 查看集群的状态,如节点健康状态、哈希槽分配情况等。
  • 使用 Redis 集群的日志和监控工具(如 Redis Sentinel 或外部工具如 PrometheusGrafana)监控性能和故障情况。
实战案例:REDIS 集群动态扩容

假设现有 Redis 集群已经部署完毕,包含 3 个主节点和 3 个从节点。我们将添加一个新节点到集群,并重新分配哈希槽。

  1. 步骤 1:启动新节点
    redis-server /path/to/redis-7006.conf
  2. 步骤 2:将新节点添加到集群
    redis-cli --cluster add-node 127.0.0.1:7006 127.0.0.1:7000
  3. 步骤 3:重新分配哈希槽
    redis-cli --cluster reshard 127.0.0.1:7000
  4. 步骤 4:验证新节点的哈希槽分布
    redis-cli -c -p 7006
    cluster nodes
  • -c是客户端连接集群要加的参数 --cluster是要管理集群加的参数