Redis狂神说学习笔记

本文详细介绍了Redis的使用,从企业架构演进讲到NoSQL,特别是Redis作为键值对存储的特性。内容包括Redis的安装、基本命令、数据类型如String、List、Set、Hash、Zset等的详细操作,以及持久化、主从复制、哨兵模式等高级特性。此外,还讨论了Redis在缓存穿透、击穿和雪崩问题上的解决方案,以及Jedis和SpringBoot集成Redis的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

【狂神说Java】Redis视频


p1-3 企业架构演进

单机Mysql --> 多Mysql --> 主从复制,读写分离 --> 加入缓存 --> Mysql集群(数据分散在多个主从数据库集群中) --> NoSQL(解决各种爆发式增长的,不同的数据如定位,图片等,对MySQL数据库的压力)


p4-6 NoSQL

  • 什么是NoSQL?
    NoSQL非关系型数据库(Mysql属于关系型数据库,按照表格存储数据,行,列),用来存储个人信息,社交网络,地理位置等不需要固定格式的数据。 比如使用Map<String, Object>数据类型。
  • NoSQL特点
    解耦,方便扩展,大数据高性能,数据类型多样

NoSQL四大存储数据方式及其对应的技术

  • KV键值对
    Redis属于这一种存储方式

  • HBase
  • 文档型数据库(BSON,和JSON一样)
    MongoDB是基于分布式文件存储的数据库,是基于非关系型数据库和关系型数据库的中间产品,是非关系型数据库中功能最丰富最像关系型数据库的!
  • 图形数据库(社交关系)
    存储图关系。Neo4j

对比:

其中RedisHBaseMongoDBNeo4j 是比较重要的。


p7 Redis

Redis (Remove Dictionary Server) 远程字典服务。免费开源的最热门NoSQL技术之一,支持持久化(rdb和aof),支持网络,Key-Value数据库,结构化数据库


p9 安装Redis

可以直接通过docker安装,直接拉取redis镜像。docker的教程也可以看狂神的视频非常详细。

启动docker后用docker pull redis就可以拉取redis镜像,这里拉取镜像需要VPN并且可能会因为网络出现问题,多试几次或者使用阿里云镜像加速服务。

启动操作redis容器有多种方法:

首先需要在终端启动redis容器,执行redis-server启动命令,并打开redis持久化配置

docker run -d --name redis -p 6379:6379 redis-server --appendonly yes

通过上面的一步就以及开启了一个容器,内部的redis-server服务也开启了。

关于狂神视频中的redis配置文件如何在docker中配置可以看这篇博客:docker安装redis并以配置文件方式启动。注意创建/usr/local/docker/redis.conf需要用sudo获取权限。用挂载的配置文件启动的命令如下:

docker run -p 6379:6379 --name myredis -v /usr/local/docker/redis.conf:/etc/redis/redis.conf -v /usr/local/docker/data:/data -d redis redis-server /etc/redis/redis.conf --appendonly yes

参数说明:

  • -d :后台运行
  • –name myredis:服务名
  • -p 6379:6379 : 将容器6379端口映射到主机6379端口(前面是主机后面是容器)
  • redis-server /etc/redis/redis.conf --appendonly yes:在容器中使用/etc/redis/redis.conf 文件启动redis-server,并打开redis aof持久化配置

1 .在外部控制,不进入redis容器
你可以使用docker exec命令来进入容器,连接开启redis-cli,容器id可以通过docker ps查看;

docker exec -ti 你的容器id redis-cli		# 后面也可以加-p xxxx 来连接对应的端口

也可以用 docker exec -it 你的容器id [COMMEND] 后面加入你想要输入的命令来操作redis。

2 .直接进入容器内部操作
可以通过可视化的docker界面选择运行中的容器进入,或者输入以下代码行进入。
在这里插入图片描述

docker exec -it 容器id /bin/sh

在这里插入图片描述
进入容器之后cd到目标的目录:

cd /
cd usr/local/bin
ls

接下来就可以尽情使用了


p10 测试redis

也有两种方法:
1.在外部调用命令

docker exec -it 94255e06d918 redis-benchmark -h localhost -p 6379 -c 100 -n 100000

其中redis-benchmark的参数(其他参数自行查阅手册):

  • -h 主机地址
  • -p 端口号
  • -c 并发连接数
  • -n 请求总数

