Redis
1 Redis入门
1.1 Redis基础
1.1.1 Redis介绍
Redis(Remote Dictionary Server)在2009年发布,开发者Salvatore Sanfilippo是意大利开发者,他本想为自己的 公司开发一个用于替换MySQL的产品Redis,但是没有想到他把Redis开源后大受欢迎,短短几年,Redis就有了很 大的用户群体,目前国内外使用的公司有知乎网、新浪微博、GitHub等
Redis和Memcached是非关系型数据库也称为NoSQL数据库,MySQL、Mariadb、SQL Server、PostgreSQL、 Oracle 数据库属于关系型数据(RDBMS, Relational Database Management System)
Redis(Remote Dictionary Server)在2009年发布,开发者Salvatore Sanfilippo是意大利开发者,他本想为自己的 公司开发一个用于替换MySQL的产品Redis,但是没有想到他把Redis开源后大受欢迎,短短几年,Redis就有了很 大的用户群体,目前国内外使用的公司有知乎网、新浪微博、GitHub等 redis是一个开源的、遵循BSD协议的、基于内存的而且目前比较流行的键值数据库(key-value database),是一个 非关系型数据库,redis提供将内存通过网络远程共享的一种服务,提供类似功能的还有memcache,但相比 memcache,redis还提供了易扩展、高性能、具备数据持久性等功能。
官方网站:https://redis.io/
1.1.2 redis对比memcached
- 支持数据的持久化:可以将内存中的数据保持在磁盘中,重启redis服务或者服务器之后可以从备份文件中恢复数 据到内存继续使用。
- 支持更多的数据类型:支持string(字符串)、hash(哈希数据)、list(列表)、set(集合)、zet(有序集合)
- 支持数据的备份:可以实现类似于数据的master-slave模式的数据备份,另外也支持使用快照+AOF。
- 支持更大的value数据:memcache单个key value最大只支持1MB,而redis最大支持512MB。 Redis 是单线程,而memcache是多线程,所以单机情况下没有memcache并发高,但redis 支持分布式集群以实现 更高的并发,单Redis实例可以实现数万并发。
- 支持集群横向扩展:基于redis cluster的横向扩展,可以实现分布式集群,大幅提升性能和数据安全性。 都是基于C语言开发。
1.1.3 redis 典型应用场景
- Session 共享:常见于web集群中的Tomcat或者PHP中多web服务器session共享
- 消息队列:ELK的日志缓存、部分业务的订阅发布系统
- 计数器:访问排行榜、商品浏览数等和次数相关的数值统计场景
- 缓存:数据库查询缓存、电商网站商品信息、新闻内容
- 微博/微信社交场合:共同好友、点赞评论等
1.2 Redis安装及使用
1.2.1 yum安装redis
在centos系统上需要安装epel源
[root@centos7 ~]# yum list redis
Loaded plugins: fastestmirror
Loading mirror speeds from cached hostfile
Available Packages
redis.x86_64 3.2.12-2.el7 epel
[root@centos7 ~]# yum -y install redis
[root@centos7 ~]# systemctl enable --now redis
# 测试
root@centos7 ~]# redis-cli
127.0.0.1:6379> info
# Server
redis_version:3.2.12
redis_git_sha1:00000000
redis_git_dirty:0
redis_build_id:7897e7d0e13773f
redis_mode:standalone
os:Linux 3.10.0-1160.el7.x86_64 x86_64
arch_bits:64
1.2.2 编译安装redis
官方下载地址:https://redis.io/download
[root@centos7 src]# wget https://download.redis.io/releases/redis-5.0.14.tar.gz
[root@centos7 src]# tar zxvf redis-5.0.14.tar.gz
[root@centos7 redis-5.0.14]# make PREFIX=/apps/redis install
[root@centos7 redis-5.0.14]# ll /apps/redis/
total 0
drwxr-xr-x 2 root root 134 Dec 24 12:59 bin
# 创建对应目录
[root@centos7 redis-5.0.14]# mkdir /apps/redis/{etc,logs,data,run}
[root@centos7 redis-5.0.14]# cp redis.conf /apps/redis/etc/
前台启动redis
[root@centos7 redis-5.0.14]# /apps/redis/bin/redis-server /apps/redis/etc/redis.conf
......
6059:M 24 Dec 2021 15:47:25.525 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.
6059:M 24 Dec 2021 15:47:25.525 # Server initialized
6059:M 24 Dec 2021 15:47:25.525 # WARNING overcommit_memory is set to 0! Background save may fail under low memory condition. To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the command 'sysctl vm.overcommit_memory=1' for this to take effect.
6059:M 24 Dec 2021 15:47:25.525 # WARNING you have Transparent Huge Pages (THP) support enabled in your kernel. This will create latency and memory usage issues with Redis. To fix this issue run the command 'echo never > /sys/kernel/mm/transparent_hugepage/enabled' as root, and add it to your /etc/rc.local in order to retain the setting after a reboot. Redis must be restarted after THP is disabled.
6059:M 24 Dec 2021 15:47:25.525 * Ready to accept connections
这时redis已经启动成功了,不过存在几个告警,下面将依次解决
# WARNING: The TCP backlog setting of 511 cannot be enforced
# because/proc/sys/net/core/somaxconn is set to the lower value of 128.
# 同时发起的TCP的最大连接数设置的默认值128太小,因此需修改内核参数
[root@centos7 ~]# vim /etc/sysctl.conf
net.core.somaxconn = 512
# WARNING overcommit_memory is set to 0! Background save may fail under low memory condition.
# To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run
# the command 'sysctl vm.overcommit_memory=1' for this to take effect.
# 0、表示内核将检查是否有足够的可用内存供应用进程使用;如果有足够的可用内存,内存申请允许;否则,内存申请失败,# 并把错误返回给应用进程。
# 1、表示内核允许分配所有的物理内存,而不管当前的内存状态如何。
# 2、表示内核允许分配超过所有物理内存和交换空间总和的内存
[root@centos7 ~]# vim /etc/sysctl.conf
vm.overcommit_memory = 1
# WARNING you have Transparent Huge Pages (THP) support enabled in your kernel. This will
# create latency and memory usage issues with Redis. To fix this issue run the command 'echo
# never > /sys/kernel/mm/transparent_hugepage/enabled' as root, and add it to your
# /etc/rc.local in order to retain the setting after a reboot. Redis must be restarted after
# THP is disabled.
# 系统自带的大页内存动态分配,需要关闭让redis 负责内存管理。
[root@centos7 ~]# echo never > /sys/kernel/mm/transparent_hugepage/enabled
[root@centos7 ~]# vim /etc/rc.local
echo never > /sys/kernel/mm/transparent_hugepage/enabled
[root@centos7 ~]# sysctl -p
net.ipv4.ip_forward = 1
net.core.somaxconn = 512
vm.overcommit_memory = 1
再次重启redis警告消失
配置redis服务启动脚本
[root@centos7 apps]# cat /usr/lib/systemd/system/redis.service
[Unit]
Description=Redis persistent key-value database
After=network.target
After=network-online.target
Wants=network-online.target
[Service]
ExecStart=/apps/redis/bin/redis-server /apps/redis/etc/redis.conf --supervised systemd
ExecReload=/bin/kill -s HUP $MAINPID
ExecStop=/bin/kill -s QUIT $MAINPID
Type=notify
User=redis
Group=redis
RuntimeDirectory=redis
RuntimeDirectoryMode=0755
[Install]
WantedBy=multi-user.target
创建redis 用户和数据目录
[root@centos7 apps]# groupadd -g 1000 redis && useradd -u 1000 -g 1000 redis -s /sbin/nologin
[root@centos7 apps]# chown redis.redis /apps/redis/ -R
创建/usr/bin的软链接
[root@centos7 apps]# ln -sv /apps/redis/bin/redis-* /usr/bin/
‘/usr/bin/redis-benchmark’ -> ‘/apps/redis/bin/redis-benchmark’
‘/usr/bin/redis-check-aof’ -> ‘/apps/redis/bin/redis-check-aof’
‘/usr/bin/redis-check-rdb’ -> ‘/apps/redis/bin/redis-check-rdb’
‘/usr/bin/redis-cli’ -> ‘/apps/redis/bin/redis-cli’
‘/usr/bin/redis-sentinel’ -> ‘/apps/redis/bin/redis-sentinel’
‘/usr/bin/redis-server’ -> ‘/apps/redis/bin/redis-server’
测试链接redis
[root@centos7 apps]# systemctl start redis
[root@centos7 apps]# redis-cli
127.0.0.1:6379> info
# Server
redis_version:5.0.14
redis_git_sha1:00000000
redis_git_dirty:0
redis_build_id:f551c421707b8bfb
redis_mode:standalone
os:Linux 3.10.0-1160.el7.x86_64 x86_64
1.3 Redis配置
1.3.1 Redis主要配置
[root@centos7 apps]# grep ^[a-Z] redis/etc/redis.conf
# 监听地址,可以用空格隔开后多个监听IP
bind 127.0.0.1
# redis3.2 之后加入的新特性,在没有设置bind IP和密码的时候,redis只允许访问127.0.0.1:6379,远程访问将提示 # 警告信息并拒绝远程访问
protected-mode yes
# 监听端口
port 6379
# 三次握手的时候server端收到client ack确认号之后的队列值。同时允许建立的最大TCP连接数
tcp-backlog 511
# 客户端和Redis服务端的连接超时时间,默认是0,表示永不超时
timeout 0
# tcp会话保持时间
tcp-keepalive 300
# 认情况下 redis 不是作为守护进程运行的,如果你想让它在后台运行,你就把它改成 yes,当redis作为守护进程运行的
# 时候,它会写一个 pid 到 /var/run/redis.pid 文件里面
daemonize no
# 和操作系统相关参数,可以设置通过upstart和systemd管理Redis守护进程,centos7以后都使用
supervised no
# pid文件路径
pidfile /var/run/redis_6379.pid
# 日志级别
loglevel notice
# 日志路径
logfile ""
# 设置db库数量,默认16个库
databases 16
# 在启动redis时是否显示log图标
always-show-logo yes
# 在900秒内有一个键内容发生更改就出就快照机制
save 900 1
save 300 10
save 60 10000
# 快照出错时是否禁止redis写入操作
stop-writes-on-bgsave-error yes
# 持久化到RDB文件时,是否压缩,"yes"为压缩,"no"则反之
rdbcompression yes
# 是否开启RC64校验,默认是开启
rdbchecksum yes
# 快照文件名
dbfilename dump.rdb
# 快照文件保存路径
dir ./
# 当从库同主库失去连接或者复制正在进行,从机库有两种运行方式:
# 1、如果replica-serve-stale-data设置为yes(默认设置),从库会继续响应客户端的读请求。
# 2、如果replica-serve-stale-data设置为no,除去指定的命令之外的任何请求都会返回一个错误"SYNC with
# master in progress"。
replica-serve-stale-data yes
# 是否设置从库只读
replica-read-only yes
# 是否使用socket方式复制数据(无盘同步),新slave连接连接时候需要做数据的全量同步,redis server就要从内存
# dump出新的RDB文件,然后从master传到slave,有两种方式把RDB文件传输给客户端:
# 1、基于硬盘(disk-backed):master创建一个新进程dump RDB,RDB完成之后由父进程(即主进程)传给slaves
# 2、基于socket(diskless):master创建一个新进程直接dump RDB到slave的socket,不经过主进程,不经过硬盘。
# 基于硬盘的话,RDB文件创建后,一旦创建完毕,可以同时服务更多的slave,但是基于socket的话, 新slave连接到
# master之后得逐个同步数据
# 在较慢并且网络较快的时候,可以用diskless(yes),否则使用磁盘(no)
repl-diskless-sync no
# diskless**复制的延迟时间**,设置0为关闭,在延迟时间内连接的新客户端,会一起通过disk方式同步数据,但是一旦
# 复制开始还没有结束之前,master节点不会再接收新slave的复制请求,直到下一次同步开始。
repl-diskless-sync-delay 5
# slave根据master指定的时间进行周期性的PING 监测
repl-ping-slave-period 10
# 复制连接的超时时间,需要大于repl-ping-slave-period,否则会经常报超时
repl-timeout 60
# 在socket模式下是否在slave套接字发送SYNC之后禁用 TCP_NODELAY,如果选择“yesRedis将使用更少的TCP包和带宽
# 来向slaves发送数据,但是这将使数据传输到slave上有延迟,Linux内核的默认配置会达到40毫秒,如果你选择了
# "no"** 数据传输到salve**的延迟将会减少但要使用更多的带宽。
repl-disable-tcp-nodelay no
# 复制缓冲区内存大小,只有在slave连接之后才分配内存。
repl-backlog-size 512mb
# 多次时间master没有slave连接,就清空backlog缓冲区。
repl-backlog-ttl 3600
# 当master不可用,Sentinel会根据slave的优先级选举一个master,最低的优先级的slave,当选master,而配置成0,
# 永远不会被选举。
replica-priority 100
# 设置redis连接密码
requirepass foobared
# 重命名一些高危命令
rename-command
# Redis最大连接客户端
maxclients 10000
# 最大内存,单位为bytes字节,8G内存的计算方式8(G)1024(MB)1024(KB)*1024(Kbyte),需要注意的是slave的输出缓# 冲区是不计算在maxmemory内。
maxmemory
# 是否开启AOF日志记录,默认redis使用的是rdb方式持久化,这种方式在许多应用中已经足够用了,但是redis如果中途宕# 机,会导致可能有几分钟的数据丢失(取决于dumpd数据的间隔时间),根据save来策略进行持久化,Append Only File
# 是另一种持久化方式,可以提供更好的持久化特性,Redis会把每次写入的数据在接收后都写入 appendonly.aof 文件,# 每次启动时Redis都会先把这个文件的数据读入内存里,先忽略RDB文件。
appendonly no
# AOF文件名
appendfilename "appendonly.aof"
1.3.2 Redis持久化
Redis 虽然是一个内存级别的缓存程序,也就是redis 是使用内存进行数据的缓存的,但是其可以将内存的数据按 照一定的策略保存到硬盘上,从而实现数据持久保存的目的,目前redis支持两种不同方式的数据持久化保存机 制,分别是RDB和AOF
RDB模式
RDB实现的具体过程如下:
- 从主进程先fork出一个子进程
- 子进程将内存的数据保存为一个临 时文件,比如dump.rdb.temp
- 当数据保存完成之后再将上一次保存的RDB文件替换掉,然后关闭子进程
RDB模式的优缺点
优点:
- RDB快照保存了某个时间点的数据,可以通过脚本执行bgsave(非阻塞)或者save(阻塞)命令自定义时间点备份,可 以保留多个备份,当出现问题可以恢复到不同时间点的版本。
- 可以最大化IO 的性能,因为父进程在保存RDB 文件的时候唯一要做的是fork出一个子进程,然后的-操作都会有这 个子进程操作,父进程无需任何的IO操作
- RDB在大量数据比如几个G的数据,恢复的速度比AOF的快
缺点:
- 不能时时的保存数据,会丢失自上一次执行RDB备份到当前的内存数据
- 数据量非常大的时候,从父进程fork的时候需要一点时间,可能是毫秒或者秒或者分钟,取决于磁盘IO性能。
AOF模式
AOF和RDB一样使用了写时复制机制,AOF默认为每秒钟fsync一次,即将执行的命令保存到AOF文件当中,这样即 使redis服务器发生故障的话顶多也就丢失1秒钟之内的数据,也可以设置不同的fsync策略,或者设置每次执行命令的时候执行fsync,fsync会在后台执行线程,所以主线程可以继续处理用户的正常请求而不受到写入AOF文件的 IO影响。
AOF模式优缺点
优点:
- 数据同步时间更短,最大限度保障了数据的完整性
- 日志重写功能,可以在不中断客户端服务情况下在后台重建AOF,使aof体积保持最小,但是还可以确保保存最完整的数据
缺点:
- AOF 文件通常比相同数据集的等效 RDB 文件大
- AOF性能不如RDB
AOF日志重写步骤
- Redis从主进程先fork出一个子进程
- 子进程使用一个新的AOF临时文件开始写入
- 父进程会把最新的数据放在缓存当中(同时还会把新的数据写到旧AOF文件当中,这样即使重写失败了也不会导致数据丢失)
- 当子进程完成重写后父进程会接收到一个信号,并将内存缓存当中的数据追加到子进程的重写文件当中
- Redis会将重写文件重命名,并将之后的数据存入该文件当中
1.4 Redis数据类型
1.4.1 字符串
字符串是所有编程语言中最常见的和最常用的数据类型,而且也是redis最基本的数据类型之一,而且redis中所有 的key的类型都是字符串。
# 添加一个key
127.0.0.1:6379[1]> set key1 value1
OK
# 添加多个key
127.0.0.1:6379[1]> MSET key1 value1 key2 value2
OK
# 删除一个key
127.0.0.1:6379[1]> DEL key1
(integer) 1
# 追加数据
127.0.0.1:6379[1]> append key1 append
(integer) 12
# 查询key
127.0.0.1:6379[1]> get key1
"value1"
# 查询多个key
127.0.0.1:6379[1]> MGET key1 key2
1) "value1"
2) "value2"
# 查询对象类型
127.0.0.1:6379[1]> type key1
string
# 数值的递增和递减
127.0.0.1:6379[1]> SET num 10
OK
127.0.0.1:6379[1]> DECR num
(integer) 9
127.0.0.1:6379[1]> INCR num
(integer) 10
# 查询字符长度
127.0.0.1:6379[1]> STRLEN key1
(integer) 6
# 判断key是否存在
127.0.0.1:6379[1]> EXISTS key1
(integer) 1
# 设置key过期时间
127.0.0.1:6379[1]> EXPIRE key1 10
(integer) 1
# 查看key过期时间
127.0.0.1:6379[1]> TTL key1
(integer) -1
-1 # 负一为永不过期,默认创建的key是永不过期,重新对key赋值,也会从有剩余生命周期变成永不过期
-2 # 为没有此key
num # key的剩余有效期
# 取消key过期时间
127.0.0.1:6379[1]> PERSIST key1
(integer) 0
1.4.2 列表
列表是一个双向可读写的管道,其头部是左侧尾部是右侧,一个列表最多可以包含2^32-1个元素即4294967295个 元素。
# 生成列表并插入数据
127.0.0.1:6379[1]> LPUSH list1 tom jack jhon
(integer) 3
# 向列表追加数据
127.0.0.1:6379[1]> LPUSH list1 bob lucy
(integer) 5
127.0.0.1:6379[1]> RPUSH list1 tom
(integer) 3
# 移除列表数据
127.0.0.1:6379[1]> RPOP list1
"jack"
127.0.0.1:6379[1]> LPOP list1
"lucy"
# 查看列表长度
127.0.0.1:6379[1]> LLEN list1
(integer) 5
1.4.3 集合
Set是 String类型的无序集合,集合中的成员是唯一的,这就意味着集合中不能出现重复的数据,可以在两个不同的集合中对数据进行对比并取值。
# 生成集合key
127.0.0.1:6379[1]> SADD set1 v1
(integer) 1
# 追加数值
127.0.0.1:6379[1]> SADD set1 v2 v3
(integer) 2
# 查看集合的所有数据
127.0.0.1:6379[1]> SMEMBERS set1
1) "v2"
2) "v1"
3) "v3"
# 获取集合的差集,已属于A而不属于B的元素称为A与B的差(集)
127.0.0.1:6379> SDIFF set1 set2
1) "v1"
2) "v3"
# 获取集合的交集,已属于A且属于B的元素称为A与B的交(集)
127.0.0.1:6379> SINTER set1 set2
1) "v4"
2) "v2"
# 获取集合的并集,已属于A或属于B的元素为称为A与B的并(集)
127.0.0.1:6379> SUNION set1 set2
1) "v2"
2) "v4"
3) "v1"
4) "v3"
1.4.4 有序集合
Redis 有序集合和集合一样也是string类型元素的集合,且不允许重复的成员,不同的是每个元素都会关联一个 double(双精度浮点型)类型的分数,redis正是通过该分数来为集合中的成员进行从小到大的排序,有序集合的成员 是唯一的,但分数(score)却可以重复,集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是O(1), 集合 中最大的成员数为 2^32 - 1 (4294967295, 每个集合可存储40多亿个成员)。
# 生成有序集合
127.0.0.1:6379[1]> ZADD zset1 1 v1
(integer) 1
127.0.0.1:6379[1]> ZADD zset2 1 v1 2 v2 3 v3
(integer) 3
# 显示集合内所有的key
127.0.0.1:6379[1]> ZRANGE zset2 0 -1
1) "v1"
2) "v2"
3) "v3"
# 显示指定集合内所有key和得分情况
127.0.0.1:6379[1]> ZRANGE zset2 0 -1 WITHSCORES
1) "v1"
2) "1"
3) "v2"
4) "2"
5) "v3"
6) "3"
# 查看集合的长度
127.0.0.1:6379[1]> ZCARD zset1
(integer) 1
# 返回某个数值的索引
127.0.0.1:6379[1]> ZRANK zset1 v1
(integer) 0
1.4.5 哈希
hash 是一个string类型的field和value的映射表,hash特别适合用于存储对象,Redis 中每个 hash 可以存储 2^32 - 1 键值对(40多亿)。
# 生成hash key
127.0.0.1:6379[1]> HSET hash1 name tom age 18
(integer) 2
# 获取hash key字段值
127.0.0.1:6379[1]> HGET hash1 name
"tom"
# 删除一个hash key的字段
127.0.0.1:6379[1]> HDEL hash1 age
(integer) 1
# 获取所有hash表中的key
127.0.0.1:6379[1]> HKEYS hash1
1) "name"
# 获取指定hash的所有key及value
127.0.0.1:6379[1]> HGETALL hash1
1) "name"
2) "tom"
1.5 消息队列
消息队列主要分为两种,分别是生产者消费者模式和发布者订阅者模式,这两种模式Redis都支持。
1.5.1 生产者消费者模式
在生产者消费者(Producer/Consumer)模式下,上层应用接收到的外部请求后开始处理其当前步骤的操作,在执行 完成后将已经完成的操作发送至指定的频道(channel)当中,并由其下层的应用监听该频道并继续下一步的操作, 如果其处理完成后没有下一步的操作就直接返回数据给外部请求,如果还有下一步的操作就再将任务发布到另外一 个频道,由另外一个消费者继续监听和处理。
模式介绍
生产者消费者模式下,多个消费者同时监听一个队里,但是一个消息只能被最先抢到消息的消费者消费,即消息 任务是一次性读取和处理,此模式在分布式业务架构中非常常用,比较常用的软件还有RabbitMQ、Kafka、 RocketMQ、ActiveMQ等。
队列当中的 消息由不同的生产者写入也会有不同的消费者取出进行消费处理,但是一个消息一定是只能被取出一次 也就是被消费一次。
# 生产者发布消息
[root@redis-s4 ~]# redis-cli
127.0.0.1:6379> AUTH 123456
OK
127.0.0.1:6379> LPUSH channel1 msg1 #从管道的左侧写入
(integer) 1
127.0.0.1:6379> LPUSH channel1 msg2
(integer) 2
127.0.0.1:6379> LPUSH channel1 msg3
(integer) 3
127.0.0.1:6379> LPUSH channel1 msg4
(integer) 4
127.0.0.1:6379> LPUSH channel1 msg5
(integer) 5
# 查看队列所有消息
127.0.0.1:6379> LRANGE channel1 0 -1
1) "msg5"
2) "msg4"
3) "msg3"
4) "msg2"
5) "msg1"
# 消费者消费消息
127.0.0.1:6379> RPOP channel1 #从管道的右侧消费
"msg1"
127.0.0.1:6379> RPOP channel1
"msg2"
127.0.0.1:6379> RPOP channel1
"msg3"
127.0.0.1:6379> RPOP channel1
"msg4"
127.0.0.1:6379> RPOP channel1
"msg5"
127.0.0.1:6379> RPOP channel1
(nil)
1.5.2 发布者订阅模式
在发布者订阅者模式下,发布者将消息发布到指定的channel里面,凡是监听该channel的消费者都会收到同样的 一份消息,这种模式类似于是收音机的广播模式,即凡是收听某个频道的听众都会收到主持人发布的相同的消息内容。
此模式常用语群聊天、群通知、群公告等场景。
Subscriber:订阅者
Publisher:发布者
Channel:频道
范例:
# 订阅者监听频道
[root@redis-s4 ~]# redis-cli
127.0.0.1:6379> AUTH 123456
OK
# 订阅者订阅指定的频道
127.0.0.1:6379> SUBSCRIBE channel1
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "channel1"
3) (integer) 1
# 发布者发布消息
127.0.0.1:6379> PUBLISH channel1 test1
(integer) 2
127.0.0.1:6379> PUBLISH channel1 test2
(integer) 2
# 各订阅者验证消息
多个频道订阅
# 订阅多个频道
> SUBSCRIBE channel1 channel2
# 订阅所有频道
127.0.0.1:6379> PSUBSCRIBE *
# 订阅匹配的频道
> PSUBSCRIBE chann*
1.6 Redis 常用命令
1.6.1 CONFIG
config 命令用于查看当前redis配置、以及不重启更改redis配置等
更改最大内存
127.0.0.1:6379[1]> CONFIG SET maxmemory 8589934592
OK
127.0.0.1:6379[1]> CONFIG GET maxmemory
1) "maxmemory"
2) "8589934592"
设置连接密码
127.0.0.1:6379[1]> CONFIG SET requirepass 123456
OK
# 修改完会立即生效
127.0.0.1:6379[1]> CONFIG GET requirepass
(error) NOAUTH Authentication required.
# 输入连接密码
127.0.0.1:6379[1]> AUTH 123456
OK
127.0.0.1:6379[1]> CONFIG GET requirepass
1) "requirepass"
2) "123456"
# 获取当前配置
127.0.0.1:6379[1]> CONFIG GET *
1.6.2 info
显示当前节点redis运行状态信息
127.0.0.1:6379[1]> info
# Server
redis_version:5.0.14
redis_git_sha1:00000000
redis_git_dirty:0
redis_build_id:f551c421707b8bfb
redis_mode:standalone
1.6.3 SELECT
切换数据库,等于MySQL的use DBNAME指令。
127.0.0.1:6379[1]> select 1
OK
1.6.4 keys
查看当前库下的所有key
127.0.0.1:6379[1]> KEYS *
1) "hash1"
2) "num"
3) "zset2"
4) "list1"
5) "key2"
6) "set1"
7) "zset1"
1.6.5 BGSAVE
手动在后台执行RDB持久化操作
127.0.0.1:6379[1]> BGSAVE
Background saving started
1.6.6 DBSIZE
返回当前库下的所有key 数量
127.0.0.1:6379[1]> DBSIZE
(integer) 7
1.6.7 FLUSHDB
强制清空当前库中的所有key
1.6.8 FLUSHALL
强制清空当前redis服务器所有数据库中的所有key,即删除所有数据
2 Redis高可用与集群
虽然Redis可以实现单机的数据持久化,但无论是RDB也好或者AOF也好,都解决不了单点宕机问题,即一旦单台 redis服务器本身出现系统故障、硬件故障等问题后,就会直接造成数据的丢失,因此需要使用另外的技术来解决 单点问题。
2.1 配置redis主从
2.1.1 Slave主要配置
Redis Slave 也要开启持久化并设置和master同样的连接密码,因为后期slave会有提升为master的可能,Slave端切换master同步后会丢失之前的所有数据。
一旦某个Slave成为一个master的slave,Redis Slave服务会清空当前redis服务器上的所有数据并将master的数据 导入到自己的内存,但是断开同步关系后不会删除当前已经同步过的数据。
在命令行配置
# 指定master的地址和端口
127.0.0.1:6379> REPLICAOF 192.168.10.208 6379
# 指定连接master的密码
127.0.0.1:6379> CONFIG SET masterauth 123321
以上命令行配置均可在配置文件当中配置,也比较推荐在命令行当中配置这两个选项
同步日志
查看当前slave信息
127.0.0.1:6379> info
测试数据能否同步
# master写入数据
127.0.0.1:6379> SELECT 2
OK
127.0.0.1:6379[2]> SET key1 value1
OK
# slave查看数据是否同步
127.0.0.1:6379> SELECT 2
OK
127.0.0.1:6379[2]> KEYS *
1) "key1"
2.1.2 主从复制原理
当首次进行主从同步复制其原理如下
- 在2.8版本之后开始使用PSYNC命令去向master同步数据,不过SYNC仍然保留,PSYNC允许部分重新同步
- 后期同步会先发送自己slave_repl_offset位置,只同步新增加的数据,不再全量同步。
- Master的切换会导致master_replid发生变化,slave之前的master_replid就和当前master不一致从而会引发所有 slave的全量同步。
如果主从节点断开,其复制过程如下
- 此时Master会打开缓存池来缓存最近的redis命令,缓存的大小可以在配置文件当中设置,默认缓存池的大小是1MB
- 这时slave会向master发送PSYNC命令和offset(数据传输断开时的位置),并从这个offset点开始传输数据
- 如果slave节点和主节点断开太久,导致无法在缓存种找到offset同步点,这时将会数据执行全量同步
2.2 Redis集群
主从复制的架构解决了Redis单点失败的问题,但是无法实现master和slave的自动切换,也无法横向扩展Redis服务提升写入性能。为了解决这两个问题需用到Sentinel(哨兵)和cluster(集群)技术。
2.2.1 Sentinel(哨兵)
Redis Sentinel解决了主从复制架构无法实现master和slave自动切换问题,Sentinel的功能如下:
- 监控:哨兵会定期检查主和从实例是否还在正常工作
- 提醒:当监测到故障时,哨兵可以通过API将故障反馈给管理员
- 自动进行故障切换:当master发生故障时,哨兵可以自动将一个slave提升为master
- 服务切换通知:哨兵会发现服务并通知给客户端:客户端连接到哨兵来获取当前master的地址。如果发生了故障切换,哨兵会向客户端报告新的master地址
Sentinel已经被集成在redis2.6+的版本中,Redis的哨兵模式到了2.8 版本之后就稳定了下来。一般在生产环境也建议使用Redis的2.8版本的以后版本。Redis Sentinel有两种不同的掉线状态:对于每个Sentinel来说当配置中is-master-down-after-milliseconds
参数指定的秒数内没有收到其他Sentinel对 PING 请求的有效回复时,则暂时认为对方已掉线,这就是所谓的SDOWN;当足够多的 Sentinels(至少配置为quorum
被监控主机的参数的数量)做出了SDOWN判断,并使用该SENTINEL is-master-down-by-addr
命令从其他 Sentinels 获取反馈,得出的Master Server下线判断,这种方式就是ODOWN。
ODOWN条件仅适用于 masters,哨兵(sentinel) 进程,这些进程使用流言协议(gossip protocols)来接收关于Master 主服务器是否下线的信息,并使用投票协议(Agreement Protocols)来决定是否执行自动故障迁移,以及选择哪个Slave作为新的Master。
关于Replication ID
每次当master重新启动或者slave节点提升为master,都会给实例生成一个新的Replication ID,从节点会继承主节点的ID,当两个节点有相同的ID意味着他们的数据内容是一样的,只不过数据的时间点不同,offset则表明两个节点的数据时间点在哪。
Redis实例之所以有两个复制ID是因为当从节点提升为主节点之后,它需要记录下之前主节点的复制ID,这样当其他旧master从节点向新master同步数据的时候,通过对比旧master的复制ID,就只需要同步在slave提升为新master时间点之后的数据。
配置Sentinel
需要手动先指定某一台Redis服务器为master,然后将其他slave服务器使用命令配置为master服务器的slave,哨兵 的前提是已经手动实现了一个redis master-slave的运行环境。
实现一个一主两从基于哨兵的高可用redis架构
# 配置服务slaveA
127.0.0.1:6379> REPLICAOF 192.168.10.208 6379
OK
127.0.0.1:6379> CONFIG SET masterauth "123321"
OK
# 配置服务slaveB
127.0.0.1:6379> REPLICAOF 192.168.10.208 6379
OK
127.0.0.1:6379> CONFIG SET masterauth "123321"
OK
查看当前master状态
编辑sentinel.conf
哨兵可以不和redis服务器部署在一起
# 配置master的sentinel.conf
[root@centos7 ~]# vim /apps/redis/etc/sentinel.conf
bind 0.0.0.0
port 26379
daemonize yes
pidfile "/apps/redis/run/redis-sentinel.pid"
logfile "/apps/redis/logs/sentinel_26379.log"
dir "/apps/redis"
# 法定人数限制(quorum),即有几个slave认为masterdown了就进行故障转移
sentinel monitor mymaster 192.168.10.208 6379 2
sentinel auth-pass mymaster 123321
# (SDOWN)主观下线的时间
sentinel down-after-milliseconds mymaster 30000
# 发生故障转移时候同时向新master同步数据的slave数量,数字越小总同步时间越长
sentinel parallel-syncs mymaster 1
# 所有slaves指向新的master所需的超时时间
sentinel failover-timeout mymaster 180000
# 禁止修改脚本
sentinel deny-scripts-reconfig yes
# 配置第一个slave的sentinel.conf
[root@localhost apps]# vim /apps/redis/etc/sentinel.conf
bind 0.0.0.0
port 26379
daemonize yes
pidfile "/apps/redis/run/redis-sentinel.pid"
logfile "/apps/redis/logs/sentinel_26379.log"
dir "/apps/redis"
sentinel myid b7b1a0d2832e20eda9be5265c5300090adc6f6ec
sentinel deny-scripts-reconfig yes
sentinel monitor mymaster 192.168.10.208 6379 2
# 配置第二个slave的sentinel.conf
[root@localhost apps]# vim /apps/redis/etc/sentinel.conf
bind 0.0.0.0
port 26379
daemonize yes
pidfile "/apps/redis/run/redis-sentinel.pid"
logfile "/apps/redis/logs/sentinel_26379.log"
dir "/apps/redis"
sentinel myid b7b1a0d2832e20eda9be5265c5300090adc6f6ec
sentinel deny-scripts-reconfig yes
sentinel monitor mymaster 192.168.10.208 6379 2
# 启动三台服务器哨兵
[root@centos7 ~]# redis-sentinel /apps/redis/etc/sentinel.conf
[root@localhost apps]# redis-sentinel /apps/redis/etc/sentinel.conf
[root@localhost apps]# redis-sentinel /apps/redis/etc/sentinel.conf
查看哨兵端口
查看redis master状态
root@centos7 ~]# redis-cli
127.0.0.1:6379> auth 123321
OK
127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:2
slave0:ip=192.168.10.210,port=6379,state=online,offset=461588,lag=1
slave1:ip=192.168.10.209,port=6379,state=online,offset=461731,lag=1
master_replid:6e19a2c25811c406633d0264d8f5c77abeb3b18c
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:461731
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:461731
查看Sentinel状态
[root@centos7 ~]# redis-cli -h 192.168.10.208 -p 26379
192.168.10.208:26379> info sentinel
# Sentinel
sentinel_masters:1
sentinel_tilt:0
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
sentinel_simulate_failure_flags:0
master0:name=mymaster,status=ok,address=192.168.10.208:6379,slaves=2,sentinels=3
故障自动切换测试
先查看master信息
# master
[root@centos7 ~]# redis-cli
127.0.0.1:6379> AUTH 123321
OK
127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:2
slave0:ip=192.168.10.210,port=6379,state=online,offset=509229,lag=0
slave1:ip=192.168.10.209,port=6379,state=online,offset=509229,lag=0
# 注意此时master_replid
master_replid:6e19a2c25811c406633d0264d8f5c77abeb3b18c
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:509229
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:509229
[root@centos7 ~]# redis-cli -h 192.168.10.208 -p 26379
192.168.10.208:26379> info sentinel
# Sentinel
sentinel_masters:1
sentinel_tilt:0
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
sentinel_simulate_failure_flags:0
master0:name=mymaster,status=ok,address=192.168.10.208:6379,slaves=2,sentinels=3
停止master服务
[root@centos7 ~]# systemctl stop redis
查看slave日志
此时两个在线的slave和宕机master的配置文件都发生了更改
当前slave信息
[root@localhost apps]# redis-cli
127.0.0.1:6379> auth 123321
OK
127.0.0.1:6379> info replication
# Replication
role:slave
# 新master地址
master_host:192.168.10.210
master_port:6379
master_link_status:up
master_last_io_seconds_ago:0
master_sync_in_progress:0
slave_repl_offset:751517
slave_priority:100
slave_read_only:1
connected_slaves:0
# 故障切换后当前master_replid
master_replid:09a678b9efaa7d8ffa679edee1472acf81fe699a
# 原来master的master_replid
master_replid2:6e19a2c25811c406633d0264d8f5c77abeb3b18c
master_repl_offset:751517
second_repl_offset:565807
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1753
repl_backlog_histlen:749765
2.2.2 Redis Cluster介绍
在哨兵sentinel机制中,解决了redis高可用问题,即当master故障后可以自动将slave提升为master从而可以 保证redis服务的正常使用,但是无法解决redis单机写入的瓶颈问题,即单机的redis写入性能受限于单机的内存大 小、并发数量、网卡速率等因素,因此redis官方在redis 3.0版本之后推出了无中心架构的redis cluster机制,在无 中心的redis集群当中,其每个节点保存当前节点数据和整个集群状态,每个节点都和其他所有节点连接,特点如 下:
- Redis cluster预先分配16384个(slot)槽位,当需要在redis集群中写入一个key -value的时候,会使用 CRC16(key) mod 16384之后的值,决定将key写入值哪一个槽位从而决定写入哪一个Redis节点上,从而有效解决单 机瓶颈。
- 每个节点都会被分配一部分槽位,读写需要到指定的redis node上进行 操作,因此有多少个reids node相当于redis 并发扩展了多少倍。
- 集群中某个节点的失效,是整个集群中超过半数的节点监测都失效才算真正的失效
Redis cluster架构
Redis集群的16384个哈希槽(hash slot)会被分到各个主节点上,例如:当有一个包含3个节点的集群,其节点分配如下:
节点 A 包含从 0 到 5500 的哈希槽
节点 B 包含从 5501 到 11000 的哈希槽
节点 C 包含从 11001 到 16383 的哈希槽
但这时如果B节点故障,那么集群也就不能继续工作,因为5501-11000范围内的哈希槽缺失,这个范围(CRC16(key) mod 16384后在5501-11000区间)内key由于找不到对应槽位,使得集群不能正常工作。
但是如果为每个集群节点添加一个从节点,这样集群就由A、B、C主节点和A1、B1、C1从节点组成。如果B节点故障,那么集群会将B1提升为新的主节点,集群也能继续正常提供服务。
2.2.3 创建Redis Cluster
创建前的注意事项
当我们在创建Redis Cluster时应当保证每个redis node的节点采用相同的硬件配置,相同的密码和相同版本的Redis,且应当保证Redis服务器没有任何数据。
环境配置:
这里由于资源不足所以在同一台服务器上部署两个redis实例,让同一台服务器两个redis实例分别监听不同端口,正式环境当中应该保证3主3从分别部署在6台服务器上以实现高可用
192.168.10.208 6379/6380
192.168.10.209 6379/6380
192.168.10.210 6379/6380
预留一台服务器做集群添加节点测试。
192.168.10.211
准备创建集群
# 在三台服务安装redis实例,redis.conf最小配置如下
port 6379
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
appendonly yes
# 使用命令启动实例
[root@redis1 ~]# /apps/redis/bin/redis-server /apps/redis/redis.conf
验证服务状态
创建集群
针对于Redis 5和以上的版本可以使用redis-cli
命令来创建集群,而对于低于5版本的则要用到redis-trib.rb
,这个命令在Src目录中,不过要运行redis-trib.rb
还需要redis gem的支持。
# 注意redis-trib.rb是用ruby写的,要保证ruby版本大于2.3.0,否则无法进行安装
gem install redis
由于这次安装的redis版本是5.0.3,所以使用redis-cli
来创建和管理集群。虽然3、4和5的命令不一样,但是在使用方式上面它们是十分相似的。而且还可以使用redis-trib.rb help
命令来查看语法帮助
# redis-cli语法
[root@redis1 ~]# /apps/redis/bin/redis-cli --cluster help
Cluster Manager Commands:
create host1:port1 ... hostN:portN
--cluster-replicas <arg>
check host:port
--cluster-search-multiple-owners
info host:port
fix host:port
--cluster-search-multiple-owners
reshard host:port
--cluster-from <arg>
--cluster-to <arg>
--cluster-slots <arg>
--cluster-yes
--cluster-timeout <arg>
--cluster-pipeline <arg>
--cluster-replace
rebalance host:port
--cluster-weight <node1=w1...nodeN=wN>
--cluster-use-empty-masters
--cluster-timeout <arg>
--cluster-simulate
--cluster-pipeline <arg>
--cluster-threshold <arg>
--cluster-replace
add-node new_host:new_port existing_host:existing_port
--cluster-slave
--cluster-master-id <arg>
del-node host:port node_id
call host:port command arg arg .. arg
set-timeout host:port milliseconds
import host:port
--cluster-from <arg>
--cluster-copy
--cluster-replace
help
For check, fix, reshard, del-node, set-timeout you can specify the host and port of any working node in the cluster.
使用命令创建集群
[root@redis1 ~]# /apps/redis/bin/redis-cli --cluster create 192.168.10.208:6379\ 192.168.10.208:6380 192.168.10.209:6379 192.168.10.209:6380 \ 192.168.10.210:6379 192.168.10.210:6380 --cluster-replicas 1
集群创建过程
出现下图表示创建成功
验证集群的主从状态
验证集群状态
# 查看集群信息
127.0.0.1:6379> CLUSTER INFO
cluster_state:ok
cluster_slots_assigned:16384
cluster_slots_ok:16384
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:6
cluster_size:3
cluster_current_epoch:6
cluster_my_epoch:1
cluster_stats_messages_ping_sent:985
cluster_stats_messages_pong_sent:920
cluster_stats_messages_sent:1905
cluster_stats_messages_ping_received:915
cluster_stats_messages_pong_received:985
cluster_stats_messages_meet_received:5
cluster_stats_messages_received:1905
# 查看集群节点信息
127.0.0.1:6379> cluster nodes
14b69dd9f55dc0a609df847d0cc8392361574967 192.168.10.210:6379@16379 master - 0 1641466952000 5 connected 10923-16383
24b8807c758fc9c1afb02ae1081b4163333a5791 192.168.10.208:6380@16380 slave 14b69dd9f55dc0a609df847d0cc8392361574967 0 1641466952606 5 connected
a100cb5003452c76920d883df64839dc2ca33be6 192.168.10.210:6380@16380 slave 7bf76194fdb37629aa3de7465fde8c9e10d643e6 0 1641466952000 6 connected
7bf76194fdb37629aa3de7465fde8c9e10d643e6 192.168.10.209:6379@16379 master - 0 1641466951000 3 connected 5461-10922
bd49155572a324deef3522fa74fd63f069eddce2 192.168.10.209:6380@16380 slave a3ed693cbd2b0316dbd1991493021f8d39e8f78f 0 1641466953609 4 connected
a3ed693cbd2b0316dbd1991493021f8d39e8f78f 192.168.10.208:6379@16379 myself,master - 0 1641466950000 1 connected 0-5460
在命令行当中查看集群信息
[root@redis1 ~]# /apps/redis/bin/redis-cli --cluster check 192.168.10.208:6379
写入key验证集群
[root@redis1 ~]# /apps/redis/bin/redis-cli
127.0.0.1:6379> SET key1 value1
# 经过算法计算,当前key的槽位需要写入指定的node,槽位不在当前node所以无法写入
(error) MOVED 9189 192.168.10.209:6379
# 指定的node就可以写入
127.0.0.1:6379> SET key2 value1
OK
2.2.4 集群的动态维护
动态添加节点
为了满足更高的读写并发需求,有时需要新添加节点,应注意新加节点应和之前Redis版本、硬件配置保持一致。添加新节点主要分为两步
- 将新节点加入集群
- 给新节点划分槽位
范例:
为公司现有集群新添加一台服务器,从而满足更高的并发写入需求。
# 启动redis服务
[root@ansible ~]# /apps/redis/redis/bin/redis-server /apps/redis/redis/redis.conf
[root@ansible ~]# /apps/slave/redis/bin/redis-server /apps/slave/redis/redis.conf
# 添加节点到集群,注意第一个地址是新节点的地址,第二个地址是任意一个集群主节点的地址
[root@redis1 ~]# /apps/redis/bin/redis-cli --cluster add-node 192.168.10.211:6379 192.168.10.208:6379
验证集群信息
[root@redis1 ~]# /apps/redis/bin/redis-cli --cluster check 192.168.10.208:6379
对新加的主机分配槽位
[root@redis1 ~]# /apps/redis/bin/redis-cli --cluster reshard 192.168.10.208:6379
>>> Performing Cluster Check (using node 192.168.10.208:6379)
......
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
# 指定要分配的槽位个数
How many slots do you want to move (from 1 to 16384)? 4096
# 指定要分配槽位的集群节点ID
What is the receiving node ID? d8683a3ba8499c6e37fd9a13390e5addb92d2ed0
Please enter all the source node IDs.
# all表示从所有节点选择划分,done可以自己指定集群节点ID
Type 'all' to use all the nodes as source nodes for the hash slots.
Type 'done' once you entered all the source nodes IDs.
Source node #1: all
# 后续会自动完成槽位分配
再次查看集群信息
为新的master添加slave节点
# 下面这条命令会自动将新的节点作为从节点分配给从节点数量最少的master
[root@redis1 ~]# /apps/redis/bin/redis-cli --cluster add-node 192.168.10.211:6380 192.168.10.208:6379 --cluster-slave
验证集群的状态
文档参考链接:
官方文档:https://redis.io/documentation
Redis主从复制原理:https://www.fatalerrors.org/a/redis-master-slave-principle-sentry-mode.html