第四章 Redis 高级话题
1. redis事务
1.1 什么是事务
事务是指一系列操作步骤。这一系列的操作步骤,要么完全地执行,要么完全地不执行。
redis中的事务(transaction)是一组命令的集合,至少是两个或两个以上的命令,redis事务保证这些命令被执行时中间不会被任何其他操作打断。
对比sql,sql是执行不成功的时候才回滚。
1.2 事务操作的命令
(1)mulit
- 语法:multi
- 作用:标记一个事务的开始。事务内的多条命令会按照先后顺序被放进一个队列当中。
- 返回值:总是返回ok
(2)exec
- 语法:exec
- 作用:执行所有事务块内的命令。
- 返回值:事务内的所有执行语句内容,事务被打断,返回nil
(3)discard
- 语法:discard
- 作用:取消事务,放弃执行事务块内的所有命令
- 返回值:总是返回ok
(4)watch
- 语法:watch key [key …]
- 作用:监视一个(或多个)key,如果在事务执行之前这个(或这些)key被其他命令所改动,那么事务将被打断。
- 返回值:总是返回ok
(5)unwatch
- 语法:unwatch
- 作用:取消watch命令对所有key的监视。如果在执行watch命令之后,exec命令或discard命令先被执行了的话,那么就不需要在执行unwatch了
- 返回值:总是返回ok
1.3 事务的实现
(1)正常执行事务
- 事务的执行步骤:
- 首先开启事务
- 其次向事务队列中加入命令
- 最后执行事务提交
## 清空数据库
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> keys *
(empty array)
## 开启事务
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> sadd works john
QUEUED # 代表入队
127.0.0.1:6379(TX)> sadd works rose
QUEUED
127.0.0.1:6379(TX)> smembers works
QUEUED
## 执行事务
127.0.0.1:6379(TX)> exec
1) (integer) 1
2) (integer) 1
3) 1) "rose"
2) "john"
Redis-cli客户端的命令要发给redis-server来执行,mulit命令执行后,在服务端开启一个小内存空间,存放每条命令。
(2)事务执行exec之前,入队命令错误(语法错误)
严重错误导致服务器不能正常工作(例如内存不足),放弃事务。
- 执行事务步骤:
- multi 正常命令
- set key value 正常错误
- incr 命令语法错误
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> set name lisi
QUEUED
127.0.0.1:6379(TX)> get name
QUEUED
127.0.0.1:6379(TX)> incr k1 k2
(error) ERR wrong number of arguments for 'incr' command
(3)事务执行exec命令后,命令执行错误,事务
- 执行步骤:
- multi正常命令。
- set username zhangsan正常命令
- lpop username 正常命令(移除并返回列表中第一个元素),语法没有错误,执行命令时才会有错误。
- exec正常执行,发现错误可以在事务提交前放弃事务,执行discard。
在exec执行后产生的错误,即使事务中有某个/某些命令在执行时产生了错误,事务中的其他命令仍然会继续执行。
redis在事务失败时不进行回滚,而是继续执行余下的命令。
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> set name lisi
QUEUED
127.0.0.1:6379(TX)> lindex k1 1 ## 语法上没错,执行时出错。
QUEUED
127.0.0.1:6379(TX)> get name
QUEUED
127.0.0.1:6379(TX)> exec
1) OK
2) (error) WRONGTYPE Operation against a key holding the wrong kind of value
3) "lisi"
(4)放弃事务
- 执行步骤:
- multi 开启事务
- set age 25 命令入队
- set age 30 命令入队
- discard放弃事务,则命令队列不会被执行。
127.0.0.1:6379> set age 10
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> set age 20
QUEUED
127.0.0.1:6379(TX)> set age 30
QUEUED
127.0.0.1:6379(TX)> discard ## 放弃事务
OK
127.0.0.1:6379> get age
"10"
(5)redis的watch机制
1. 原理
- 原理:
- watch机制:使用watch监视一个或多个key,跟踪key的value修改情况,如果有key的value值在事务exec执行之前被修改了,整个事务被取消。exec返回提示信息,表示事务已经失败。
- watch机制使事务exec变的有条件,事务只有在被watch的key没有修改的前提下才能执行。不满足条件,事务被取消。使用watch监视了一个带过期时间的键,那么即使这个键过期了,事务仍然可以正常执行。
为了高效,redis并没有给数据上锁。
- 大多数情况下,不同的客户端会访问不同的键,相互同时竞争同一key的情况一般都很少,watch能很好解决数据冲突的问题。
2. 何时取消key的监视(watch)?
- watch命令可以被调用多次。对键的监视从watch执行之后开始生效,直到调用exec为止。不管事务是否成功执行,对所有键的监视都会被取消。
- 当客户端断开连接时,该客户端对键的监视也会被取消。
- unwatch命令可以手动取消对所有键的监视。
3.watch的事例
-
执行步骤
- 首先启动redis-server,在开启两个客户端连接。分别叫A客户端和B客户端。
- 启动redis服务器。
客户端a,设置登录人数10,监视。开启事务设置为11
客户端b,修改为15,客户端a
事务还未执行exec,另外一个客户端修改了数据,事务提交返回nil,代表事务中断。
2.持久化
2.1 持久化概述
- 持久化可以理解为存储,就是将数据存储到一个不会丢失的地方,如果把数据放在内存中,电脑关闭或重启数据就会丢失,所以放在内存中的数据不是持久化的,而放在磁盘就算是一种持久化
- redis的数据存储在内存中,内存是瞬时的,如果linux宕机或重启,又或者redis崩溃或重启,所有的内存数据都会丢失,为解决这个问题,redis提供两种机制对数据进行持久化储存,便于发生故障后迅速恢复数据。
2.2 持久化方式
(1)RDB方式
-
什么是RDB方式?
- redis database (rdb),就是在指定的时间间隔内将内存中的数据集快照写入磁盘,数据恢复时将快照文件直接再度到内存。
- rdb保存了某个时间点的数据集(全部数据)。
- 储存在一个二进制文件中,只有一个文件。默认是dump.rdb。
- rdb技术非常适合做备份,可以保存最近一个小时,一天,一个月的全部数据。
- 保存数据是在单独的进程中写文件,不影响redis的正常使用。
- rdb恢复数据时比其他aof速度快。
-
如何实现?
-
rdb方式的数据持久化,仅需在redis.conf文件中配置即可,默认配置是启用的。
-
在配置文件redis.conf中搜索SNAPSHOTTING,查找在注释开始和结束之间的关于rdb的配置说明。配置SNAPSHOTTING地方有三处。
-
配置执行rdb生成快照文件的时间策略。
- 对redis进行设置,让它在"n秒内数据集至少有M个key改动"这一条件被满足时,自动保存一次数据集。
save <seconds> <changes> save 900 1 ## save 20 3 如果20秒钟只保存了2个数据就有丢失数据的可能
-
dbfilename:设置rdb的文件名,默认围巾啊名为dump.rdb
-
dir:指定rdb文件的储存位置,默认是./当前目录。
配置步骤: ## 如果redis服务启动,先停止 ps -ef | grep redis ## 修改redis.conf文件,修改前先备份 cp redis.conf bak_redis.conf ## 查看默认启用的rdb文件 ll dump.rdb
-
(2)AOF方式
-
什么是AOF方式?
Append-only file, redis每次接收一条改变数据的命令时,它将把该命令写到一个AOF文件中(只记录写操作,读操作不记录),当redis重启时,它通过执行aof文件中所有的命令来恢复数据。
-
如何实现
- aof方式的数据持久化,仅需在redis.conf文件中配置即可
- 配置项
- appendonly:默认是no,改成yes即开启了aof持久化。
- appendfilename: 指定AOF文件名,默认文件名为appendonly.aof
- dir: 指定RDB和AOF文件存放的目录,默认是 ./
- appendfsync: 配置向aof文件写命令数据的策略。
- no: 不主动进行同步操作,而是完全交由操作系统来做(即每30秒一次),比较快但不是很安全。
- always:每次执行写入都会执行同步,慢一些但是比较安全。
- everysec :每秒执行一次同步操作,比较平衡,介于速度和安全之间。这是默认项。
- Auto-aof-rewrite-min-size : 允许重写的最小AOF文件大小,默认是64M。当aof文件大于64M时,开始整理aof文件,去掉无用的操作命令。缩小aop文件。
aof的重写机制:把无效命令删掉,减小文件的大小。
(3)总结
- aof能确保数据的安全性,性能上没有rdb快
- aof是文本文件,占得空间大,读写速度比rdb要慢
3.主从复制(读写分离)
3.1 主从复制-读写分离
通过持久化的功能,redis保证了即使在服务器重启的情况下也不会丢失(或少量丢失)数据,但是由于数据是存储在一台服务器上的,如果这台服务器出现故障,比如硬盘坏了,也会导致数据丢失。
为了避免单点故障,需要将数据复制多份部署在多台不同的服务器上,即使有一台服务器出现故障,其他服务器依然可以继续提供服务。
要求当一台服务器上的数据更新后,自动将更新的数据同步到其他服务器上,可以使用redis的主从复制。
- redis提供了复制功能来自动实现多台redis服务器的数据同步。
- 可以通过部署多台redis,并配置文件中指定这几台redis之间的主从关系。
- 主负责写入数据,同时把写入的数据实时同步到从机器,这种模式叫做主从复制,即master/slave
- 并且redis默认master用于写
- slave用于读,向slave写数据会导致错误
(1)新建三个redis的配置文件
如果redis启动,先停止
作为master的redis的端口是6380
作为slaver的redis端口分别是6382,6384
redis.conf 拷贝三份,redis6380.conf\redis6382.conf\redis6384.conf
## 复制配置文件
$ cp redis.conf redis6380.conf
## 重定向清空文件
$ > redis6380.conf
## 输出文件发现清空
$ cat redis6380.conf
## 复制文件
$ cp redis6380.conf redis6384.conf
(2)编辑Master配置文件
编辑Master的配置文件redis6380.conf : 在空文件加入如下内容
include /usr/local/redis-4.0.13/redis.conf
daemonize yes
port 6380
pidfile /var/run/redis_6380.pid
logfile 6380.log
dbfilename dump6380.rdb
配置项说明
## 包含原来的配置文件内容。按照自己的目录设置
include /usr/local/etc/redis6380.conf
## 后台启动应用,相当于 ./redis-server &的作用
daemonize yes
## 自定义端口号
port 6380
## 自定义的文件,表示当前程序的pid,进程id.
pidfile /var/run/redis_6380.pid
## 日志文件
logfile 6380.log
## rdb持久化的文件名称。
dbfilename dump6380.rdb
(3)编辑Slave配置文件
include /usr/local/etc/redis6382.conf
daemonize yes
port 6382
pidfile /var/run/redis_6382.pid
logfile 6382.log
dbfilename dump6382.rdb
slaveof 127.0.0.1 6380
## 主ip 主端口
include /usr/local/etc/redis6384.conf
daemonize yes
port 6384
pidfile /var/run/redis_6384.pid
logfile 6384.log
dbfilename dump6384.rdb
slaveof 127.0.0.1 6380
(4)启动文件
./redis-server ../redis6380.conf
./redis-server ../redis6382.conf
./ redis-server ../redis6384.conf
## mac下我没有启动成功 linux下应该没问题
## mac在redis/bin下新建终端 执行命令 报 Segmentation fault: 11 暂时无解
./redis-server /usr/local/etc/redis6380.conf
./redis-server /usr/local/etc/redis6382.conf
./redis-server /usr/local/etc/redis6384.conf
## 欢迎大佬提示。
FVFC81YQL414:bin bytedance$ ps -ef | grep redis
501 555 1 0 9:57上午 ?? 0:32.83 /usr/local/opt/redis/bin/redis-server 127.0.0.1:6379
501 18565 18522 0 7:36下午 ttys001 0:00.01 grep redis
(5)连接客户端
redis-cli -p 6380
## 客户端查看主从信息
info replication
新建终端窗口
redis-cli -p 6382
## 客户端查看主从信息
info replication
redis-cli -p 6384
## 客户端查看主从信息
info replication
3.2 容灾处理
当master服务出现故障,需手动将slave中的一个提升为master,剩下的slave挂至新的master上(冷处理:机器挂掉,再处理)
命令:
## 将一台slave服务器升至为master(提升至slave为master)
slaveof no one
## 将 Master 6380 停止 模拟挂掉
shutdown
## 查看所有redis进程
ps -ef |grep redis
## 链接到6380
./redis-cli -p 6380
## 展示关键信息
info replication
## 将slave 挂至新的master上。
slaveof 127.0.0.1 6382
## 查看主从信息发现编程从
info replication
## 在6384 上
set k7 v7
## 会报错 读写分离,不能写入 , 6832为主 可以写入
3.3 高可用Sentine哨兵
Sentine哨兵是redis官方提供的高可用方案,可以用它来监控多个redis服务实例的运行情况。Redis Sentinel是一个运行在特殊模式下的redis服务器。Redis Sentinel是在多个Sentinel进程环境下互相协作工作的。
Sentinel系统有三个主要任务:
- 监控:Sentinel不断的检查主服务器和从服务器是否按照预期正常工作。
- 提醒:被监控的redis出现问题时,Sentinel会通知管理员或其他应用程序。
- 自动故障转移:监控的主redis不能正常工作,Sentinel会开始进行故障迁移操作。将一个从服务器升级新的主服务器,让其他从服务器挂到新的主服务器。Redis Sentinel是在多个Sentinel进程环境下互相协助工作的。
多个sentinel哨兵之间可以相互交流,哨兵可以监控主从redis,当两个或多个哨兵监测到哨兵不能正常工作后,就会进行故障处理。
少数服从多数原则:认为主服务器出现故障的哨兵多于判定为无故障的哨兵数量时,即可判定出现故障。
(1)哨兵的配置
我用brew安装的redis,哨兵的配置文件在mac的local/etc中。redis-sentinel.conf
# Example sentinel.conf
port 26379
daemonize no
# location here.
pidfile /var/run/redis-sentinel.pid
# output for logging but daemonize, logs will be sent to /dev/null
logfile ""
# sentinel announce-ip <ip>
# unmounting filesystems 哨兵存储文件的目录
dir /tmp
# The valid charset is A-z 0-9 and the three characters ".-_".
# 哨兵 监控 两个关键字 2 是redis哨兵认为主redis不能工作生效的数量(投票数)
sentinel monitor mymaster 127.0.0.1 6379 2
# Default is 30 seconds.
sentinel down-after-milliseconds mymaster 30000
acllog-max-len 128
# Default is 3 minutes.
sentinel failover-timeout mymaster 180000
SENTINEL resolve-hostnames no
SENTINEL announce-hostnames no
复制三份
redis-sentinel26380.conf
redis-sentinel26382.conf
redis-sentinel26384.conf
开三个shell窗口
## 复制哨兵文件
cp sentinel.conf sentinel26380.conf
cp sentinel.conf sentinel26382.conf
cp sentinel.conf sentinel26384.conf
vim sentinel26380.conf
## 修改port
## 修改 监控信息 主是6382
port 26380
sentinel monitor mymaster 127.0.0.1 6382 2
## 在不同终端下重启redis
./redis-cli -p 6384
## 在linux不同终端上启动哨兵 (出于保护mac系统,建议虚拟机操作)
./redis-sentinel ../sentinel26380.conf
./redis-sentinel ../sentinel26382.conf
./redis-sentinel ../sentinel26384.conf
## 停掉 主 6382
shutdown
## 哨兵把从升级为主
## 切换到 6380redis终端
info replication
set k7 v7
## 加入成功 证明变为主
## 切换成 6382
set k8 v8 ## 失败(主从读写分离)
## 通过6382验证主从信息
./redis-server ../redis6382.conf
./redis-cli -p 6382
info replication
哨兵的高可用方案:
哨兵自动化进行故障处理,哨兵是高度自动化的,可以减轻管理员的工作,但是哨兵本身也是程序,也有故障的几率,也不能完全都依赖哨兵。一般系统都会使用,但是哨兵系统应该是独立的,单个哨兵放到独立服务器上,不能影响其他哨兵,或者独立的虚拟机中。
4.安全设置
(1)设置密码
1.开启密码设置
- 访问redis默认是没有密码的,这样不安全,任意用户都可以访问。可以启用使用安全密码才能访问redis.
- 修改 redis.conf 中 requirepass。
- redis速度相当快,外部用户可以一秒钟进行150k次的密码尝试,需要复杂密码防止密码爆破。也可以定期修改密码。
vim redis.conf
## vim中搜索 shift+:
/requirepass
## 把注释去掉
requirepass 123456
2.访问有密码的redis
-
连接到客户端后,使用命令auth密码,命令执行成功后,可以正常使用redis。
./redis-cli auth 123456
-
在连接客户端时使用 -a密码。例如 ./redis-cli -h ip -p port -a password
(2)修改默认端口
- 修改redis端口,使用默认端口是十分危险的。
- redis.conf中修改port 6379。
- 将其修改为自己指定的端口,端口1024是保留给操作系统用的。
- 用户可以使用的范围是 1024-65535
也可以使用 -p 参数指定端口,例如:./redis-cli -p 新设置的端口号