2.进入容器内部调用(注意需要cd到目标目录)

redis-benchmark -h localhost -p 6379 -c 100 -n 100000

p11

打开redis.conf,由于我们是挂载的redis.conf,直接看本机位置的redis.conf就可以了。

Redis基础知识

  • redis默认有16个数据库,可以在redis.conf中修改。默认使用第0个数据库,可以在redis-cli中使用select [index]切换数据库
  • 查看数据库大小:dbsize
  • 查看所有key: keys *
  • 清空当前/所有数据库:flushdb/flushall

Redis是单线程的!
因为redis是基于内存操作的,其瓶颈不在于CPU而是内存。

为什么Redis是单线程的还那么快?
首先多线程不一定比单线程效率高,多线程涉及CPU上下文切换。
其次Redis是把所有数据放在内存中的!!!,内存系统没有上下文切换效率是最高的,所以单线程CPU多次读写都是在一个CPU上的,这是最快的。


p12 Redis其他基本命令

  • 判断key是否存在,存在返回1,否则返回0:exists name
  • 移动key-value,把name移动到1号数据库:move name 1
  • 设置过期时间,设置name10秒过期:expire name 10,一般用于设置热点信息等。
  • 查看过期时间:ttl name,返回值为正数则表示剩余秒数,返回值为-1表示没有过期时间,返回值为-2表示已经过期。
  • 查看当前key的类型:type name

在实战中使用redis可以做单点登录。
其他命令在官网查看帮助文档


p13 String字符串类型详解

String相关命令:

  • 上面使用的set和get命令就是字符串类型的设置和读取。
  • 删除:del key [key]
  • 追加(动态修改),在原有key对应的String后面添加字符串,如果当前key不存在则set,返回字符串长度:append name zww
  • 获取字符串长度strlen name
  • 加1/减1 :incr views / decr views
  • 加减指定数:incrby views 10 / decrby views 10
  • 获取子字符串,若后面两个参数为0和3,则会取出包含0和3位置下的四个字符,若为0和-1表示所有字符:getrange key1 0 3 / getrange key1 0 -1
  • 替换指定位置开始的字符串:setrange key1 6 zww
  • 创建并设置过期时间:setex key2 30 "hello"
  • 不存在则创建,存在则不做任何事并返回0,经常在分布式锁中使用 :setnx name lll
  • 批量设置/获取:mset key1 v1 key2 v2 key3 v3 / mget key1 key2 key3
  • 批量不存在再设置,如果有一个失败则全部失败(原子性操作):msetnx key1 v1 key2 v2 key3 v3
  • 先get再set,不存在则返回null,如果存在则返回原来的值,并设置新的值,可以用来更新(类似于CAS中的比较并修改的操作):getset db redis

设置对象的用法(设置一个user1,其name=zhangsan,age=30):

方法一:设置user:1表示user1对象,其值为一个json字符串。

set user:1 {name:zhangsan,age:3}

方法二:用mset,为每个属性(域)user:{id}:{filed},设置一个key-value。

mset user:1:name zhangsan user:1:age:3

适用hash存储对象会更加合适,在下面会讲解到。


p14 List列表数据类型详解

所有list命令都是l开头的,可以存在重复的值。本质上是一个链表,如果链表中没有值则为空链表,当作不存在。

基本操作:

  • 插入列表头部lpush list one
  • 插入列表尾部rpush list right
  • 获取区间的值:lrange list 0 -1 / lrange list 0 1
  • 移除:lpop list one / rpop list right
  • 通过下标获取某个值:lindex list 1
  • 长度:llen list
  • 移除指定的值,移除最后一个加入的one,可以同时移除多个:lrem list 1 one
  • 截断(修建):ltrim list 1 2
  • 更新指定下标的值,如果list不存在或者这个下标不存在则报错:lset list 1 one
  • 插入到指定元素的前面或后面:linsert list one before/after xhp

组合操作:

  • 移除list1最右边的数把它push到list2最左边,如果没有list2会先建立一个空的list2:rpoplpush list1 list2

可以用来做消息队列


p15 Set集合数据类型详解

set集合无序且不出现重复

