Redis基础+使用+八股文!万字详解一篇就够!

12 篇文章 0 订阅
7 篇文章 0 订阅

一、目标

学习Redis基础必须掌握的内容:

  • 了解 Redis 以及缓存的作用;
  • 掌握 Redis 5 大基本数据类型的使用;
  • 掌握常见Redis 面试题;
  • 掌握 Redis 的持久化功能;
  • 了解 Redis 集群功能。
    img

二、什么是缓存?

缓存定义:缓存是⼀个高速数据交换的存储器,使用它可以快速的访问和操作数据。

当程序没有使用缓存:

img

这是多部分公司的普遍的架构流程图,因此当公司业务发展到⼀定规模之后,最可能出现性能瓶颈的地方就是数据库。

数据库的资源同时也是程序中最昂贵的资源,因此为了防止数据库被过度的浪费,我们就需要给它雇⼀ 个“助理”了,这个助理就是缓存系统。

加入缓存后的程序:

img
这样改造之后,所有的程序不会直接调用数据库,而是会先调用缓存,当缓存中有数据时会直接返回, 当缓存中没有数据时才去查询数据库,这样就大大的降低了数据库的压力,并加速了程序的响应速度。

1添加缓存后的优点

相比于数据库而言,缓存的操作性能更高,缓存性能高的主要原因有以下几个:

  1. 缓存⼀般都是 key-value 查询数据的,因为不像数据库⼀样还有查询的条件等因素,所以查询的性能⼀般会比数据库高;
  2. 缓存的数据是存储在内存中的,而数据库的数据是存储在磁盘中的,因为内存的操作性能远远大于磁盘,因此缓存的查询效率会高很多;
  3. 缓存更容易做分布式部署(当⼀台服务器变成多台相连的服务器集群),而数据库⼀般比较难实现 分布式部署,因此缓存的负载和性能更容易平行扩展和增加。

2缓存的分类

缓存大致可以分为两⼤类: 本地缓存、分布式缓存

①本地缓存

  • 本地缓存常见使用:Spring Cache、Mybatis的缓存等。

  • 本地缓存也叫单机缓存,也就是说可以应用在单机环境下的缓存。所谓的单机环境是指将服务部署到一台服务器上。

  • 本地缓存的特征是只适⽤于当前系统。

img

举个栗子:本地缓存相当于每家企业的公司规定⼀样,不同的公司规定也是不同的,比如上班时间,不同的公司上班时间规定也是不同的,对于企事业单位来说⼀般要求 9:00-17:00 上班,而对于酒吧来说,这个时间就完全不适合了。

②分布式缓存

  • 分布式缓存的常见使用:Redis和Memcached(已退出历史舞台),Redis属于分布式缓存的一种。

  • 分布式缓存是指可以应用在分布式系统中的缓存。所谓的分布式系统是指将⼀套服务器部署到多台服务器,并且通过负载分发将用户的请求按照⼀定的规则分发到不同服务器。

img

举个栗子:分布式缓存相当于适用于所有公司的规定,比如无论是任何公司都不能偷税漏税,不能做违反法律的事情,这种情况就和分布式缓存很像,适用于所有的系统。 比如我们在分布式系统中的服务器 A 中存储了⼀个缓存 key=xiaoming,那么在服务器 B 中也可以读取 到 key=xiaoming 的数据,这样情况就是分布式缓存的作用。

3本地缓存:Spring Cache的使用

在 Spring Boot 项目,可以直接使用 Spring 的内置 Cache(本地缓存),只需要完成以下三个步骤就可以正常使用了:

①开启缓存

开启缓存只需要在启动类上添加如下代码:

@SpringBootApplication
@EnableCaching # 开启缓存功能
public class BiteApplication {
    public static void main(String[] args) {
        SpringApplication.run(BiteApplication.class, args);
    }
}

②操作缓存

在 Service 层增加三个缓存操作的方法:添加缓存、修改缓存、删除缓存,示例代码如下:

