Redis 多机

举报
幼儿园老大* 发表于 2025/01/31 22:04:37 2025/01/31
1.2k+ 0 0
【摘要】 Redis 多机集群当数据量过大时,单个 Redis 数据库就无法存放。我们需要多个 Redis 数据库组成集群(cluster),去分别存放不同的数据。key-value 数据进行存入时,会根据 key 的哈希值对 16384 取模,放入相应的槽(slot)存放。这 16384 个槽会分发给各个存储空间。各个存储空间之间会相互通信,并记录所有编号的槽都存储在哪个存储空间:保证最多访问 Re...

Redis 多机

集群

当数据量过大时,单个 Redis 数据库就无法存放。我们需要多个 Redis 数据库组成集群(cluster),去分别存放不同的数据

  1. key-value 数据进行存入时,会根据 key 的哈希值对 16384 取模,放入相应的槽(slot)存放。这 16384 个槽会分发给各个存储空间。

  2. 各个存储空间之间会相互通信,并记录所有编号的槽都存储在哪个存储空间:保证最多访问 Redis 两次可以命中。

配置文件

cluster enabled yes                            # 开启集群
cluster-config-file nodes-6379.conf            # 集群配置文件(默认为 nodes.conf)Copy to clipboardErrorCopied

集群指令

  • 通过 redis-cli 打开 Redis 集群中的数据库,输入指令去插入不属于这个存储空间的键值,会返回错误。

  • 通过 redis-cli -c 打开 Redis 集群中的数据库,会自动将插入数据指令转发到相应的存储空间。

主从复制

如果数据只交给一个 Redis 服务器处理,那么可能面临两大问题:

  1. 服务器同时处理过多读写操作,超过服务器负载。
  2. 一旦服务器宕机,就会导致服务异常中断。

为了避免这两个问题,我们必须引入多个 Redis 服务器来保存相同数据,并采用主从复制结构:一个主服务器 Master 对应多个从服务器 Slave 。

  1. 读写分离:Master 负责写入数据;Slave 则会自动同步数据,并负责读取数据。起到均衡负载的作用。
  2. 数据冗余:即使某个 Slave 故障,由于其他 Slave 已保存了这些数据,并不会导致服务中断。

容错机制

  • 主从之间使用心跳建立连接。 Slave 每秒 ping 一次,汇报自己的偏移量、获取最新的指令。Master 默认每 10s ping 一次 Slave, 检查 slave 是否在线:如果 Slave 多数掉线或者高延迟,Master 停止写和数据同步功能,保障数据稳定性。

  • 在分布式系统里还会部署多个 Redis 服务器作为哨兵(除端口号外完全相同),不提供数据服务,只负责监控主从机制的运行:如果发现 Master 宕机,哨兵将通知所有机器,使 Master 下线并开启投票机制选用一个 Slave 担任 Master 。

配置连接

Master 和 Slave 通过以下过程建立连接。

相比于输入指令,我们一般直接修改 conf 文件夹内的配置文件,由机器自动建立连接。

# Master 
requirepass 123456                # 连接主服务器需要密码(可选)

# Slave
slaveof 127.0.0.1 6379            # 根据套接字自动连接主服务器
masterauth 123456                 # 主服务器密码Copy to clipboardErrorCopied

数据同步

Slave 会定期复制 Master 的持久化文件,以保证数据同步。

复制缓冲区:一个先入先出队列,用来存储 AOF 形式指令。由偏移量记录当前执行到的位置。

  • 如果开启 AOF ,在创建时就会开启复制缓冲区。(偏移量记录自己执行到的位置)
  • 如果使用 RDB ,在成为 Master 时会开启复制缓冲区。(Master 可以含有多个偏移量,记录不同 Slave 读取到的位置。)

如果 Slave 过多,数据同步也会导致 Master 负载过高。因此 Slave 也可以兼职 Master,向下级 Slave 提供服务。但层次太多会导致数据延迟,慎用。

  1. 数据同步阶段应避免流量高峰期,防止影响业务正常执行;也不要多个从服务器同时同步数据。
  2. 缓冲区应该足够大,否则一旦发生数据溢出会反复进行全量复制。
# Master
repl-backlog-size 2mb                 # 修改缓冲区大小(默认 1mb)

# Slave
slave-serve-stale-data no             # 不允许同步数据时读数据Copy to clipboardErrorCopied

命令传播