基本操作:

  • 添加元素,返回1添加成功,返回0表示已经存在,添加失败:sadd myset xhp
  • 获取所有元素:smembers myset
  • 查询元素是否存在:sismember myset xhp
  • 查询元素个数:scard myset
  • 移除元素:srem myset xhp
  • 随机抽选,后面可以加上个数:srandmember myset 2
  • 随机移除spop myset
  • 移动元素:smove myset1 myset2 xhp
  • 差集:sdiff key1 key2
  • 交集:sinter key1 key2 # 可以用来做共同关注,推荐好友
  • 并集:sunion key1 key2

p16 Hash哈希数据类型详解

本质上和String差不多,只是同时存下了key-vaule。

基本操作:

  • 添加/获取/批量:hset myhash field1 xhp / hget myhash field1 / hmset / hmget
  • 获取所有值:hgetall myhash
  • 删除指定:hdel myhash field1
  • 获取长度:hlen myhash
  • 是否存在:hexist myhash field1
  • 获得所有的field:hkeys myhash
  • 获得所有value:hvals myhash
  • 增加/减少:hincrby myhash field1 2 / hdecrby myhash field1 2
  • 不存在则创建,存在则不做任何事并返回0 :hsetnx myhash field2 zww

hash可以用来存储变更的数据,尤其是用户信息的保存。
hash更适合存储对象,对应了对象的属性和值,比上面介绍的String存储对象要更加适用。


p17 Zset有序集合数据类型详解

Zset是有序集合,在set的基础上增加了一个优先级值score,1表示优先级最大。

基本操作:

  • 添加:zadd myset 1 one 2 two 3 three
  • 查询升序/降序:zrange / zrevrange myset 0 -1
  • 根据score查询升序/降序:zrangebyscore / zrevrangebyscore key min max [withscores] [limit offset count]
    其中min max表示查询的范围,withscores表示是否需要带优先级值输出。
    比如我们把薪水的值作为优先级值存在zset中:
zrangebyscore salary -inf +inf withscores	# 从小到大输出的所有元素,并输出score
  • 移除元素:zrem salary zhangsan
  • 个数:zcard salary
  • 统计区间个数:zcount salary min max

Zset存储成绩表,工资表,普通消息-重要消息,排行榜应用,取Top N。

剩下的API查询官方文档即可。


p18 geospatial地理位置数据类型

经常应用在朋友定位,附近的人,打车距离计算

geospatial通过经度纬度来定位,其底层是通过Zset实现的。

在这里插入图片描述

  1. geoadd key longitude latitude member [longitude latitude member …]
    往key中添加经度longitude,维度latitude 的成员member
    比如:geoadd china:city 116.40 39.90 beijing
    在这里插入图片描述

  2. geopos key member [member …]
    获取指定城市的经度和纬度

  3. geodist key member1 member2 [m|km|ft|mi]
    获取两地的距离,其中单位默认为米m,可以指定单位千米km,英里ft,英尺mi。

  4. georadius key longitude latitude radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count] [ASC|DESC] [STORE key] [STOREDIST key]
    在key中根据当前位置和半径查找
    其中longitude latitude为当前位置的经纬度;
    radius为查找半径,m|km|ft|mi为单位;
    [WITHCOORD] [WITHDIST] [WITHHASH]分别表示附带查询目标的经纬度、距离、哈希值;
    [COUNT count]选出前几个;
    [ASC|DESC]结果按照升序还是降序排列;

  5. georadiusbymember key member radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count] [ASC|DESC] [STORE key] [STOREDIST key]
    在key中根据指定member和半径查找

  6. geohash 基本不使用

由于geospatial底层是用Zset实现的,所以可以用Zset的所有操作,比如移除zrem,查询zrange等。


p19 Hyperloglog数据类型

hyperloglog是一个用于基数统计的数据结构,优点是内存非常小,但是会有一个小误差。比如网页的UV(User Views)一个人多次访问只能算一个人,而且允许一定的误差,可以用这个数据结构。

什么是基数
基数是集合中不重复元素的个数

基本操作:

  • pfadd key element [element …] : 添加元素
  • pfcount key [key …] : 查询数量
  • pfmerge destkey sourcekey [sourcekey …] : 并集操作,把sourcekey中的元素都加到destkey中,destkey可以是空集合。

p20 Bitmap位图数据结构

