Redis高级
一、事务
概念
事务是指一系列操作步骤,这一系列的操作步骤,要么完全地执行,要么完全地不执行。
Redis 中的事务(transaction)是一组命令的集合,至少是两个或两个以上的命令,redis事务保证这些命令被执行时中间不会被任何其他操作打断。
相关命令
- multi:标记一个事务的开始。事务内的多条命令会按照先后顺序被放进一个队列当中。
- exec:执行所有事务块内的命令。
- discard:取消事务,放弃执行事务块内的所有命令。
- watch:监视一个(或多个) key ,如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断。
- unwatch:取消 WATCH 命令对所有 key 的监视。如果在执行 WATCH 命令之后, EXEC 命令或 DISCARD 命令先被执行了的话,那么就不需要再执行 UNWATCH 了。
事务的执行
正常
事务的执行步骤: 首先开启事务, 其次向事务队列中加入命令,最后执行事务提交。
multi
sadd user tom
sadd user jack
exec
异常
事务行 执行 exec 之前 ,入队命令错误 (语法 错误;严重 错误 导致 服务器不能正常工作 例如 内存不足 ) ,放弃事务 。
multi
sadd user tom
saad user jack //错误的语法
exec
例外
事务行 执行 exec 命令后,执行队列命令,命令执行错误,事务提交。
multi
set username tom
incr username
set userpass abc
exec
在exec执行后的所产生的错误, 即使事务中有某个/某些命令在执行时产生了错误,事务中的其他命令仍然会继续执行。事务没有回滚。Redis 在事务执行过程的错误情况做出了权衡取舍,那就是放弃了回滚。
Redis 官方文档对此给出的解释是:
1、Redis 操作失败的原因只可能是语法错误或者错误的数据库类型操作,这些都是在开发层面能发现的问题不会进入到生产环境,因此不需要回滚。
2、Redis 内部设计推崇简单和高性能,因此不需要回滚能力。
放弃
如果发现命令有错,可以在执行exec之前,执行discard放弃此次事务。
multi
set username tom
incr username
set userpass abc
discard
watch
锁
- 悲观锁:每次操作数据都悲观的认为别人会操作这个数据,所以每次操作数据之前先给数据上锁。这样别人想操作这个数据就会block(阻塞)。
- 乐观锁:乐观的任务认为在操作数据是别人不会操作这个数据。所以不会给数据上锁。但是如何判断是否有人在我操作之前修改了数据?一般使用version字段或者使用time字段来判断。如果没有被修改,我就执行操作。如果被修改了,我就放弃操作。
redis的watch机制
WATCH 机制:使用 WATCH 监视一个或多个 key , 跟踪 key 的 value 修改情况, 如果有key 的 value 值在事务 EXEC 执行之前被修改了, 整个事务被取消。EXEC 返回提示信息,表示事务已经失败。
注意:使用 WATCH 监视了一个带过期时间的键, 那么即使这个键过期了, 事务仍然可以正常执行
何时取消对key的监视?
① WATCH 命令可以被调用多次。 对键的监视从 WATCH 执行之后开始生效,直到调用 EXEC 为止。不管事务是否成功执行, 对所有键的监视都会被取消。
② 当客户端断开连接时, 该客户端对键的监视也会被取消。
③ UNWATCH 命令可以手动取消对所有键的监视
A客户端 B客户端
set money 500
watch money
multi
set money 1500 set money 2000
exec
二、发布和订阅
概念
发布订阅数一种系统之间通信,传递数据的技术手段。特别是在异构系统(不同语言)之间作用非常明显。
Redis的发布订阅机制包括三个部分,发布者,订阅者和Channel。
发布订阅类似微信中的关注公众号,公众号发送信息或者文章。订阅者及时获取到最新的内容。
发布订阅数一对多的关系。
发布:内容的提供者,把内容信息发送给多个订阅者。
订阅:对某个内容感兴趣,只要订阅的内容发生变化就会立即得到通知。
发布订阅应用在及时通信应用中较多,比如聊天室、滴滴打车的订单、外卖的订单,微信的群聊消息等。
相关命令
- publish:将msg发送到指定的chanel(频道名称是自定义的)。
- subscribe:订阅一个或多个频道。
- unsubscribe:退订一个或多个频道。
启动三个窗口链接redis(一个发布者 二个订阅者)。
注意:要先启动订阅者,等待发布者发布消息。
三、持久化
持久化可以理解为存储,就是将数据存储到一个不会丢失的地方,如果把数据放在内存中,电脑关闭或重启数据就会丢失,所以放在内存中的数据不是持久化的,而放在磁盘就算是一种持久化。
Redis 的数据存储在内存中,内存是瞬时的,如果 linux 宕机或重启,又或者 Redis 崩溃或重启,所有的内存数据都会丢失,为解决这个问题,Redis 提供两种机制对数据进行持久化存储,便于发生故障后能迅速恢复数据。
RDB
Redis Database(RDB),就是在指定的时间间隔内将内存中的数据集快照写入磁盘,数据恢复时将快照文件直接再读到内存。
使用一个新Fork的进程来进行数据保存(写)操作。
Fork的作用:复制一个当前进程(包括当前进程的所有数据信息),新建立的进程为原进程的子进程。
RDB 保存了在某个时间点的数据集(全部数据)。存储在一个二进制文件中,只有一个文件。默认是 dump.rdb。
RDB 技术非常适合做备份,可以保存最近一个小时,一天,一个月的全部数据。保存数据是在单独的进程中写文件,不影响 Redis 的正常使用。RDB 恢复数据时比 AOF 速度快。
配置
RDB 方式的数据持久化,仅需在 redis.conf 文件中配置即可,默认配置是启用的。
- save 900 1:配置执行 RDB 生成快照文件的时间策略。
对 Redis 进行设置, 让它在“ N 秒内数据集至少有 M 个 key 改动”这一条件被满足时,自动保存一次数据集。
配置格式:save
save 900 1
save 300 10
save 60 10000
- dbfilename dump.rdb:设置 RDB 的文件名,默认文件名为 dump.rdb
- dir ./:指定 RDB 文件的存储位置,默认是 ./ 当前目录。
总结
优点:
-
由于存储的是数据快照文件,恢复数据很方便,也比较快。
缺点:
-
会丢失最后一次快照以后更改的数据。如果你的应用能容忍一定数据的丢失(比如用作缓存时),那么使用rdb是不错的选择;如果你不能容忍一定数据的丢失,使用 rdb 就不是一个很好的选择。
-
由于需要经常操作磁盘,RDB 会分出一个子进程(去写文件)。如果你的 redis 数据库很大的话,子进程占用比较多的时间,并且可能会影响 Redis 暂停服务一段时间(millisecond 级别),如果你的数据库超级大并且你的服务器 CPU 比较弱,有可能是会达到一秒。
AOF
Append-only File(AOF),Redis 每次接收到一条改变数据的命令时,它将把该命令写到一个 AOF 文件中(只记录写操作,读操作不记录),当 Redis 重启时,它通过执行 AOF 文件中所有的命令来恢复数据。
如果同时配置了AOF和RDB,则只加载AOF文件恢复数据。
关闭RDB:将配置文件中关于save配置信息全部注释。
配置
AOF 方式的数据持久化,仅需在 redis.conf 文件中配置即可。
- appendonly:默认是 no,改成 yes 即开启了 aof 持久化。
- appendfilename:指定 AOF 文件名,默认文件名为 appendonly.aof
- dir:指定 RDB 和 AOF 文件存放的目录,默认是 ./
- appendfsync:配置向 aof 文件写命令数据的策略。
no:不主动进行同步操作,而是完全交由操作系统来做(即每 30 秒一次),比较快但不是很安全。
always:每次执行写入都会执行同步,慢一些但是比较安全。
everysec:每秒执行一次同步操作,比较平衡,介于速度和安全之间。这是默认项
- auto-aof-rewrite-percentage 100
- auto-aof-rewrite-min-size 64M
AOF的Rewrite(重写) :AOF采用文件追加的方式持久化数据,所以文件会越来越大,为了避免这种情况发生,增加了重写机制
Redis会记录上次重写时的AOF文件大小,默认配置时当AOF文件大小是上次rewrite后大小的一倍且文件大于64M时触发
总结
优点:
- append-only 文件是另一个可以提供完全数据保障的方案;数据丢失几率小。
缺点:
- AOF 文件会在操作过程中变得越来越大。比如,如果你做一百次加法计算,最后你只会在数据库里面得到最终的数值,但是在你的 AOF 里面会存在 100 次记录,其中 99 条记录对最终的结果是无用的。
四、主从复制
通过持久化功能,Redis 保证了即使在服务器重启的情况下也不会丢失(或少量丢失)数据,但是由于数据是存储在一台服务器上的,如果这台服务器出现故障,比如硬盘坏了,也会导致数据丢失。为了避免单点故障,我们需要将数据复制多份部署在多台不同的服务器上,即使有一台服务器出现故障其他服务器依然可以继续提供服务。这就要求当一台服务器上的数据更新后,自动将更新的数据同步到其他服务器上,那该怎么实现呢? 使用Redis 的主从复制。
Redis 提供了复制(replication)功能来自动实现多台 redis 服务器的数据同步(每天19 点 新闻联播,基本从 cctv1-8,各大卫视都会播放)
我们可以通过部署多台 redis,并在配置文件中指定这几台 redis 之间的主从关系,主负责写入数据,同时把写入的数据实时同步到从机器,这种模式叫做主从复制,即master/slave,并且 redis 默认 master 用于写,slave 用于读,向 slave 写数据会导致错误。
方式一
修改配置文件,启动时,服务器读取配置文件,并自动成为指定服务器的主从配置,从而构成主从复制的关系。
-
创建三个空的配置文件。
如果 Redis 启动,先停止。
作为 Master 的 Redis 端口是 6380,作为 Slaver 的 Redis 端口分别是 6382 , 6384。
创建三个配置分别命名为 redis6380.conf, redis6382.conf redis6384.conf
-
编辑Master 配置文件。
编辑 Master 的配置文件 redis6380.conf : 在空文件加入如下内容: include /usr/local/redis-3.2.9/redis.conf port 6380 pidfile /var/run/redis_6380.pid logfile 6380.log dbfilename dump6380.rdb include : 包含原来的配置文件内容。 /usr/local/redis-3.2.9/redis.conf 按照自己的目录设置。 port : 自定义的端口号 pidfile : 自定义的文件,表示当前程序的 pid ,进程 id。 logfile:日志文件名 dbfilename:持久化的 rdb 文件名
-
编辑 Slave 配置文件
编辑 Slave 的配置文件 redis6382.conf 和 redis6384.conf: 在空文件加入如下内容 ①:redis6382.conf: include /usr/local/redis-3.2.9/redis.conf port 6382 pidfile /var/run/redis_6382.pid logfile 6382.log dbfilename dump6382.rdb slaveof 127.0.0.1 6380 配置项说明: slaveof : 表示当前 Redis 是谁的从。当前是 127.0.0.0 端口 6380 这个 Master 的从。 ②:redis6384.conf: include /usr/local/redis-3.2.9/redis.conf port 6384 pidfile /var/run/redis_6384.pid logfile 6384.log dbfilename dump6384.rdb slaveof 127.0.0.1 6380
-
启动服务器 Master/Slave 都启动
启动方式 ./redis-server 配置文件 redis-server redis6380.conf redis-server redis6382.conf redis-server redis6384.conf 启动 Redis,并查看启动进程 ps -ef | grep redis
-
查看配置后的服务信息
①: Redis 客户端使用指定端口连接 Redis 服务器 ./redis-cli -p 端口 ②:查看服务器信息 info replication 进入客户端需指定端口:./redis-cli -p 6380 不配置启动默认都是主 master
-
向 Master 写入数据, 在从 Slave 读数据。
方式二
./redis-server --slaveof <master-ip> <master-port>,在启动 redis 时指定当前服务成为某个主 Redis 服务的从 Slave
Redis 主从数据同步(主从复制)的过程?
1、slave启动后,向master发送sync命令
2、master收到sync之后,执行bgsave保存快照,生成RDB全量文件
3、master把slave的写命令记录到缓存
4、bgsave执行完毕之后,发送RDB文件到slave,slave执行
5、master发送缓冲区的写命令给slave,slave接收命令并执行,完成复制初始化。
6、此后,master每次执行一个写命令都会同步发送给slave,保持master与slave之间数据的一致性
五、哨兵模式
概念
主从切换技术的方法是:当主服务器宕机后,需要手动把一台从服务器切换为主服务器,这就需要人工干预,费事费力,还会造成一段时间内服务不可用。
哨兵(sentinal)是Redis集群架构中非常重要的一个组件,哨兵的出现主要是解决了主从复制出现故障时需要人为干预的问题。
Redis哨兵主要功能
(1)集群监控:负责监控Redis master和slave进程是否正常工作
(2)消息通知:如果某个Redis实例有故障,那么哨兵负责发送消息作为报警通知给管理员
(3)故障转移:如果master node挂掉了,会自动转移到slave node上
(4)配置中心:如果故障转移发生了,通知client客户端新的master地址
原理
Redis哨兵的高可用其原理是哨兵通过发送命令,等待Redis服务器响应,从而监控运行的多个Redis实例。
- 哨兵机制建立了多个哨兵节点(进程),共同监控数据节点的运行状况。
- 同时哨兵节点之间也互相通信,交换对主从节点的监控状况。
- 每隔1秒每个哨兵会向整个集群:Master主服务器+Slave从服务器+其他Sentinel(哨兵)进程,发送一次ping命令做一次心跳检测。
哨兵用来判断节点是否正常的重要依据,涉及两个新的概念**:主观下线和客观下线。**
**主观下线:**一个哨兵节点判定主节点down掉是主观下线。
**客观下线:**只有半数哨兵节点都主观判定主节点down掉,此时多个哨兵节点交换主观判定结果,才会判定主节点客观下线。
**原理:**基本上哪个哨兵节点最先判断出这个主节点客观下线,就会在各个哨兵节点中发起投票机制Raft算法(选举算法),最终被投为领导者的哨兵节点完成主从自动化切换的过程。
六、安全设置
- 端口:修改 redis 的端口,这一点很重要,使用默认的端口很危险,redis.conf 中修改 port 6379将其修改为自己指定的端口(可随意),端口 1024 是保留给操作系统使用的。用户可以使用的范围是 1024-65535。
- 绑定IP:修改 redis.conf 文件,把# bind 127.0.0.1 前面的注释#号去掉,然后把 127.0.0.1 改成允许访问你 redis 服务器的 ip 地址,表示只允许该 ip 进行访问。多个 ip 使用空格分隔。
- 密码:开启访问密码设置,修改 redis.conf , 使用 vim 命令。 找到 requirepass 行去掉注释,requirepass 空格后就是密码。
访问有密码的 Redis 两种方式:
①:在连接到客户端后,使用命令 auth 密码 , 命令执行成功后,可以正常使用 Redis
②:在连接客户端时使用 -a 密码。例如 ./redis-cli -h ip -p port -a password
七、集群
集群原理
Redis集群是一个由多个节点组成的分布式服务器群,它具有复制、高可用和分片特性;Redis集群没有中心节点,并且带有复制和故障转移特性,这可以避免单个节点成为性能瓶颈,或者因为某个节点下线而导致整个集群下线。
(1)所有的redis节点彼此互联(PING-PONG机制),内部使用二进制协议优化传输速度和带宽.
(2)节点的fail是通过集群中超过半数的节点检测失效时才生效.
(3)客户端与redis节点直连,不需要中间proxy层.客户端不需要连接集群所有节点,连接集群中任何一个可用节点即可
(4)redis-cluster把所有的物理节点映射到[0-16383]slot上,cluster 负责维护node<->slot<->value
Redis集群中内置了 16384 个哈希槽,当需要在 Redis 集群中放置一个 key-value 时,redis 先对key 使用 crc16 算法算出一个结果,然后把结果对 16384 求余数,这样每个 key 都会对应一个编号在 0-16383 之间的哈希槽,redis 会根据节点数量大致均等的将哈希槽映射到不同的节点。
容错处理
Redis Cluster通过ping/pong消息实现故障发现:不需要sentinel。ping/pong不仅能传递节点与槽的对应消息,也能传递其他状态,比如:节点主从状态,节点故障等。
故障发现就是通过这种模式来实现,分为主观下线和客观下线。
主观下线
- 节点1定期发送ping消息给节点2
- 如果发送成功,代表节点2正常运行,节点2会响应PONG消息给节点1,节点1更新与节点2的最后通信时间
- 如果发送失败,则节点1与节点2之间的通信异常判断连接,在下一个定时任务周期时,仍然会与节点2发送ping消息
- 如果节点1发现与节点2最后通信时间超过node-timeout,则把节点2标识为pfail状态。
客观下线
- 某个节点接收到其他节点发送的ping消息,如果接收到的ping消息中包含了其他pfail节点,这个节点会将主观下线的消息内容添加到自身的故障列表中,故障列表中包含了当前节点接收到的每一个节点对其他节点的状态信息
- 当前节点把主观下线的消息内容添加到自身的故障列表之后,会尝试对故障节点进行客观下线操作。
- 当半数以上持有槽的主节点都标记某节点主观下线时,可以保证判断的公平性。
集群搭建
Redis集群中至少需要三个节点。要保证集群的高可用,每个节点有一个备份机。所以需要6台服务器。可以使用一台虚拟机运行6个redis实例(7000~7005)。
安装依赖
yum -y install ruby
yum -y install rubygems
gem install redis-3.2.2.gem
配置6个Redis
注意:每个Redis实例必须是"新的",不能有之前"持久化"的残留。
# 端口号
port 7000
# 后台启动
daemonize yes
# 开启集群
cluster-enabled yes
#集群节点配置文件
cluster-config-file nodes-7000.conf
# 集群连接超时时间
cluster-node-timeout 5000
# 进程pid的文件位置
pidfile redis-7000.pid
# rdb文件路径
dbfilename dump-7000.rdb
# :%s/from/to/g : 对所有行的内容进行替换。 7000->700x
创建集群操作文件
复制redis-3.2.9/src/redis-trib.rb文件到当前文件夹。
启动
redis-server redis7000.conf
redis-server redis7001.conf
redis-server redis7002.conf
redis-server redis7003.conf
redis-server redis7004.conf
redis-server redis7005.conf
./redis-trib.rb create --replicas 1 192.168.26.128:7000 192.168.26.128:7001 192.168.26.128:7002 192.168.26.128:7003 192.168.26.128:7004 192.168.26.128:7005
# --replicas 1 表示 自动为每一个master节点分配一个slave节点 上面有6个节点,程序会按照一定规则生成 3个master(主)3个slave(从)
#ps -ef | grep redis 检测启动结果
停止
redis-cli -p 7000 shutdown
redis-cli -p 7001 shutdown
redis-cli -p 7002 shutdown
redis-cli -p 7003 shutdown
redis-cli -p 7004 shutdown
redis-cli -p 7005 shutdown
rm -f ./*.rdb
rm -f ./*.aof
rm -f ./nodes-*.conf
测试
redis-cli -c -p 7000