十、Redis事务
10.1 事务概念与ACID特性
数据库层面事务
在数据库层面,事务是指一组操作,这些操作要么全都被成功执行,要么全都不执行。
数据库事务的四大特性
- A:Atomic,原子性,将所有SQL作为原子工作单元执行,要么全部执行,要么全部不执行;
- C:Consistent,一致性,事务完成后,所有数据的状态都是一致的,即A账户只要减去了100,B账户则必定加上了100;
- I:Isolation,隔离性,如果有多个事务并发执行,每个事务作出的修改必须与其他事务隔离;
- D:Duration,持久性,即事务完成后,对数据库数据的修改被持久化存储。
Redis事务
Redis事务是一组命令的集合,一个事务中的所有命令都将被序列化,按照一次性、顺序性、排他性的执行一系列的命令。
Redis事务三大特性
- 单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断;
- 没有隔离级别的概念:队列中的命令没有提交之前都不会实际的被执行,因为事务提交前任何指令都不会被实际执行,也就不存在”事务内的查询要看到事务里的更新,在事务外查询不能看到”。
- 不保证原子性:redis同一个事务中如果有一条命令执行失败,其后的命令仍然会被执行,没有回滚;
Redis事务执行的三个阶段
- 开启:以
MULTI
开始一个事务; - 入队:将多个命令入队到事务中,接到这些命令并不会立即执行,而是放到等待执行的事务队列里面;
- 执行:由
EXEC
命令触发事务;
10.2 事务的基本操作
Multi、Exec、discard
事务从输入Multi命令开始,输入的命令都会依次压入命令缓冲队列中,并不会执行,直到输入Exec后,Redis会将之前的命令缓冲队列中的命令依次执行。组队过程中,可以通过discard来放弃组队。
- 正常执行
127.0.0.1:6379> MULTI #开启事务 OK 127.0.0.1:6379(TX)> set id 2 QUEUED 127.0.0.1:6379(TX)> get id QUEUED 127.0.0.1:6379(TX)> incr id QUEUED 127.0.0.1:6379(TX)> incr id QUEUED 127.0.0.1:6379(TX)> exec #提交事务 1) OK 2) "2" 3) (integer) 3 4) (integer) 4
- 放弃事务
127.0.0.1:6379> MULTI #开启事务 OK 127.0.0.1:6379(TX)> get id QUEUED 127.0.0.1:6379(TX)> incr id QUEUED 127.0.0.1:6379(TX)> DISCARD #放弃事务 OK 127.0.0.1:6379> get id "4" 127.0.0.1:6379>
- 全体连坐
127.0.0.1:6379> MULTI OK 127.0.0.1:6379(TX)> set name zj QUEUED 127.0.0.1:6379(TX)> get name QUEUED 127.0.0.1:6379(TX)> incr t1 QUEUED 127.0.0.1:6379(TX)> get t1 QUEUED 127.0.0.1:6379(TX)> set email (error) ERR wrong number of arguments for 'set' command 127.0.0.1:6379(TX)> exec (error) EXECABORT Transaction discarded because of previous errors.
注意:
命令集合中含有错误的指令(注意是语法错误),均连坐,全部失败。一人犯罪全家遭殃。
- 冤有头,债有主
127.0.0.1:6379> MULTI OK 127.0.0.1:6379(TX)> set age 11 QUEUED 127.0.0.1:6379(TX)> incr t1 QUEUED 127.0.0.1:6379(TX)> set email abc@163.com QUEUED 127.0.0.1:6379(TX)> incr email #语法正确,但是执行的时候会错误,该命令不会被执行。 QUEUED 127.0.0.1:6379(TX)> get age QUEUED 127.0.0.1:6379(TX)> exec 1) OK 2) (integer) 5 3) OK 4) (error) ERR value is not an integer or out of range 5) "11"
注意:
运行时错误,即非语法错误,正确命令都会执行,错误命令返回错误。
十一、Redis集群
11.1 主从复制概念
概述
在现有企业中80%公司大部分使用的是redis单机服务,在实际的场景当中单一节点的redis容易面临风险。
面临问题:
- 机器故障。我们部署到一台 Redis 服务器,当发生机器故障时,需要迁移到另外一台服务器并且要保证数据是同步的。
- 容量瓶颈。redis的数据保存在内存中,当我们有需求需要扩容 Redis 内存时,从 16G 的内存升到 64G,单机肯定是满足不了。当然,你可以重新买个 128G 内存的新机器。
解决办法
要实现分布式数据库的更大的存储容量和承受高并发访问量,我们会将原来集中式数据库的数据分别存储到其他多个网络节点上。
注意:
Redis 为了解决这个单一节点的问题,也会把数据复制多个副本部署到其他节点上进行复制,实现 Redis的高可用,实现对数据的冗余备份从而保证数据和服务的高可用。
什么是主从复制
主从复制,是指将一台Redis服务器的数据,复制到其他的Redis服务器。前者称为主节点(master),后者称为从节点(slave),数据的复制是单向的,只能由主节点到从节点。
主从复制的作用
- 数据冗余:主从复制实现了数据的热备份,是持久化之外的一种数据冗余方式。
- 故障恢复:当主节点出现问题时,可以由从节点提供服务,实现快速的故障恢复;实际上是一种服务的冗余。
- 负载均衡:在主从复制的基础上,配合读写分离,可以由主节点提供写服务,由从节点提供读服务(即写Redis数据时应用连接主节点,读Redis数据时应用连接从节点),分担服务器负载;尤其是在写少读多的场景下,通过多个从节点分担读负载,可以大大提高Redis服务器的并发量。
- 高可用基石:除了上述作用以外,主从复制还是哨兵和集群能够实施的基础,因此说主从复制是Redis高可用的基础。
11.2 主从复制搭建
通过不同的端口,来模拟不同的redis服务。但是在实际的开发中肯定不是这样搭建的主从集群。企业中一台服务器搭建一个redis
新建redis6379.conf配置文件
在新建配置文件之前先将redis服务停掉。
[root@localhost ~]# lsof -i:6379 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME redis-ser 1855 root 6u IPv4 33744 0t0 TCP *:6379 (LISTEN) redis-ser 1855 root 7u IPv6 33745 0t0 TCP localhost:6379 (LISTEN) [root@localhost ~]# kill 1855
在redis根目录下编写配置文件
include /usr/local/redis-6.2.6/redis.conf pidfile /var/run/redis_6379.pid port 6379 dbfilename dump6379.rdb
新建redis6380.conf配置文件
include /usr/local/redis-6.2.6/redis.conf pidfile /var/run/redis_6381.pid port 6381 dbfilename dump6381.rdb
新建redis6381.conf配置文件
include /usr/local/redis-6.2.6/redis.conf pidfile /var/run/redis_6380.pid port 6380 dbfilename dump6380.rdb
启动三台redis服务器
./redis-server ../redis6379.conf ./redis-server ../redis6380.conf ./redis-server ../redis6381.conf
查看系统进程
打开三个窗口连接不同端口下的redis服务
./redis-cli -p 6379 ./redis-cli -p 6380 ./redis-cli -p 6381
查看服务详情
127.0.0.1:6379> info replication 127.0.0.1:6380> info replication 127.0.0.1:6381> info replication
在从库中指定主库
slaveof <ip> <port>
127.0.0.1:6380> slaveof 127.0.0.1 6379 127.0.0.1:6381> slaveof 127.0.01 6379
主机写数据
127.0.0.1:6379> set test1 testvalue OK
从机读数据
127.0.0.1:6380> get test1 "testvalue" 127.0.0.1:6381> get test1 "testvalue"
注意,从节点只能读数据不能写数据。
11.3 主从复制原理刨析
主从复制可以分为3个阶段
- 连接建立阶段(即准备阶段)
- 数据同步阶段
- 命令传播阶段
复制过程大致分为6个过程
1、保存主节点(master)信息。
执行 slaveof 后 查看状态信息
info replication # Replication role:slave master_host:127.0.0.1 master_port:6379 master_link_status:up
2、从节点(slave)内部通过每秒运行的定时任务维护复制相关逻辑,当定时任务发现存在新的主节点后,会尝试与该主节点建立网络连接
3、从节点与主节点建立网络连接
从节点会建立一个 socket 套接字,从节点建立了一个端口为51234的套接字,专门用于接受主节点发送的复制命令。
4、从节点发送ping命令到主节点
作用:
- 检测主从之间网络套接字是否可用。
- 检测主节点当前是否可以接受命令 。
5、权限认证
如果主节点设置了 requirepass 参数,则需要密码验证,从节点必须配置 masterauth 参数保证与主节点相同的密码才能通过验证;如果验证失败复制将终止,从节点重新发起复制流程
6、同步数据集
主从复制连接正常通信后,对于首次建立复制的场景,主节点会把持有的数据全部发送给从节点,这部分操作是耗时最长的步骤。
主从同步策略
主从刚刚连接的时候,进行全量同步;全同步结束后,进行增量同步。当然,如果有需要,slave 在任何时候都可以发起全量同步。redis 策略是,无论如何,首先会尝试进行增量同步,如不成功,要求从机进行全量同步。
例如:
保存一个缓存
set name zj
记录命令为
$3 \r \n set \r \n $4 \r \n name \r \n $5 \r \n baizhan \r \n
偏移量 | 1000 | 1001 | 1002 | 1003 | 1004 | 1005 | 1006 | 1007 | 1008 |
字节值 | $ | 3 | \r | \n | $ | 4 | n | a | m |
7、命令持续复制。
当主节点把当前的数据同步给从节点后,便完成了复制的建立流程。接下来主节点会持续地把写命令发送给从节点,保证主从数据一致性。
11.4 哨兵监控概述
Redis主从复制缺点
当主机 Master 宕机以后,我们需要人工解决切换。
暴漏问题:
一旦主节点宕机,写服务无法使用,就需要手动去切换,重新选取主节点,手动设置主从关系。
主从切换技术
当主服务器宕机后,需要手动把一台从服务器切换为主服务器,这就需要人工干预,费事费力,还会造成一段时间内服务不可用。这不是一种推荐的方式,更多时候,我们优先考虑哨兵模式。
哨兵模式概述
哨兵模式是一种特殊的模式,首先Redis提供了哨兵的命令,哨兵是一个独立的进程,作为进程,它会独立运行。其原理是哨兵通过发送命令,等待Redis服务器响应,从而监控运行的多个Redis实例。
哨兵作用
- 集群监控:负责监控redis master和slave进程是否正常工作
- 消息通知:如果某个redis实例有故障,那么哨兵负责发送消息作为报警通知给管理员
- 故障转移:如果master node挂掉了,会自动转移到slave node上
- 配置中心:如果故障转移发生了,通知client客户端新的master地址
11.5 配置哨兵监控
新建sentinel-26379.conf配置文件
#端口 port 26380 #守护进程后台运行 daemonize yes #日志文件 logfile "26380.log" #主节点 sentinel monitor mymaster 192.168.66.100 6379 2
参数:
sentinel monitor mymaster 192.168.92.128 6379 2 配置的含义是:该哨兵节点监控192.168.92.128:6379这个主节点,该主节点的名称是mymaster,最后的2的含义与主节点的故障判定有关:至少需要2个哨兵节点同意,才能判定主节点故障并进行故障转移。
新建sentinel-26380.conf文件
#端口 port 26380 #守护进程运行 daemonize yes #日志文件 logfile "26380.log" sentinel monitor mymaster 192.168.66.100 6379 2
新建sentinel-26381.conf文件
#端口 port 26381 #守护进程运行 daemonize yes #日志文件 logfile "26381.log" sentinel monitor mymaster 192.168.66.100 6379 2
实际开发中一个redis中搭配一个哨兵。
哨兵节点的启动
[root@localhost src]# ./redis-sentinel ../ sentinel-26379.conf [root@localhost src]# ./redis-sentinel ../ sentinel-26380.conf [root@localhost src]# ./redis-sentinel ../ sentinel-26381.conf
查看哨兵节点状态
[root@localhost src]# ./redis-cli -p 26379 127.0.0.1:26379> 127.0.0.1:26379> 127.0.0.1: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.66.100:6379,slaves=2,sentinels=3
11.6 哨兵监控原理刨析
监控阶段
过程:
- sentinel(哨兵1)----->向master(主)和slave(从)发起info,拿到全信息。
- sentinel(哨兵2)----->向master(主)发起info,就知道已经存在的sentinel(哨兵1)的信息,并且连接slave(从)。
- sentinel(哨兵2)----->向sentinel(哨兵1)发起subscribe(订阅)。
通知阶段
sentinel不断的向master和slave发起通知,收集信息。
故障转移阶段
通知阶段sentinel发送的通知没得到master的回应,就会把master标记为SRI_S_DOWN,并且把master的状态发给各个sentinel,其他sentinel听到master挂了,说我不信,我也去看看,并把结果共享给各个sentinel,当有一半的sentinel都认为master挂了的时候,就会把master标记为SRI_0_DOWN。
问题来了:
这时就要把master给换掉了,到底谁当Master呢。
投票方式
方式:
自己最先接到哪个sentinel的竞选通知就会把票投给它。
剔除一些情况:
- 不在线的
- 响应慢的
- 与原来master断开时间久的
- 优先级原则