Bitmap是一种位存储结构,都是操作二进制位来记录。

应用如下:统计用户信息,是否活跃,是否登录,每日打卡。比如一年打开情况365bit就可以存储了,365bits约等于46字节,非常省内存。

  • setbit key offset value : 在key的下标offset的设置value值,只能设置0或1
  • getbit key offset : 获取下标offset的值
  • bitcount key [start end] : 统计区间1的个数

p21 Redis基本事务

事务的本质就是一组命令的集合,一个事务中的所有命令会被序列化,按顺序执行

事务的执行具有一次性、顺序性、排他性。

关系型数据库支持事务的 ACID 原则:原子性、一致性、持久性、隔离性。

而Redis作为非关系型数据库其单条命令是保证原子性的,但是Redis事务是不保证原子性,并且没有隔离的概念。

Redis事务的操作:

  • 开启事务(multi)
  • 命令入队(…放入正常的命令…)
  • 执行事务(exec)
  • 放弃、取消事务(discard)

编译型异常,即一条命令本身有错误,那么事务中的所有命令都不会执行。

运行时异常,即出现类似1/0或者访问数组下标不存在这一类错误,只有出错的命令不执行,其他命令还是会执行成功,从这一点可以看出Redis事务是不保证原子性的。


p22 Redis实现乐观锁(面试常问)

Redis使用Watch监控来实现乐观锁

监视/加锁对象:watch key
解锁对象:unwatch

开启事务之前watch需要监视的对象,如果在事务执行完成之前,有其他线程修改了监视的对象,则会执行失败,返回nil。

如果事务执行失败则需要先解锁,再重新加锁,再次执行。


p23-24 使用Jedis来操作Redis和事务

Jedis是java操作Redis的中间件(jar包)。

1、导入对应的依赖

<dependencies>
    <!--导入Jedis包-->
    <!-- https://mvnrepository.com/artifact/redis.clients/jedis -->
    <dependency>
        <groupId>redis.clients</groupId>
        <artifactId>jedis</artifactId>
        <version>3.2.0</version>
    </dependency>
    <!--fastjson-->
    <!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
        <version>1.2.62</version>
    </dependency>
</dependencies>

2、编码测试

  • 连接数据库:
public class TestPing {
    public static void main(String[] args) {
        // 1 new Jedis对象,传入主机地址和端口号即可
        Jedis jedis = new Jedis("127.0.0.1", 6379);
        // Jedis的所有命令都在这个对象里,Jedis的命令和之前学习的Redis命令完全一致
        System.out.println(jedis.ping());
    }
}
  • 操作命令
  • 断开连接 :jedis.close()
  • 事务操作:
public class TestTX {
    public static void main(String[] args) {
        Jedis jedis = new Jedis("127.0.0.1", 6379);

        jedis.flushDB();

        JSONObject jsonObject = new JSONObject();
        jsonObject.put("hello", "world");
        jsonObject.put("name", "xhp");
        String result = jsonObject.toString();

        // 开启事务
        Transaction multi = jedis.multi();

        try {
            multi.set("user1", result);
            multi.set("user2", result);

            int i = 1/0;    //java代码执行异常,抛出异常

            multi.incrBy("user1",10);   //这一句命令错误,但是不会有任何异常,正确的命令还会执行
            multi.set("user3", result);

            //执行事务
            multi.exec();
        } catch (Exception e) {
            //捕获异常,放弃事务
            multi.discard();
            e.printStackTrace();
        } finally {
            System.out.println(jedis.get("user1"));
            System.out.println(jedis.get("user2"));
            System.out.println(jedis.get("user3"));
            //最终都要关闭
            jedis.close();
        }

    }
}


p25 SpringBoot集成Redis

项目勾选默认三个开发工具、Spring Web和Spring Data Redis。

SpringBoot在2.2.x之后用lettuce替代Jedis来操作Redis。

Jedis和lettuce的区别:
Jedis:直接连接Redis数据库,多个线程操作不安全,需要用Jedis pool连接池来解决这个问题。(类似于BIO的模式)
lettuce:底层采用netty,实例可以在多个线程中共享,不存在线程不安全的情况。(类似NIO模式)

整合测试过程