import com.example.bittemplate.pojo.UserDO;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import java.util.concurrent.TimeUnit;
@Service
public class UserService {
    /**
 	 * 查询⽤户信息(⾛缓存)
	 */
    @Cacheable(cacheNames = "getuser", key = "#id")
    public UserDO getUserById(int id) throws InterruptedException {
        System.out.println("进⼊ get user ⽅法了。。。。。。。");
        UserDO userDO = new UserDO();
        userDO.setId(id);
        userDO.setName("Java");
        userDO.setAge(18);
        return userDO;
    }
    /**
     * 修改⽤户信息
     */
    @CachePut(cacheNames = "getuser", key = "#id")
    public UserDO updateUser(int id, String name) {
        UserDO userDO = new UserDO();
        userDO.setId(id);
        userDO.setName(name);
        return userDO;
    }
    /**
     * 删除⽤户信息
     */
    @CacheEvict(cacheNames = "getuser", key = "#id")
    public boolean delUser(int id) {
        return true;
    }
}

③调用缓存

编写触发代码,在 controller 添加如下代码:

import com.example.bite.pojo.UserDO;
import com.example.bite.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/user")
public class UserController {
    // 获得 UserService 对象
    @Autowired
    private UserService userService;
    @RequestMapping("/getuser")
    public UserDO getUserById(int id) throws InterruptedException {
        return userService.getUserById(id);
    }
    @RequestMapping("/up")
    public UserDO updateUserById(int id, String name) {
        return userService.updateUser(id, name);
    }
    @RequestMapping("/del")
    public boolean delUserById(int id) {
        return userService.delUser(id);
    }
}

以上步骤执行完之后,可以使用 Postman 模拟调用来查看缓存。

三、Redis基础知识

1.Linux安装Redis

①yum安装Redis

使用以下命令,直接将 redis 安装到 linux 服务器:

yum -y install redis

②启动 redis

redis-server /etc/redis.conf &

③操作Redis

使用以下命令启动 redis 客户端:

redis-cli![点击并拖拽以移动](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)

img

④设置远程连接
  1. 将 redis 配置文件下载到本地:redis 配置文件是 linux 下的 /etc/redis.conf
  2. 将 redis.conf 中的 “bind 127.0.0.1”注释掉;
  3. 将 redis.conf 中的**“protected-mode yes” 改为“protected-mode no”**;
  4. 将修改后的 redis.conf 上传至 liunx 下的 /etc 目录;
  5. 使用命令 redis-cli shutdown 先关闭 redis 服务,再使用 redis-server /etc/redis.conf & 启动 redis 服务。

Redis图形管理工具: Another Redis Desktop Manager

Redis管理工具无法连接的可能原因:

  1. 上述配置文件没有
  2. 服务器防火墙端口没有开放

2.Redis五大基础数据类型

  • String——字符串类型(常用)

  • Hash——字典类型(常用)

  • List——列表类型

  • Set——集合类型

  • ZSet——有序集合类型

①字符串类型

字符串类型(Simple Dynamic Strings 简称 SDS),译为:简单动态字符串,它是以键值对 keyvalue 的形式进⾏存储的,根据 key 来存储和获取 value 值,它的使用相对来说⽐较简单,但在实际项目中应非常⼴泛。

127.0.0.1:6379> set k1 v1 # 添加数据
OK
127.0.0.1:6379> get k1 # 查询数据
"v1"
127.0.0.1:6379> strlen k1 # 查询字符串的⻓度
(integer) 2 

127.0.0.1:6379> set k1 v1 ex 1000 # 设置 k1 1000s 后过期(删除) exceed缩写
OK

字符串的常见使用场景:

  • 存放用户(登录)信息( 可以使用ex设置无操作30min需要重新登陆);
  • 存放文章详情和列表信息;
  • 存放和累计网页的统计信息。 …

②字典类型

假如我们使⽤字典类型来存储⼀篇⽂章的详情信息,存储结构如下图所示:
Alt

127.0.0.1:6379> hset myhash key1 value1 # 添加数据
(integer) 1
127.0.0.1:6379> hget myhash key1 # 查询数据
"value1"

③列表类型

列表类型 (List) 是⼀个使用链表结构存储的有序结构,它的元素插入会按照先后顺序存储到链表结构中,因此它的元素操作 (插入和删除) 时间复杂度为 O(1),所以相对来说速度还是比较快的,但它的查询时间复杂度为 O(n),因此查询可能会比较慢。

127.0.0.1:6379> lpush list 1 2 3 # 添加数据
(integer) 3
127.0.0.1:6379> lpop list # 获取并删除列表的第⼀个元素
1

