主从复制、集群和应用问题【Redis篇4】

本文涉及的产品
云数据库 Redis 版,社区版 2GB
推荐场景:
搭建游戏排行榜
简介: 主从复制、集群和应用问题

1、主从复制

1.1、简介

主从复制是什么?

  • 主机数据更新后根据配置和策略, 自动同步到备机的master/slaver机制,Master以写为主,Slave以读为主。

用途

在这里插入图片描述

  • 读写分离,性能扩展
  • 容灾快速恢复

1.2、搭建一主二从

  1. 创建/myredis文件夹
  2. 复制redis.conf配置文件到文件夹中
  3. 配置一主两从,创建三个配置文件

    • redis6379.conf
    • redis6380.conf
    • redis6381.conf
  4. 在三个配置文件写入内容
include /myredis/redis.conf
pidfile /var/run/redis6379.conf
port 6379
dbfilename dump6379.rdb
  1. 启动三个redis服务

    1. 查看当前主机运行状况
    2. 在主机上执行slaveof 主机ip 端口号

# 1. 创建/myredis文件夹
 mkdir /myredis


# 2. 复制redis.conf配置文件到文件夹中

 cp /etc/redis.conf /myredis/redis.conf


# 3. 配置一主两从,创建三个配置文件
# 配置内容
include /myredis/redis.conf
pidfile /var/run/redis6379.conf
port 6379
dbfilename dump6379.rdb


# 5. 启动三个redis服务
redis-server /myredis/redis6379.conf
redis-server /myredis/redis6380.conf
redis-server /myredis/redis6381.conf


# 查看主机运行情况
info replication


# 建立主从关系
slaveof 127.0.0.1 6379
  • 搭建好的一主二从,主机如果进行了写操作,那么从机可以读取到相应的内容。但是从机不能够进行写操作

1.3、一主二仆

相关特点

  1. 如果从机宕机之后,从机再次进行启动的话,那么该从机是以master的身份进行启动。我们需要手动的进行恢复其从机的身份。恢复之后,如果主机在从机宕机的过程中改变了数据库的内容,从机会从头开始进行数据恢复
  2. 如果主机宕机的话,从机还是保持从机的身份。如果主机重新启动的话,那么它还是以原来的主机身份启动

1.4、复制原理

在这里插入图片描述

1. 从机连接主机时主动发起同步

  1. 当从机连接上主服务器之后,从服务器向主服务器进行数据同步消息。
  2. 主服务器连接到从服务器发送过来的同步消息,把主服务器数据进行持久化到rdb文件,把rdb文件发送给从服务器,从服务器拿到rdb文件进行读取。

2. 主机发起同步

  1. 每次主服务器进行写操作之后,和从服务器进行数据同步。

3. 相关的复制类型

  • 全量复制:而slave服务在接收到数据库文件数据后,将其存盘并加载到内存中。
  • 增量复制:Master继续将新的所有收集到的修改命令依次传给slave,完成同步

1.5、薪火相传

在这里插入图片描述

  • 如果一个主机对应好多个从机的话,那么主机进行管理会显得十分吃力。那么有一个方法就是主机进行管理一两个从机,然后其它的从机作为该一两个主机的从机进行被管理。如果主机对数据库内容进行改变,其从机以及从机的从机都可以读到相应的数据。那么,这就是薪火相传。
  • 如果主机宕机的话,那么其它从机还是以从机的身份进行运行,主机进行重启之后,还是以主机的身份进行运行。就是无法写数据了。
  • 中途变更转向:会清除之前的数据,重新建立拷贝最新的。风险是一旦某个slave宕机,后面的slave都没法备份

1.6、反客为主

  • 如果主机宕机的话,我们可以slaveof no one使得从机进行反客为主,其后面的slave不要修改。但这必须是手动完成的。

1.7、哨兵模式

1.7.1、哨兵模式是什么?

  • 反客为主的自动版,能够后台监控主机是否故障,如果故障了根据投票数自动将从库转换为主库

在这里插入图片描述

1.7.2、配置哨兵模式

  1. 调整为一主二仆模式
  2. 自定义的/myredis目录下新建sentinel.conf文件,名字绝不能错。
  3. 配置哨兵,填写内容
# 其中mymaster为监控对象起的服务器名称, 1 为当至少有多少个哨兵同意迁移才发生迁移时哨兵的数量。
sentinel monitor mymaster 127.0.0.1 6379 1
 
  1. 启动哨兵