1.导入依赖
2.配置连接
SpringBoot 所有配置类都有一个自动配置类,Redis的自动配置类为 RedisAutoConfiguration,对应的配置文件叫RedisProperties,其源码如下:

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class)		// 绑定了一个叫RedisProperties的配置文件
@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
public class RedisAutoConfiguration {

	@Bean
	@ConditionalOnMissingBean(name = "redisTemplate")	// redisTemplate不存在时才会创建新的RedisTemplate,这意味着我们可以自己手动编写redisTemplate来进行覆盖
	@ConditionalOnSingleCandidate(RedisConnectionFactory.class)
	public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
		// Redis对象都是要进行序列化的,序列化在 new RedisTemplate 中进行
		RedisTemplate<Object, Object> template = new RedisTemplate<>();
		template.setConnectionFactory(redisConnectionFactory);
		return template;
	}

	@Bean
	@ConditionalOnMissingBean	#String时Redis的常用类型,单独提出来一个Bean
	@ConditionalOnSingleCandidate(RedisConnectionFactory.class)
	public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
		StringRedisTemplate template = new StringRedisTemplate();
		template.setConnectionFactory(redisConnectionFactory);
		return template;
	}

}

我们可以参照RedisProperties.class源码在application.properties中以spring.redis为前缀就可以对其进行配置了。比如:

#配置redis
spring.redis.host=127.0.0.1
spring.redis.port=6379
spring.redis.database=0
spring.redis.lettuce.pool.max-active=8	#记得使用lettuce不要用jedis

3.测试!
首先在测试类中注入RedisTemplate,然后就可以使用redisTemplate中封装好的方法来进行Redis操作。(在之后的实际使用过程中,我们通常会自己编写RedisTemplate做一些增强的操作来使用,而不是使用默认的RedisTemplate)

class Redis02SpringbootApplicationTests {

    //注入RedisTemplate
    @Autowired
    private RedisTemplate redisTemplate;

    @Test
    void contextLoads() {

        // redisTemplate 操作不同的数据类型有不同的opsFor方法,其中的api指令是相同的
        // opsForValue  ->String
        // opsForList   ->List
        // opsForSet
        // opsForZSet
        // opsForHash
        // opsForHyperLogLog
        // opsForGeo

        // 还有Redis事务操作 discard mutil exec等

//        // 获取Redis连接对象,进行Redis数据库的清空操作
//        RedisConnection connection = redisTemplate.getConnectionFactory().getConnection();
//        connection.flushDb();
//        connection.flushAll();

        redisTemplate.opsForValue().set("name","xhp");
        System.out.println(redisTemplate.opsForValue().get("name"));

    }

}

Redis对象的序列化

在这里插入图片描述
如果保存对象的情况需要实现对象的序列化,不然会报错。


p26 自定义RedisTemplate

1.创建RedisConfig.java类,内部编写自己的RedisTemplate,代码和所给的默认RedisTemplate一样,把@ConditionalOnMissingBean去掉即可,在中间添加上自己需要配置的代码。

2.比如配置具体的序列化方式。
下面的例子可以解决使用默认jdk序列化存储对象产生的乱码情况。

@Configuration
public class RedisConfig {

    //编写自己的redisTemplate
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(redisConnectionFactory);

        //自己配置序列化方式
        //json序列化
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.WRAPPER_ARRAY);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        //String序列化
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();

        //key采用String序列化方式
        template.setKeySerializer(stringRedisSerializer);
        //hash的key采用String序列化方式
        template.setHashKeySerializer(stringRedisSerializer);
        //value采用json序列化方式
        template.setValueSerializer(jackson2JsonRedisSerializer);
        //hash的value采用json序列化方式
        template.setHashValueSerializer(jackson2JsonRedisSerializer);
        template.afterPropertiesSet();  //把前面的序列化方式都set进Properties

        return template;
    }
}

在视频中不需要编写@Qualifier注解,因为默认的RedisTemplate编写了@ConditionalOnMissingBean注解。

3.企业级开发中会进一步封装一个RedisUtils类,内部有各种常用的操作可以自己使用。相当于自己的轮子,网上也有很多RedisUtils的现成代码可以提供参考。


p27 Redis配置文件解析

redis.conf 文件就是docker中挂载使用的文件,在启动redis时就需要调用这个配置文件。

这一部分狂神对配置文件里面的内容进行了讲解,具体如果需要修改使用的话再去翻看。