列表的典型使⽤场景有以下两个:

  • 消息队列:列表类型可以使用 rpush 实现先进先出的功能,同时又可以使用 lpop 轻松的弹出(查询并删除)第⼀个元素,所以列表类型可以用来实现消息队列;
  • ⽂章列表:对于博客站点来说,当⽤户和⽂章都越来越多时,为了加快程序的响应速度,我们可以把用户自己的文章存入到 List 中,因为 List 是有序的结构,所以这样又可以完美的实现分页功能,从而加速了程序的响应速度。

④ 集合类型

集合类型 (Set) 是⼀个无序并唯⼀的键值集合。

127.0.0.1:6379> sadd myset v1 v2 v3 # 添加数据
(integer) 3
127.0.0.1:6379> smembers myset # 查询集合中的所有数据
1) "v1"
2) "v3"
3) "v2"

集合类型的经典使⽤场景如下:

  • 微博关注我的人和我关注的人都适合用集合存储,可以保证人员不会重复;

  • 中奖人信息也适合用集合类型存储,这样可以保证⼀个人不会重复中奖。

集合类型(Set)和列表类型(List)的区别如下:

  • 列表可以存储重复元素,集合只能存储非重复元素;
  • 列表是按照元素的先后顺序存储元素的,而集合则是无序方式存储元素的。

⑤ 有序集合类型

有序集合类型 (Sorted Set) 相比于集合类型多了⼀个排序属性 score(分值),对于有序集合 ZSet 来说,每个存储元素相当于有两个值组成的,⼀个是有序结合的元素值,⼀个是排序值。有序集合的存储元素值也是不能重复的,但分值是可以重复的。

当我们把学生的成绩存储在有序集合中时,它的存储结构如下图所示:

Alt

127.0.0.1:6379> zadd zset1 3 golang 4 sql 1 redis # 添加数据
(integer) 3
127.0.0.1:6379> zrange zset 0 -1 # 查询所有数据,从0开始,-1是个特殊的值表示查询所有
1) "redis"
2) "mysql"
3) "java"

有序集合的经典使用场景如下:

  • 学生成绩排名;
  • 粉丝列表,根据关注的先后时间排序。

3.持久化的三种方式

所谓的持久化就是将数据从内存保存到磁盘的过程,它的目的就是为了防止数据丢失。因为内存中的数据在服务器重启之后就会丢失,而磁盘的数据则不会,因此为了系统的稳定起见,我们需要将数据进行持久化。

同时持久化功能⼜是 Redis 和 Memcached 最主要的区别之⼀,因为 Redis ⽀持持久化⽽ Memcached 不⽀持。

Redis 持久化的方式有以下 3 种:

1️⃣快照方式(RDB, Redis DataBase):将某⼀个时刻的内存数据,以⼆进制的方式写入磁盘;

2️⃣文件追加方式(AOF, Append Only File):记录所有的操作命令,并以文本的形式追加到文件中;

3️⃣混合持久化方式:Redis 4.0 之后新增的方式,混合持久化是结合了 RDB 和 AOF 的优点,在写入的时候,先把当前的数据以 RDB 的形式写入文件的开头,再将后续的操作命令以 AOF 的格式存入文件,这样既能保证 Redis 重启时的速度,⼜能减低数据丢失的风险。

持久化策略设置:

👊混合持久化:可以在 redis-cli 命令行中执行 config set aof-use-rdb-preamble yes 来开启混合持久化,当开启混合持久化时 Redis 就以混合持久化方式来作为持久化策略;

👊AOF:当没有开启混合持久化的情况下,使用 config set appendonly yes 来开启 AOF 持久化的策略;

👊RDB:当 AOF 和混合持久化都没开启的情况下默认会是 RDB 持久化的方式。

①RDB

优点

  • RDB 的内容为⼆进制的数据,占⽤内存更小,更紧凑,更适合做为备份⽂件;
  • RDB 对灾难恢复非常有用,它是⼀个紧凑的文件,可以更快的传输到远程服务器进行 Redis 服务 恢复;
  • RDB 可以更⼤程度的提高 Redis 的运行速度,因为每次持久化时 Redis 主进程都会 fork() ⼀个子进程,进行数据持久化到磁盘,Redis 主进程并不会执行磁盘 I/O 等操作;
  • 与 AOF 格式的⽂件相比,RDB ⽂件可以更快的重启。