服务器每次启动都会随机生成一个运行 ID(40 位 16 进制字符) ,Master 和 Slave 之间正是依靠 Master 的运行 ID 相互识别。


Redis 潜在问题

缓存故障

Redis 缓存技术常用于高并发情况下,有效减轻服务器和数据库负载。如果 Redis 出现问题导致无法均衡负载,就可能导致服务崩溃。

  1. 缓存预热

当系统刚启动时,由于 Redis 尚未保存数据导致无法命中,数据库被频繁请求数据,由于过载导致数据库崩溃。

数据库崩溃后, Redis 和应用服务器无法获取数据,请求积压会进一步导致 Redis 和服务器崩溃。

  1. 缓存雪崩

当流量激增时,如果 Redis 大量 key 过期导致无法命中,数据库被频繁请求数据,由于过载导致数据库崩溃。

数据库崩溃后, Redis 和应用服务器无法获取数据,请求积压会进一步导致 Redis 和服务器崩溃。

  1. 缓存击穿

当流量激增时,如果 Redis 某个极高热度的 key 过期导致无法命中,数据库被频繁请求数据,由于过载导致数据库崩溃。

数据库崩溃后, Redis 和应用服务器无法获取数据,请求积压会进一步导致 Redis 和服务器崩溃。

  1. 缓存穿透

当流量激增时,如果 Redis 收到大量非法访问导致无法命中,数据库被频繁请求数据,由于过载导致数据库崩溃。

数据库崩溃后, Redis 和应用服务器无法获取数据,请求积压会进一步导致 Redis 和服务器崩溃。

一致性问题

如果在缓存中存储数据库数据备份,以提高查询效率,就一定会出现一致性问题,导致脏读。比如数据库中数据从 1 更新到 10 ,但缓存还未更新时读取,就会读取到 1。这个问题难以避免。

  1. 缓存就是缓存,必须要设过期时间。
  2. 实时性要求比较高的(比如充值),直接读数据库。
  3. 数据库并发高需要分库分表。

Redis 客户端

我们在实际使用 Redis 时往往要通过 Redis 客户端,以便在程序中直接操作 Redis 。常使用的 Redis 客户端有 Jedis、 以及功能更为高级的 Redisson、Lettuce 等。

RedisTemplate 类

Spring Boot 提供了 RedisTemplate 工具类直接对 Redis 进行操作,也提供了 StringRedisTemplate 类继承 RedisTemplate 类,两者方法完全一致。

  • RedisTemplate 类:存储数据时序列化成字节数组保存,在 Redis 中数据为字节码。读取数据时自动转化为对象。
  • StringRedisTemplate 类:存储数据直接以字符串形式保存,在 Redis 中数据直接可读。只适用于字符串类型的数据。

由于两种序列化方法不同导致的数据存储形式差异,两个类之间不能对另一方存储的 Redis 数据进行操作。

常用方法

/* 直接对 key 操作 */
redisTemplate.delete("key");                                             // 删除 key
redisTemplate.delete(collection);                                        // 批量删除 key
redisTemplate.expire("key",10,TimeUnit.MINUTES);                         // 设置 key 失效时间
Long expire = redisTemplate.getExpire("key");                            // 获取 key 失效时间
boolean flag = redisTemplate.hasKey("key");                              // 判断 key 是否存在

/* 操作字符串 */
redisTemplate.opsForValue().set("key", "value");                         // 设置键值对 
String str = (String)redisTemplate.opsForValue().get("key");             // 获取键值

/* 操作 hash */
redisTemplate.opsForHash().put("HashKey", "SmallKey", "HashValue");                  // 设置键值对
redisTemplate.boundHashOps("HashKey").putAll(hashMap);                               // 批量设置键值对
String value = (String) redisTemplate.opsForHash().get("HashKey", "SmallKey");       // 获取键值
Map entries = redisTemplate.opsForHash().entries("HashKey");                         // 获取全部键值对
redisTemplate.boundHashOps("HashKey").delete("SmallKey");                            // 删除键值对
Boolean isEmpty = redisTemplate.boundHashOps("HashKey").hasKey("SmallKey");          // 是否含有键值对
redisTemplate.opsForList();   // 操作 list
redisTemplate.opsForSet();    // 操作 set
redisTemplate.opsForZSet();   // 操作有序 set
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

作者其他文章

评论(0

抱歉,系统识别当前为高风险访问,暂不支持该操作

    全部回复

    上滑加载中

    设置昵称

    在此一键设置昵称,即可参与社区互动!

    *长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

    *长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。