# 启动哨兵
redis-sentinel  /myredis/sentinel.conf 

# 默认启动端口号为26379
  1. 当主机挂掉,从机选举中产生新的主机。
  2. 复制延时:由于所有的写操作都是先在Master上操作,然后同步更新到Slave上,所以从Master同步到Slave机器有一定的延迟,当系统很繁忙的时候,延迟问题会更加严重,Slave机器数量的增加也会使这个问题更加严重。

1.7.3、故障恢复

  • 当主机宕机后,新的主机的选择条件为
  1. 选择优先级靠前的。优先级在redis.conf中默认:replica-priority 100,值越小优先级越高。
  2. 选择偏移量最大的。偏移量是指获得原主机数据最全的。
  3. 选择runid最小的从服务.每个redis实例启动后都会随机生成一个40位的runid。
  • 当新的主服务器挑选出来了之后,sentinel会向原主服务的从服务发送slaveof新主服务的命令,复制新master
  • 当已下线的服务重新上线时,sentinel会向其发送slaveof命令,让其成为新主的从机。

1.8、Java开发中主从复制配置

主要就是连接池的配置有变

private static JedisSentinelPool jedisSentinelPool=null;

public static  Jedis getJedisFromSentinel(){
    if(jedisSentinelPool==null){
        Set<String> sentinelSet=new HashSet<>();
        sentinelSet.add("192.168.11.103:26379");

        JedisPoolConfig jedisPoolConfig =new JedisPoolConfig();
        jedisPoolConfig.setMaxTotal(10); //最大可用连接数
        jedisPoolConfig.setMaxIdle(5); //最大闲置连接数
        jedisPoolConfig.setMinIdle(5); //最小闲置连接数
        jedisPoolConfig.setBlockWhenExhausted(true); //连接耗尽是否等待
        jedisPoolConfig.setMaxWaitMillis(2000); //等待时间
        jedisPoolConfig.setTestOnBorrow(true); //取连接的时候进行一下测试 ping pong
        
        jedisSentinelPool=new JedisSentinelPool("mymaster",sentinelSet,jedisPoolConfig);
        return jedisSentinelPool.getResource();
   }else{
        return jedisSentinelPool.getResource();
   }
}

2、集群

2.1、面临的问题

  • 容量不够,redis如何进行扩容?
  • 并发写操作, redis如何分摊?
  • 解决方案无中心化集群配置

2.2、什么是集群?

  • Redis 集群实现了对Redis的水平扩容,即启动N个redis节点,将整个数据库分布存储在这N个节点中,每个节点存储总数据的1/N
  • Redis 集群通过分区(partition)来提供一定程度的可用性(availability): 即使集群中有一部分节点失效或者无法进行通讯, 集群也可以继续处理命令请求。

2.3、集群的搭建步骤

1. redis6379.conf的配置信息

include /home/bigdata/redis.conf
port 6379
pidfile "/var/run/redis_6379.pid"
dbfilename "dump6379.rdb"
cluster-enabled yes    #打开集群模式
cluster-config-file nodes-6379.conf  #设定节点配置文件名
cluster-node-timeout 15000   #设定节点失联时间,超过该时间(毫秒),集群自动进行主从切换。

2. 对上面的配置文件进行拷贝多份

  • redis6380.conf
  • redis6381.conf
  • redis6389.conf
  • redis6390.conf
  • redis6391.conf

3. 使用查找替换修改另外5个文件

# 例如
%s/6379/6380

4. 分别启动6个redis服务

5. 将六个节点合成一个集群

  • 组合之前,请确保所有redis实例启动后,nodes-xxxx.conf文件都生成正常。
  • 合体之前需要切换到/opt/redis-6.2.1/src,然后执行以下指令
redis-cli --cluster create --cluster-replicas 1 192.168.123.129:6379 192.168.123.129:6380 192.168.123.129:6381 
192.168.123.129:6389 192.168.123.129:6390 192.168.123.129:6391

特别说明

  • 此处不要用127.0.0.1, 请用真实IP地址
  • --replicas 1 采用最简单的方式配置集群,一台主机,一台从机,正好三组。

6. 连接方式

  1. 普通方式连接
redis-cli -p 6379
  1. -c 采用集群策略连接,设置数据会自动切换到相应的写主机。
redis-cli -c -p 6379

7. 查看集群信息