缺点

  • 因为 RDB 只能保存某个时间间隔的数据,如果中途 Redis 服务被意外终止了,则会丢失⼀段时间内的 Redis 数据;
  • RDB 需要经常 fork() 才能使用子进程将其持久化在磁盘上。如果数据集很大,fork() 可能很耗时, 并且如果数据集很大且 CPU 性能不佳,则可能导致 Redis 停⽌为客户端服务几毫秒甚至一秒钟。

②AOF

优点

  • AOF 持久化保存的数据更加完整,AOF 提供了三种保存策略:每次操作保存、每秒钟保存⼀次、 跟随系统的持久化策略保存。其中每秒保存⼀次,从数据的安全性和性能两方面考虑是⼀个不错的选择,也是 AOF 默认的策略,即使发生了意外情况,最多只会丢失 1s 钟的数据;
  • AOF 采用的是命令追加的写入方式,所以不会出现文件损坏的问题,即使由于某些意外原因,导致了最后操作的持久化数据写⼊了⼀半,也可以通过 redis-check-aof ⼯具轻松的修复;
  • AOF 持久化文件,⾮常容易理解和解析,它是把所有 Redis 键值操作命令,以文件的方式存入了 磁盘。即使不小心使用 flushall 命令删除了所有键值信息,只要使用 AOF 文件,删除最后的 flushall 命令,重启 Redis 即可恢复之前误删的数据。

缺点

  • 对于相同的数据集来说,AOF 文件要大于 RDB 文件;
  • 在 Redis 负载比较高的情况下,RDB 比 AOF 性能更好;
  • RDB 使用快照的形式来持久化整个 Redis 数据,而 AOF 只是将每次执⾏的命令追加到 AOF ⽂件 中,因此从理论上说,RDB 比 AOF 更健壮。

③删混合持久化

优点

  • 混合持久化结合了 RDB 和 AOF 持久化的优点,开头为 RDB 的格式,使得 Redis 可以更快的启动,同时结合 AOF 的优点,有减低了⼤量数据丢失的风险。

缺点

  • AOF 文件中添加了 RDB 格式的内容,使得 AOF 文件的可读性变得很差;
  • 兼容性差,如果开启混合持久化,那么此混合持久化 AOF 文件,就不能⽤在 Redis 4.0 之前版本 了。

四、Redis的使用

1.添加依赖

Spring Data Redis(Access+Driver)

Alt

2.修改配置

spring.redis.database=0
spring.redis.port=6379
spring.redis.host=82.157.146.10
#可省略
spring.redis.lettuce.pool.min-idle=5
spring.redis.lettuce.pool.max-idle=10
spring.redis.lettuce.pool.max-active=8
spring.redis.lettuce.pool.max-wait=1ms
spring.redis.lettuce.shutdown-timeout=100ms

3.手动代码方式操作

了解即可

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.concurrent.TimeUnit;
@RestController
public class RedisController {
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    // 在 redis 存储数据
    @RequestMapping("/setrs")
    public String setRedis(String name, String value) {
        stringRedisTemplate.opsForValue().set(name, value,30, TimeUnit.SECONDS);
        return "Set redis success.";
    }
    // 读取 redis 中的数据
    @RequestMapping("/getrs")
    public String getRedis(String name) {
        Object valObj = stringRedisTemplate.opsForValue().get(name);
        if (valObj != null) return valObj.toString();
        return "Null";
    }
}

3.注解方式操作

相关注解

@EnableCaching:开启全局注解缓存。

@Cacheable:查询/添加操作,判断Redis是否有缓存,如果没有那么就把当前放啊返回值存到Redis;如果存在缓存直接返回。

@CachePut:修改操作,将方法返回值更新到Redis缓存中。

@CacheEvict:删除操作,将Redis中的对应缓存删除。

1.开启全局注解缓存

package com.example.redis;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;

@EnableCaching
@SpringBootApplication
public class RedisApplication {
    public static void main(String[] args) {
        SpringApplication.run(RedisApplication.class, args);
    }
}

2.实现Redis的增删查改

package com.example.redis.controller;