p28 Redis持久化 - RDB和AOF(面试工作重点!!!!!)

Redis是内存数据库,需要进行持久化操作。

RDB : Redis DataBase

  • 工作原理:在指定时间间隔中,把内存中的数据集快照(Snapshot)写入磁盘中,恢复时把快照文件直接读取到内存中。
  • 工作过程:Redis单独创建(fork)一个子进程来进行持久化操作,把持久化数据写入一个临时文件,持久化结束时用这个临时文件替换上次的持久化文件。
  • 默认使用RDB作为持久化方式,它比AOF更高效,但是可能会在两次持久化之间丢失数据。
  • RDB保存的持久化文件是dump.rdb
  • RDB一般在主从复制中用来作为备用。

RDB触发持久化保存机制:

  1. save命令规则。可以在redis.conf中设置save规则,满足时会自动触发RDB。
  2. 执行flushall命令。
  3. 退出redis。

RDB恢复:只要把dump.rdb文件放在redis启动目录(/usr/local/bin)即可,redis启动时会自动检查dump.rdb中的数据。

优点:

  1. 适合大规模。因为fork了一个子进程,效率很高。
  2. 对数据完整性不高。因为最后一次持久化数据可能会丢失

缺点:

  1. 占用内存。子进程需要占用额外内存。
  2. 需要时间间隔,数据会丢失。

AOF : Append Only File

  • 工作原理:把所有命令全部记录在文件中,恢复时把所有命令再执行一遍。
  • 工作过程:fork子进程以日志形式记录所有写操作(读操作不记录),只允许在之前的基础上追加文件,也是 append only file 的由来。
  • 默认不开启,需要手动修改配置文件开启(将appendonly改为 yes),两个都开启的方式下,优先使用AOF。
  • AOF保存的持久化文件是appendonly.aof,如果这个文件被破坏修改,是启动不起来的,可以使用redis-check-aof --fix appendonly.aof进行修复。
  • AOF是文件的无限追加,所以有一个重写机制来保证aof文件不会过大,默认64m时进行重写。

优点:可以每次修改都进行同步,安全性更高,但实际使用是每秒同步一次。

缺点:恢复速度慢,aof文件所占内存会很大。


p30 Redis发布订阅

Redis发布订阅(pub/sub)是一种消息通信模式:发送者发布pub消息,订阅者接受sub消息。

完成发布订阅需要消息发布者、频道、消息接受者;
频道一般用消息队列来实现。

命令:

在这里插入图片描述
订阅端先订阅频道,然后就会监听等待读取信息;发送端发布消息到频道;订阅端就会接受到三条信息,分别是消息类型,频道和消息内容。
在这里插入图片描述

底层原理:维护一个字典,字典的键就是频道,字典的值是一个链表,链表内部存储所有订阅者的客户端。发布者发布消息使用给定的频道查找对应的订阅着链表进行遍历,并将消息发布给所有订阅者。

在实际场景中可以用来:

  • 实时消息系统
  • 实时群聊天室
  • 发布订阅关注系统

在更复杂的场景中使用消息中间件(MQ)去完成更专业的功能。


p31 Redis主从复制(重点!!!)

主从复制,读写分离:将主服务器复制到几个从服务器上,主服务器负责写操作,其他从服务器负责读操作。用来解决服务器压力,架构中经常使用。因为80%的情况都是读操作。最低配置为一主二从。

在这里插入图片描述

docker伪集群搭建

多次启动镜像,并映射到不同的端口号即可,这里为了分开不同的服务器还需要为每个redis库分配不同的ip地址,并且设置bind 0.0.0.0来让所有IP都能通信。

docker network create --subnet=172.60.0.0/16 mynetwork   #在这里为了方便实验,单独创建一个bridge网桥,分配172.60.0网段
docker run -d --name redis-master --net=mynetwork -p 6380:6379 --ip 172.60.0.2 -v /usr/local/docker/redis.conf:/etc/redis/redis.conf -v /usr/local/docker/data:/data redis redis-server /etc/redis/redis.conf --bind 0.0.0.0
docker run -d --name redis-slave1 --net=mynetwork -p 6381:6379 --ip 172.60.0.3 -v /usr/local/docker/redis.conf:/etc/redis/redis.conf -v /usr/local/docker/data:/data redis redis-server /etc/redis/redis.conf --bind 0.0.0.0
docker run -d --name redis-slave2 --net=mynetwork -p 6382:6379 --ip 172.60.0.4 -v /usr/local/docker/redis.conf:/etc/redis/redis.conf -v /usr/local/docker/data:/data redis redis-server /etc/redis/redis.conf --bind 0.0.0.0