查看命令

cluster nodes

查看结果

6019df2006afe1f05319c77560ff0c5da57003b4 192.168.123.129:6381@16381 master - 0 1639140826000 3 connected 10923-16383
185b8d4e5cffdda4589e53b4ef25cc6dabfd2eee 192.168.123.129:6389@16389 slave bf3d815ea4343d1edbe2531309e89ffb336aa4d3 0 1639140826372 1 connected
bf3d815ea4343d1edbe2531309e89ffb336aa4d3 192.168.123.129:6379@16379 myself,master - 0 1639140820000 1 connected 0-5460
3ac702d1d41273475444e83ab82cdbb3033cbd84 192.168.123.129:6391@16391 slave 6019df2006afe1f05319c77560ff0c5da57003b4 0 1639140825364 3 connected
819200fa1a92f6199fddca92c034bad1f4bc4be2 192.168.123.129:6390@16390 slave 2b68d1fec5abe9832c371dca7a16444f485ab4c9 0 1639140824357 2 connected
2b68d1fec5abe9832c371dca7a16444f485ab4c9 192.168.123.129:6380@16380 master - 0 1639140824000 2 connected 5461-10922

2.4、集群操作

1. 什么是slots?

  • 一个 Redis 集群包含 16384 个插槽(hash slot), 数据库中的每个键都属于这 16384 个插槽的其中一个
  • 集群使用公式 CRC16(key) % 16384 来计算键 key 属于哪个槽, 其中 CRC16(key) 语句用于计算键 key 的 CRC16 校验和 。
  • 集群中的每个节点负责处理一部分插槽。 举个例子, 如果一个集群可以有主节点, 其中:

    • 节点 A 负责处理 0 号至 5460 号插槽。
    • 节点 B 负责处理 5461 号至 10922 号插槽。
    • 节点 C 负责处理 10923 号至 16383 号插槽。

2. 在集群中录入值

# 向其中添加单值的时候,其会计算其插槽值,然后切换至相应的服务器进行添加
127.0.0.1:6379> set k1 v1
-> Redirected to slot [12706] located at 192.168.123.129:6381
OK


# 向其中添加多值的时候,其会根据其组名计算其插槽值,然后切换至相应的服务器进行添加
192.168.123.129:6381> mset name{user} lucy age{user} 20
-> Redirected to slot [5474] located at 192.168.123.129:6380
OK

3. 查询集群中的值

# 计算key对应插槽所对应的值
192.168.123.129:6380> cluster keyslot cust
(integer) 4847


# 返回对应插槽的键的个数
127.0.0.1:6379> cluster countkeysinslot 449
(integer) 1


# 返回 count 个 slot 槽中的键
cluster getkeysinslot <slot>  <count>

2.5、故障恢复

1. 只有主机宕机

  • 如果主机宕机的话,那么其从机就会接力而上作为主机;原本的主机再次启动的时候就会作为接力而上的主机的从机。

2. 主从一起宕机

  • 如果某一段插槽的主从都挂掉,而cluster-require-full-coverage 为yes ,那么 ,整个集群都挂掉
  • 如果某一段插槽的主从都挂掉,而cluster-require-full-coverage 为no ,那么,该插槽数据全都不能使用,也无法存储
  • redis.conf中的参数 cluster-require-full-coverage

2.6、java中redis集群开发

  • 即使连接的不是主机,集群会自动切换主机存储。主机写,从机读
  • 无中心化主从集群。无论从哪台主机写的数据,其他主机上都能读到数据。
public class JedisClusterTest {
  public static void main(String[] args) { 
     HostAndPort hostAndPort =new HostAndPort("192.168.123.129",6379);
     JedisCluster jedisCluster=new JedisCluster(hostAndPort);
     jedisCluster.set("k1", "v1");
     System.out.println(jedisCluster.get("k1"));
  }
}

2.7、优点

  • 实现扩容
  • 分摊压力
  • 无中心配置相对简单

2.8、缺点

  • 多键操作是不被支持的
  • 多键的Redis事务是不被支持的。
  • lua脚本不被支持
  • 由于集群方案出现较晚,很多公司已经采用了其他的集群方案,而代理或者客户端分片的方案想要迁移至redis cluster,需要整体迁移而不是逐步过渡,复杂度较大。

3、应用问题解决

3.1、缓存穿透