import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class RedisController {
    /**
     * Cacheable    查询后 有结果就返回结果
     * 				没有结果就 add一个缓存,value为返回值
     * @param name 姓名  tel 电话
     * @key 使用#传递参数,和下方参数相同,可以为多个,注意分隔方式
     */
    @RequestMapping("/get-and-add")
    @Cacheable(value = "cache", key = "#name+'-'+#tel")
    public String getAndAdd(String name, String tel) {
        System.out.println("执行了 getAndAdd");
        //非空判断
        if (!StringUtils.hasLength(name) && !StringUtils.hasLength(tel)) {
            return "请先输入name和age";
        }
        //返回的内容即为这个key的value值
        return "name" + name + " | age" + tel;
    }

    /**
     * CachePut     通过传入的name和value定位需要修改的对象
     * 修改后的结果为 返回值
     */
    @RequestMapping("/put")
    @CachePut(value = "cache", key = "#name+'-'+#tel")
    public String put(String name, String tel) {
        System.out.println("执行了 put");
        return "[name=="+name+"#tel=="+tel+"]";
    }

    /**
     * CacheEvict   删除这个对象
     */
    @RequestMapping("/del")
    @CacheEvict(value = "cache", key = "#name+'-'+#tel")
    public void del(String name, String tel) {
        System.out.println("执行了 del");
    }

五、Spring Session持久化

1.添加依赖

不仅要添加 ,还要添加 
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pc8eHh1A-1686819963246)(C:\Users\19396\AppData\Roaming\Typora\typora-user-images\image-20230615152956260.png)]

2.修改配置

spring:
  #Redis相关配置
  redis:
    database: 0
    host: 120.53.20.213 #Redis的地址
    port: 6379 #端口号,6379为默认的端口号
  #Session相关配置
  session:
    store-type: redis #存放在 Redis 中,这样就不会存储到内存中了
    timeout: 1800
    redis:
      flush-mode: on_save
      namespace: spring:session #放置的位置,相当于文件夹

3.操作Session

其实操作和我们之前的操作一模一样。

设置默认保存位置为 Redis 后,框架会自动帮我们存储到 Redis 中,其实也就实现了持久化。

package com.example.redis.controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

@RestController("redis-session")
public class RedisSessionController {
    private final static String SESSION_KEY = "USER_SESSION_KEY";//设置Key值

    @RequestMapping("/login")
    public String login(HttpSession session) {
        //会自动存储到Redis中
        session.setAttribute(SESSION_KEY, "liMing");
        return "登录成功";
    }

    @RequestMapping("get")
    public String get(HttpServletRequest request) {
        HttpSession session = request.getSession(false);
        if (session != null) {
            //从Redis中获取用户
            Object user = session.getAttribute(SESSION_KEY);
            if (user != null) {
                return user.toString();
            }
        }
        return "暂无Session,未登录";
    }
}

六、面试题

基础内容

在上文中已经提到

1.什么是缓存?

2.缓存的优点和分类?

3.Redis五大基础数据类型?

4.持久化的三种方式?三种方式的优缺点?

常问的

1.缓存雪崩

缓存雪崩是指在短时间内,有大量缓存同时过期,导致⼤量的请求直接查询数据库,从⽽对数据库造成 了巨大的压力,严重情况下可能会导致数据库宕机的情况叫做缓存雪崩。

正常情况下系统的执行流程如下图所示:

image-20230615130330489

缓存雪崩的执行流程,如下图所示:

image-20230615130342108

以上对比图可以看出缓存雪崩对系统造成的影响,那如何解决缓存雪崩的问题? 主要有 加锁排队、随机化过期时间、设置⼆级缓存。

加锁排队

加锁排队可以起到缓冲的作用,防止大量的请求同时操作数据库,但它的缺点是增加了系统的响应时间,降低了系统的吞吐量,牺牲了⼀部分用户体验。

随机化过期时间

为了避免缓存同时过期,可在设置缓存时添加随机时间,这样就可以极大的避免⼤量的缓存同时失效。 示例代码如下:

// 缓存原本的失效时间
int exTime = 10 * 60;
// 随机数⽣成类
Random random = new Random();
// 缓存设置
jedis.setex(cacheKey, exTime+random.nextInt(1000) , value);

设置二级缓存

⼆级缓存指的是除了 Redis 本身的缓存,再设置⼀层缓存,当 Redis 失效之后,先去查询⼆级缓存。

例如可以设置⼀个本地缓存,在 Redis 缓存失效的时候先去查询本地缓存而非查询数据库。

加⼊⼆级缓存之后程序执⾏流程,如下图所示:
alt

2.缓存穿透

