这里是参考B站上的大佬做的面试题笔记。大家也可以去看视频讲解!!!
文章目录
1、简述Redis事务实现
1、事务开始
MULTI命令的执行,标识着一个事务的开始。MULTI命令会将客户端状态的flag属性中打开REDIS_MULTI标识来完成的。
2、命令入队
当一个客户端切换到事务状态后,服务器会根据这个客户端发送来的命令来执行不同的操作。如果客户端发送的命令为MULTI、EXEC、WATCH、DISCARD中的一个,立即执行这个命令,否则将命令放入一个事务队列里面,然后向客户端返回QUEUED回复。
- 如果客户端发送的命令为EXEC、DISCARD、WATCH、MULTI四个命令的其中一个,那么服务器立即执行这个命令。
- 如果客户端发送的是四个命令以外的其他命令,那么服务器并不立即执行这个命令。
- 首先检查命令的格式是否正确、如果不正确,服务器会在客户端状态(redisClient)的flags属性关闭REDIS_MULTI标识,并且返回错误信息给客户端。如果正确,将这个命令放入一个事务队列里面,然后向客户端返回QUEUED回复
事务队列是按照FIFO的方式保存入队的命令。
3、事务执行
客户端发送EXEC命令,服务器执行EXEC命令逻辑
- 如果客户端状态的flags属性不包含REDIS_MULTI标识,或者包含REDIS_DIRTY_CAS或者REDIS_DIRTY_EXEC标识,那么就直接取消事务的执行。
- 否则客户端处于事务状态(flags有REDIS_MULTI标识),服务器会遍历客户端的事务队列,然后执行事务队列中的所有的命令,最后将返回结果全部返回给客户端;
redis不支持事务回滚机制,但是它会检查每一个事务中的命令是否错误。
Redis事务不支持检查哪些程序员自己的逻辑错误。例如对String类型的数据库执行对HashMap类型的操作!!!
- WATCH命令是一个乐观锁,可以为Redis事务提供check-and-set(CAS)行为,可以监控一个或多个键,一旦其中有一个键被修改(或删除),之后的事务就不会执行,监控一直持续到EXEC命令。
- MULTI命令有用于开启一个事务,它总是返回OK,MULTI执行之后,客户端可以继续向服务器发送任意多条命令,这些命令不会立即被执行,而是被放到一个队列中,当EXEC命令被调用时,所有队列中的命令才会被执行。
- EXEC:执行所有事务块内的命令。返回事务块内所有命令的返回值,按命令执行的先后顺序排列。当操作被打断时,返回空值NULL。
- 通过调用DISCARD,客户端可以清空事务队列,并放弃执行事务,并且客户端会从事务状态中退出。
- UNWATCH命令可以取消watch对所有key的监控。
2、redis集群方案
主从
哨兵模式:
sentinel,哨兵是redis集群中非常重要的一个组件,主要有一下功能:
- 集群监控:负责监控redis master 和slave进程是否正常工作。
- 信息通知:如果某个redis实例有故障,那么哨兵负责发送信息作为报警通知给管理员
- 故障转移:如果master node挂掉了,会自动转移到slave node上。
- 配置中心:如果故障转移发生了,通知client客户端新的master地址
哨兵用于实现redis集群的高可用,本身也是分布式的,作为一个哨兵集群去运行,互相协同工作。
- 故障转移时,判断一个master node是否宕机了,需要大部分的哨兵都同意才行,涉及到了分布式选举
- 即使部分哨兵节点挂掉了,哨兵集群还是能正常工作的
- 哨兵通常需要3个实例,来保证自己的健壮性。
- 哨兵+redis主从的部署架构,是不保证数据零丢失的,只能保证redis集群的高可用性。
- 对于哨兵+redis主从这种复杂的部署架构,尽量在测试环境和生产环境,都进行充足的测试和演练。
redis Cluster 是一种服务端Sharding技术,3.0版本开始正式提供。采用==slot(槽)==的概念,一共分成16384个槽。将请求发送到任意节点,接收到请求的节点会将查询请求发送到正确的节点上执行
方案说明:
- 通过哈希的方式,将数据分片,每个节点均分存储一定哈希槽(哈希值)区间的数据,默认分配 了16384个槽位
- 每份数据分片会存储在多个互为主从的节点上
- 数据写入先写主节点,再同步到从节点(支持配置为阻塞同步)
- 同一分片多个节点间的数据不保持一致性
- 读取数据时,当客户端操作的key没有分配在该节点上时,redis会返回转向指令,指向正确的节点
- 扩容时需要把旧节点的数据迁移一部分到新节点
在redis cluster架构下,每个redis要开放两个端口号,比如一个是6379,另外一个就是加1w的端口号,比如16379.
16379端口号是用来进行节点间通信的,也就是cluster bus的通信,用来进行故障检测、配置更新、故障转移授权。cluster bus用了另外一种二进制的协议,gossip协议,用于节点间进行高效的数据交换,占用更少的网络带宽和处理时间。
优点:
- 无中心架构,支持动态扩容,对业务透明
- 具备Sentine的监控和自动Failover(故障转移)能力
- 客户端不需要连接集群所有节点,连接集群中任何一个可用节点即可
- 高性能,客户端直连redis服务,免去了proxy代理的损耗
缺点:
- 运维也很复杂、数据迁移需要人工干预
- 只能使用0号数据库
- 不支持批量操作(pipeline管道操作)
- 分布式逻辑和存储模块耦合等
Redis Sharding是Redis Cluster出来之前,业界普遍使用的多Redis实例集群方法。其主要思想是采用哈希算法将Redis数据的Key进行散列,通过hash函数,特定的key会映射到特定的Redis节点上。java redis客户端驱动jedis,支持Redis Sharing功能,即ShardedJedis以及结合缓存池的ShardegjedisPool
优点:
优势在于非常简单,服务端的redis实例彼此独立,相互无关联,每个Redis实例像单服务器一样运行,非常容易线性扩展,系统的灵活性很强。
缺点:
由于sharding处理放到客户端,规模进一步扩大时给运维带来挑战。
客户端sharding不支持动态增删改节点。服务端redis实例群拓扑结构有变化时,每个客户端都需要更新调整。连接不能共享,当应用规模增大时,资源浪费制约优化
3、redis主从复制的核心原理
通过执行slaveof命令或设置slaveof选项,让一个服务器去复制另一个服务器的数据。主数据库可以进行读写操作,当写操作导致数据变化时会自动将数据同步给从数据库。而从数据库一般只是只读的,并接受主数据库同步过来的数据。一个主数据库可以拥有多个从数据库,而一个从数据库只能拥有一个主数据库。
全量复制:
- 1、主节点通过bgsave命令fork子进程进行RDB持久化,该过程是非常消耗CPU、内存(页表复制)、硬盘IO的
- 2、主节点通过网络将RDB文件发送给从节点,对主节点的带宽都会带来很大的消耗
- 3、从节点清空老数据,载入新RDB文件的过程是阻塞的,无法响应客户端的命令;如果从节点执行
bgrewriteaof
,也会带来额外的消耗
部分复制:
- 1、复制偏移量:执行复制的双方,主从节点,分别会维护一个复制偏移量offset
- 2、复制积压缓冲区:主节点内部维护了一个固定长度的、先进先出(FIFO)队列作为复制积压缓冲区,当主从节点offset的差距过大超过缓冲区长度时,将无法执行部分复制,只能执行全量复制。
- 3、服务器运行ID(runid):每个Redis节点,都有其运行ID,运行ID由节点在启动时自动生成,主节点会将自己的运行ID发送给从节点,从节点会将主节点的运行ID存起来。从节点Redis断开重连的时候,就是根据运行ID来判断同步的进度:
如果从节点保存的runid与主节点现在的runid相同,说明主从节点之前同步过,主节点会继续尝试使用部分复制(到底能不能部分复制还要看offset和复制积压缓冲区的情况)
如果从节点保存的runid与主节点现在的runid不同,说明从节点在断线前同步的Redis节点并不是当前的主节点,只能进行全量复制。
4、CAP理论,BASE理论
CAP理论
Consistency(一致性):
- 即更新操作成功并返回客户端后,所有节点在同一时间的数据完全一致。
- 对于客户端来说,一致性指的是并发访问时更新过的数据如何获取的问题。
- 从服务端来看,则是更新如何复制分布到整个系统,以保证数据最终一致。
Availability(可用性):
- 即服务一直可用,而且是正常响应时间。系统能够很好的为用户服务,不出现用户操作失败或者访问超时等用户体验不好的情况
Partition Tolerance(分区容错性):
- 即分布式系统在遇到某节点或网络分区故障的时候,仍然能够对外提供满足一致性和可用性的服务。分区容错性要求能够使应用虽然是一个分布式系统,而看上去却好像是在一个可以运转正常的整体。比如现在的分布式系统中由某一个或几个机器宕机了,其他剩下的机器还能够正常运转满足系统需求,对于用户而言并没有什么体验上的影响。
CP和AP:分区容错是必须保证的,当发生网络分区的时候,如果要继续服务,那么强一致性和可用性只能2选1.
BASE是Basically Available(基本可用)、soft state(软状态)和Eventually consisten(最终一致性)
BASE理论是对CPA中一致性和可用性权衡的结果,其来源于对大规模互联网系统分布式实践的总结,是基于CAP定理逐步演化而来的。BASE理论的核心思想是:即使无法做到强一致性,但每个应用都可以根据自身业务特定,采用适当的方式来使系统达到最终一致性
基本可用:
- 响应时间上的损失:正常情况下,处理用户请求需要0.5s返回结果,但是由于系统出现故障,处理用户请求的时间变为3s
- 系统功能上的损失:正常情况下,用户可以使用系统的全部功能,但是由于系统访问量突然剧增,系统的部分非核心功能无法使用。
软状态:数据同步允许一定的延迟
最终一致性:系统中所有的数据副本,在经过一段时间的同步后,最终能够达到一个一致的状态,不要求实时
5、负载均衡算法、类型
1、轮循法
- 将请求按顺序轮流分配到后端服务器上,它均衡地对待后端的每一台服务器,而不关心服务器实际的连接数和当前的系统负载
2、随机法
- 通过系统的随机算法、根据后端服务器的列表大小值来随机选取其中的一台服务器访问。由概率统计理论可以得知,随着客户端调用服务端的次数增多,其实际效果越来越接近于平均分配调用量到后端的每一台服务器,也就是轮循的结果
3、源地址哈希法
- 源地址哈希的思想是根据获取客户端的IP地址,通过哈希函数计算得到的一个数值,用该数值对服务器列表的大小进行取模运算,得到的结果便是客户端要访问服务器的序号。采用源地址哈希法进行负载均衡,同一IP地址的客户端,当 后端服务器列表不变时,它每次都会映射到同一台后端服务器进行访问。
4、加权轮询法
- 不同的后端服务器可能机器的配置和当前系统的负载并不相同,因此它们的抗压能力也不相同。给配置高、负载低的机器配置更高的权重,让其处理更多的请求;而配置低、负载高的机器,给其分配较低的权重,降低其系统负载,加权轮循能很好地处理这一问题,并将请求顺序且按照权重分配到后端。
5、加权随机法
- 与加权轮询法一样,加权随机法也可根据后端机器的配置,系统的负载分配不同的权重。不同的是,它是按照权重随机请求后端服务器,而非顺序
6、随机连接数法
- 最小连接数算法比较灵活和智能,由于后端服务器的配置不尽相同,对于请求的处理有快有慢,它是根据后端服务器当前的连接情况,动态地选取其中当前积压连接数最少的一台服务器来处理当前的请求,尽可能地提高后端服务的利用效率,将负责合理地分流到每一台服务器。
类型:
DNS方式实现负载均衡
硬件负载均衡:F5和A10
软件负载均衡:
Nginx、HAproxy、Lvs。其中的区别:
- Nginx:七层负载均衡,支持HTTP、E-mail协议,同时也支持4层负载均衡;
- HAproxy:支持七层规则的,性能也很不粗。OpenStack默认使用的负载均衡软件就是HPproxy
- LVS:运行在内核态,性能是软件负载均衡中最高的,严格来说工作在三次(传输层),所以更通用一些,适用各种应用服务。