查看当前库的信息(是主库还是从库等):info replication
在这里插入图片描述
默认情况下启动的redis库都是主节点。我们只需要配置从节点,标记他的主节点即可。

slaveof host port
# 这里我们分别在不同的从库上输入主库的ip地址和端口号即完成主从复制
slaveof 172.60.0.2 6379

之后就可以用info replication查看信息,并且进行set get操作:
在这里插入图片描述
主机可以写,但是从机只能进行读操作

测试

  • 主机断开宕机了,那么主机就不能再写数据,但是从机依然可以运行读工作。如果主机之后恢复连接,不需要额外的操作,仍可以进行主从复制。
  • 从机断开,再重新启动,那么这时候就不再有主从关系,这个从机又变成自己的主机了,如果手动再设置为一个从机,则可以立即获得目标主机的所有值。这是通过一个同步操作,在第一次连接时主机启动一个进程,将所有数据都同步到slave中(全量复制)。

可以通过 slaveof no one 来让从机变回主机。


p33 哨兵模式(面试工作重点!!!!!)

作用:在主机故障宕机时,从机可以根据投票数自动将从库转换为主库,不再需要人工干预。

原理:哨兵是一个独立的进程,其原理是通过发送命令等待redis服务器响应,如果没有响应则说明服务器发生故障,进行相应的操作。 为了应对哨兵故障的可能,还使用了多哨兵模式,哨兵之间相互监控。

操作过程:主服务器宕机后,哨兵1检测到,则这时主服务器主观下线,哨兵1通知其他哨兵检查,如果多个哨兵都认为主服务器下线,则一个哨兵发起故障转移(failover)投票,选出投票最高的从机作为新主机,这个过程为客观下线

配置开启哨兵模式

  1. 创建配置文件sentinel.conf,写入对应配置。(网上有很多配置文件可以参考)最重要的命令如下:

  2. 在usr/local/bin目录下使用上面配置的文件开启哨兵进程

如果主机断开,从机晋升为主机之后,主机回来了,那么主机就会被降级为从机。

优点:

  • 所有主从复制的优点他都有。
  • 可以在故障模式下完成自动的转移,系统可用性高。

缺点:

  • Redis不能在线扩容
  • 哨兵模式的配置实现比较复杂

p35 缓存穿透、击穿和雪崩(面试高频,工作常用!!!!!!!!!!!)

一、缓存穿透(查不到)

概念

用户查询数据在缓存中没有找到,在MySQL中也没有找到,那么本次查询失败。如果同时有很多用户发送这样的请求,则缓存没有起到为MySQL分担压力的作用,最后导致数据库崩溃,这就叫缓存穿透。

解决方案

布隆过滤器:是一种数据结构,通过算法将无效请求直接丢弃。内部以hash的形式存储查询参数,在控制层先校验。

缓存空对象:如果没有命中则把空对象存储在缓存中,并设置过期时间。
存在两个问题:1.需要额外的空间存储空对象。2.空对象有过期时间,在过期时间内导致数据不一致问题。

二、缓存击穿(量太大,缓存一个key过期)

概念

大并发请求在一个热点数据上,这个热点数据由于有过期时间,在失效恢复时间间隔的一瞬间,大并发直接请求在底层数据库上,导致服务器宕机。

解决方案

热点数据不过期

加分布式互斥锁

三、缓存雪崩(很多key过期失效)

概念

在一个时间段内,很多key失效,但是这些key还是有很多请求访问,这时候MySQL服务器压力又会过大,导致宕机。

解决方案

redis高可用:使用多个redis集群,异地多活。

限流降级:在缓存失效之后,加锁或者队列来限制请求线程的数量,或者停掉一些不重要的服务。

数据预热:在即将发生大访问并发之前,手动把预测的热点数据放入缓存中,并根据情况设置不同的过期时间来符合预期要求。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值