缓存穿透是指查询数据库和缓存都无数据,因为数据库查询无数据,出于容错考虑,不会将结果保存到缓存中,因此每次请求都会去查询数据库,这种情况就叫做缓存穿透。

缓存穿透执行流程如下图所示:(其中红⾊路径表示缓存穿透的执行路径,可以看出缓存穿透会给数据库造成很大的压力)

alt

解决方案:缓存空结果

我们可以把每次从数据库查询的数据都保存到缓存中,为了提⾼前台用户的使用体验 (解决长时间内查询不到任何信息的情况),我们可以将空结果的缓存时间设置的短一些,例如 3-5 分钟。

3.缓存击穿

缓存击穿指的是某个热点缓存,在某⼀时刻恰好失效了,然后此时刚好有⼤量的并发请求,此时这些请求将会给数据库造成巨大的压力,这种情况就叫做缓存击穿。

缓存击穿的执行流程如下图所示:

alt

解决方案有:加锁排队、设置永不过期

加锁排队

此处理⽅式和缓存雪崩加锁排队的⽅法类似,都是在查询数据库时加锁排队,缓冲操作请求以此来减少服务器的运行压力

设置永不过期

对于某些热点缓存,我们可以设置永不过期,这样就能保证缓存的稳定性,但需要注意在数据更改之后,要及时更新此热点缓存,不然就会造成查询结果的误差。

4.缓存预热

首先来说,缓存预热并不是⼀个问题,而是使用缓存时的⼀个优化方案,它可以提高前台用户的使用体验。

缓存预热指的是在系统启动的时候,先把查询结果预存到缓存中,以便用户后面查询时可以直接从缓存中读取,以节约用户的等待时间。

缓存预热的执行流程,如下图所示:

image-20230615132338279

缓存预热的实现思路有以下三种:

  1. 把需要缓存的方法写在系统初始化的方法中,这样系统在启动的时候就会自动的加载数据并缓存数据;
  2. 把需要缓存的方法挂载到某个页面或后端接口上,⼿动触发缓存预热;
  3. 设置定时任务,定时自动进行缓存预热。

Redis集群

随着业务的不断发展,单机 Redis 的性能已经不能满足我们的需求了,此时我们需要将单机 Redis 扩 展为多机服务,Redis 多机服务主要包含以下 3 个内容:

  • Redis 主从同步
  • Redis 哨兵模式
  • Redis 集群服务(Redis 3.0 新增功能)

下⾯我们分别来看这 3 部分的内容。

1.Redis 主从同步

主从同步 (主从复制) 是 Redis 高可用服务的基石,也是多机运行中最基础的⼀个。

我们把主要存储数据的节点叫做主节点 (master),把其他通过复制主节点数据的副本节点叫做从节点 (slave),如下图所示:
alt

在 Redis 中⼀个主节点可以拥有多个从节点,⼀个从节点也可以是其他服务器的主节点,如下图所示:
alt

主从服务器设置

在 Redis 运行过程中,我们可以使⽤ replicaof host port 命令,把自己设置为目标 IP 的从服务器,执行命令如下:

127.0.0.1:6379> replicaof 127.0.0.1 6380
OK

如果主服务设置了密码,需要在从服务器输入主服务器的密码,使⽤ config set masterauth 主服务密码命令的方式,例如:

127.0.0.1:6377> config set masterauth pwd654321
OK

主从同步优、缺点分析

主从同步具有以下 3 个优点:

  • 性能方面:有了主从同步之后,可以把查询任务分配给从服务器,用主服务器来执行写操作,这样极大的提高了程序运行的效率,把所有压力分摊到各个服务器了;
  • 高可用:当有了主从同步之后,当主服务器节点宕机之后,可以很迅速的把从节点提升为主节点,为 Redis 服务器的宕机恢复节省了宝贵的时间;
  • 防止数据丢失:当主服务器磁盘坏掉之后,其他从服务器还保留着相关的数据,不至于数据全部丢失

主从同步的缺点:

  • 这种模式本身存在⼀个致命的问题,当主节点奔溃之后,需要人工干预才能恢复 Redis 的正常使用。

    解决这一问题我们可以使用哨兵模式

2.哨兵模式

假如晚上发生了主从服务器宕机的情况,尤其是在主从服务器比较多的情况下,如果需要人工恢复,那么需要的时间和难度是很大的,因此我们需要⼀个自动的⼯具——Redis Sentinel (哨兵模式) 来把手动的过程变成自动的,让 Redis 拥有自动容灾恢复 (failover) 的能力。