1. 简介

  • key对应的数据在数据源并不存在,每次针对此key的请求从缓存获取不到,请求都会压到数据源,从而可能压垮数据源
  • 比如用一个不存在的用户id获取用户信息,不论缓存还是数据库都没有,若黑客利用此漏洞进行攻击可能压垮数据库

在这里插入图片描述

  1. 应用服务器压力变大了。
  2. redis命中率降低。
  3. 一直查询数据库,且数据库中也不存在该数据。从而导致缓存穿透。

2. 解决方案

  1. 对空值缓存:如果一个查询返回的数据为空(不管是数据是否不存在),我们仍然把这个空结果(null)进行缓存,设置空结果的过期时间会很短,最长不超过五分钟
  2. 设置可访问的名单(白名单):使用bitmaps类型定义一个可以访问的名单,名单id作为bitmaps的偏移量,每次访问和bitmap里面的id进行比较,如果访问id不在bitmaps里面,进行拦截,不允许访问
  3. 采用布隆过滤器:(布隆过滤器(Bloom Filter)是1970年由布隆提出的。它实际上是一个很长的二进制向量(位图)和一系列随机映射函数(哈希函数)。布隆过滤器可以用于检索一个元素是否在一个集合中。它的优点是空间效率和查询时间都远远超过一般的算法,缺点是有一定的误识别率和删除困难。将所有可能存在的数据哈希到一个足够大的bitmaps中,一个一定不存在的数据会被 这个bitmaps拦截掉,从而避免了对底层存储系统的查询压力。
  4. 进行实时监控当发现Redis的命中率开始急速降低,需要排查访问对象和访问的数据,和运维人员配合,可以设置黑名单限制服务

3.2、缓存击穿

1. 简介

  • 某个热点key对应的数据存在,但在redis中过期,此时若有大量并发请求过来,这些请求发现缓存过期一般都会从后端DB加载数据并回设到缓存,这个时候大并发的请求可能会瞬间把后端DB压垮。

在这里插入图片描述

  • redis某个热点key过期了,但这个热点key所对应的数据在数据库中存在,大量访问使用这个key。从而导致数据库访问量增大。

2. 解决方案

  1. 预先设置热门数据:在redis高峰访问之前,把一些热门数据提前存入到redis里面,加大这些热门数据key的时长。
  2. 实时调整:现场监控哪些数据热门,实时调整key的过期时长
  3. 使用锁:效率低。

在这里插入图片描述

1. 就是在缓存失效的时候(判断拿出来的值为空),不是立即去load db。
2. 先使用缓存工具的某些带成功操作返回值的操作(比如Redis的SETNX)去set一个mutex key.
3. 当操作返回成功时,再进行load db的操作,并回设缓存,最后删除mutex key;
4. 当操作返回失败,证明有线程在load db,当前线程睡眠一段时间再重试整个get缓存的方法。


3.3、缓存雪崩

1. 简介

  • 大量key对应的数据存在,但在redis中集中过期,此时若有大量并发请求过来,这些请求发现缓存过期一般都会从后端DB加载数据并回设到缓存,这个时候大并发的请求可能会瞬间把后端DB压垮。

在这里插入图片描述

  • 在极少时间段内,查询大量key的集中过期情况

2. 解决方案

  1. 构建多级缓存架构:nginx缓存 + redis缓存 +其他缓存(ehcache等)
  2. 使用锁或队列:用加锁或者队列的方式保证来保证不会有大量的线程对数据库一次性进行读写,从而避免失效时大量的并发请求落到底层存储系统上。不适用高并发情况
  3. 设置过期标志更新缓存:记录缓存数据是否过期(设置提前量),如果过期会触发通知另外的线程在后台去更新实际key的缓存。
  4. 将缓存失效时间分散开:比如我们可以在原有的失效时间基础上增加一个随机值,比如1-5分钟随机,这样每一个缓存的过期时间的重复率就会降低,就很难引发集体失效的事件。

3.4、分布式锁

1. 分布式锁是什么?

  • 原单体单机部署的系统被演化成分布式集群系统后,由于分布式系统多线程、多进程并且分布在不同机器上,这将使原单机部署情况下的并发控制锁策略失效,单纯的Java API并不能提供分布式锁的能力。那么分布式锁也就诞生了。

2. 分布式锁主流的实现方案

  1. 基于数据库实现分布式锁
  2. 基于缓存(Redis等)
  3. 基于Zookeeper

每一种分布式锁解决方案都有各自的优缺点:

  1. 性能:redis最高
  2. 可靠性:zookeeper最高

3. redis怎样实现分布式锁?

  1. 使用setnx上锁,通过del释放锁。
setnx users 20

del users
  1. 锁一直没有释放,设置key过期时间自动释放。
setnx users 10 # 上锁

expire users 10 # 设置key过期时间自动释放
  1. 如果上锁之后突然出现异常,无法设置过期时间了。但由于我们上面设置过程不是原子性操作。
# 上锁时候同时设置过期时间就可以

set users 10 nx ex 12
相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
11天前
|
存储 缓存 NoSQL
【Go语言专栏】Go语言中的Redis操作与缓存应用
【4月更文挑战第30天】本文探讨了在Go语言中使用Redis进行操作和缓存应用的方法。文章介绍了Redis作为高性能键值存储系统,用于提升应用性能。推荐使用`go-redis/redis`库,示例代码展示了连接、设置、获取和删除键值对的基本操作。文章还详细阐述了缓存应用的步骤及常见缓存策略,包括缓存穿透、缓存击穿和缓存雪崩的解决方案。利用Redis和合适策略可有效优化应用性能。
|
1天前
|
存储 监控 NoSQL
Redis哨兵&分片集群
Redis哨兵&分片集群
6 0
|
2天前
|
NoSQL 算法 Java
深入浅出Redis(八):Redis的集群模式
深入浅出Redis(八):Redis的集群模式
|
2天前
|
负载均衡 NoSQL 关系型数据库
深入浅出Redis(六):Redis的主从架构与主从复制原理
深入浅出Redis(六):Redis的主从架构与主从复制原理
|
8天前
|
NoSQL Redis
透视Redis集群:心跳检测如何维护高可用性
Redis心跳检测保障集群可靠性,通过PING命令检测主从连接状态,预防数据丢失。当连接异常时,自动触发主从切换。此外,心跳检测辅助实现`min-slaves-to-write`和`min-slaves-max-lag`策略,避免不安全写操作。还有重传机制,确保命令无丢失,维持数据一致性。合理配置心跳检测,能有效防止数据问题,提升Redis集群的高可用性。关注“软件求生”获取更多Redis知识!
109 10
透视Redis集群:心跳检测如何维护高可用性
|
10天前
|
监控 NoSQL 算法
Redis集群模式:高可用性与性能的完美结合!
小米探讨Redis集群模式,通过一致性哈希分散负载,主从节点确保高可用性。节点间健康检测、主备切换、数据复制与同步、分区策略和Majority选举机制保证服务可靠性。适合高可用性及性能需求场景,哨兵模式则适用于简单需求。一起学习技术的乐趣!关注小米微信公众号“软件求生”获取更多内容。
43 11
Redis集群模式:高可用性与性能的完美结合!
|
11天前
|
监控 NoSQL Redis
|
11天前
|
监控 NoSQL 算法
深入剖析Redis哨兵模式的原理和应用
Redis的哨兵模式是实现高可用性和自动故障转移的机制,当主服务器故障时,哨兵能自动检测并进行故障转移,确保服务连续和稳定性。哨兵模式通过监控主从服务器状态、自动故障转移、防止数据不一致,提高容错能力和负载均衡,降低运维成本,实现高可用性。哨兵通过检测主观下线和客观下线状态,以及选举Leader Sentinel来协调故障转移。Raft算法在其中用于领导者选举和状态一致性。哨兵模式通过综合考虑多种因素选举新主服务器并执行故障转移,保障集群稳定运行。
46 0
深入剖析Redis哨兵模式的原理和应用
|
13天前
|
监控 NoSQL Serverless
Serverless 应用引擎产品使用之在Serverless 应用引擎中,无法连接外部Redis数据库如何解决
阿里云Serverless 应用引擎(SAE)提供了完整的微服务应用生命周期管理能力,包括应用部署、服务治理、开发运维、资源管理等功能,并通过扩展功能支持多环境管理、API Gateway、事件驱动等高级应用场景,帮助企业快速构建、部署、运维和扩展微服务架构,实现Serverless化的应用部署与运维模式。以下是对SAE产品使用合集的概述,包括应用管理、服务治理、开发运维、资源管理等方面。
22 2
|
16天前
|
NoSQL Redis Docker
使用Docker搭建Redis主从集群
使用Docker搭建Redis主从集群
32 1