也就是说:使用哨兵模式可以用来监控主从同步服务器节点,并在主从服务器出现问题的时候实现自动容灾恢复。

哨兵模式如下所示(⼩贴⼠:Redis Sentinel 的最⼩分配单位是⼀主⼀从。):

alt

哨兵工作原理

哨兵的⼯作原理是,首先每个 Sentinel 会以每秒钟 1 次的频率,向已知的主服务器、从服务器和以及其他 Sentinel 实例,发送⼀个 PING 命令。

如果最后⼀次有效回复 PING 命令的时间超过 down-after-milliseconds 所配置的值 (默认 30s),那么这个实例会被 Sentinel 标记为主观下线。

如果⼀个主服务器被标记为主观下线,那么正在监视这个主服务器的所有 Sentinel 节点,要以每秒 1 次的频率确认主服务器的确进入了主观下线状态。

如果有足够数量 (quorum 配置值) 的 Sentinel 在指定的时间范围内同意这⼀判断,那么这个主服务器被标记为客观下线。此时所有的 Sentinel 会按照规则协商自动选出新的主节点。

注意:⼀个有效的 PING 回复可以是:+PONG-LOADING 或者 -MASTERDOWN。如果返回值非以上三种回复,或者在指定时间内没有回复 PING 命令, 那么 Sentinel 认为服务器返回的回复无效 (non-valid )。

Redis主从同步 + 哨兵模式 并不是 Redis 多机运行最完美的解决方案,集群模式才是现在常用的

3.Redis 集群服务

Redis 集群(Redis Cluster)是 Redis 多机运行最完美的终极方案,它是 Redis 3.0 之后推出的服务,它的出现可以让我们完全抛弃主从同步和哨兵模式来实现 Redis 多机运行。

Redis Cluster 是无代理模式去中心化的运行模式,客户端发送的绝大数命令会直接交给相关节点执行,这样大部分情况请求命令无需转发,或仅转发⼀次的情况下就能完成请求与响应,所以集群单个节点的性能与单机 Redis 服务器的性能是非常接近的,因此在理论情况下,当水平扩展⼀倍的主节点就相当于请求处理的性能也提高了⼀倍,所以 Redis Cluster 的性能是非常高的。

Redis Cluster 架构图如下所示:

alt

如果⼀个主服务器被标记为主观下线,那么正在监视这个主服务器的所有 Sentinel 节点,要以每秒 1 次的频率确认主服务器的确进入了主观下线状态。

如果有足够数量 (quorum 配置值) 的 Sentinel 在指定的时间范围内同意这⼀判断,那么这个主服务器被标记为客观下线。此时所有的 Sentinel 会按照规则协商自动选出新的主节点。

注意:⼀个有效的 PING 回复可以是:+PONG-LOADING 或者 -MASTERDOWN。如果返回值非以上三种回复,或者在指定时间内没有回复 PING 命令, 那么 Sentinel 认为服务器返回的回复无效 (non-valid )。

Redis主从同步 + 哨兵模式 并不是 Redis 多机运行最完美的解决方案,集群模式才是现在常用的

3.Redis 集群服务

Redis 集群(Redis Cluster)是 Redis 多机运行最完美的终极方案,它是 Redis 3.0 之后推出的服务,它的出现可以让我们完全抛弃主从同步和哨兵模式来实现 Redis 多机运行。

Redis Cluster 是无代理模式去中心化的运行模式,客户端发送的绝大数命令会直接交给相关节点执行,这样大部分情况请求命令无需转发,或仅转发⼀次的情况下就能完成请求与响应,所以集群单个节点的性能与单机 Redis 服务器的性能是非常接近的,因此在理论情况下,当水平扩展⼀倍的主节点就相当于请求处理的性能也提高了⼀倍,所以 Redis Cluster 的性能是非常高的。

Redis Cluster 架构图如下所示:

加粗样式alt

从上图可以看出 Redis 的主从同步只能有⼀个主节点,而 Redis Cluster 可以拥有无数个主从节点,因此 Redis Cluster 拥有更强大的平行扩展能力,也就是说当 Redis Cluster 拥有两个主从节点时,从理论上来讲 Redis 的性能相比于单机服务来说性能提升了 2 倍。

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

shn!

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值