Redis学习笔记

概述

NoSQL

关系型数据库:

优点:

  • 易于维护:都是使用表结构,格式一致;
  • 使用方便:SQL语言通用,可用于复杂查询;
  • 复杂操作:支持SQL,可用于一个表以及多个表之间非常复杂的查询。

缺点:

  • 读写性能比较差,尤其是海量数据的高效率读写;
  • 固定的表结构,灵活度稍欠;
  • 高并发读写需求,对传统关系型数据库来说,硬盘I/O是一个很大的瓶颈。

关系型数据库
Nosql,泛指非关系型数据库:

特点:

  • 易拓展:数据之间无关系;
  • 大数据量,高性能:Redis一秒写8万次,读取11万次;
  • 灵活的数据结构:无须事先为要存储的数据建立字段,随时可以存储自定义的数据格式;
  • 高可用。

NoSQL

Nosql分类:键值(Key-Value)存储数据库、列存储数据库、文档型数据库、图形(Graph)数据库。

NoSQL四大类型

Redis

Redis,Remote Dictionary Server,远程字典服务,是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。

Redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步。

Redis作用:

  • 内存存储、持久化(rdb,aof);
  • 效率高,可以用于高速缓存;
  • 发布订阅系统;
  • 地图信息分析;
  • 计时器、计数器;
  • …………

官网:https://redis.io/
中文网站:http://www.redis.cn/

数据结构

String

Map<String, String>

应用场景:

  • 缓存功能:利用redis支持高并发的特点,大大加快系统的读写速度;
  • 验证码:利用String对应关系和过期时间的设置来实现;
  • 计数器:利用自增自减命令来实现;
  • 存储对象:将对象转换为JSON字符串进行存储;
  • 共享session:用redis将用户session集中管理,每次获取用户更新或查询登录信息都直接从redis中集中获取;
  • 分布式锁:利用setnx和setex实现分布式锁。

Hash

Map<String, Map<String, String>>

应用场景:

  • 利用hashmap的对应关系,比string更适合存储对象信息。

List

Map<String, List<>>

应用场景(链表结构,适合作为栈和队列使用):

  • 利用list的数据具有顺序的特征对信息进行管理(系统运营数据、新闻、资讯等);
  • 利用队列模型解决多路信息汇总合并的问题;
  • 利用栈模型解决最新消息等问题。

Set

Map<String, Set<>>

应用场景:

  • 利用Redis的Set数据类型的唯一性跟踪一些唯一性数据,比如访问某一博客的唯一IP地址信息。对于此场景,我们仅需在每次访问该博客时将访问者的IP存入Redis中,Set数据类型会自动保证IP地址的唯一性;
  • 利用唯一性和交并差关系处理用户关系,例如共同好友共同关注等。

Zset

Map<String, Sorted Set<>>

通过给每个member赋一个score实现排序,其中member唯一,score不唯一。

应用场景:

  • 利用排序的特性,实现成绩单、排行榜、重要度等功能。

Geospatial

Geo的底层实现是Zset,可以用Zset命令来操作geo。

应用场景:

  • 更好的支持位置类的数据,利用距离计算和半径搜索等命令实现附近的人、周边商铺等功能;
  • 利用key对位置信息进行分类存储,利用member查询明细信息等;
  • 作为缓存使用,结合其他持久化存储使用。

Hyperloglog

基数:不重复的元素。
Hyperloglog用来进行基数统计。

优点:占用的内存是固定的,2^64不同的元素的存储,只需要12KB内存。
缺点:有0.81%错误率。

应用场景:

  • 用来进行基数统计,可以实现统计页面实时uv、统计注册ip数等功能。比起set,更方便且更节省空间。需要接受错误率。

Bitmaps

Redis提供了Bitmaps这个“数据类型”可以实现对位的操作:

  • Bitmaps本身不是一种数据类型,实际上它就是字符串,但是它可以对字符串的位进行操作;
  • Bitmaps单独提供了一套命令,所以在Redis中使用Bitmaps和使用字符串的方法不太相同。可以把Bitmaps想象成一个以位为单位的数组,数组的每个单元只能存储0和1,数组的下标在Bitmaps中叫做偏移量。

应用场景:

  • 统计用户活跃度、登录情况、打卡情况、感染情况等。在面对只有两个状态的情景时更方便且更节省空间。

事务

Redis事务:一组命令的集合,一个事务中的所有命令都会被序列化,在事务的执行过程中会按照顺序执行。

Redis事务具有一次性、顺序性、排他性。
Redis事务没有隔离级别的概念。
Redis单条命令保证原子性,但是事务不保证原子性。

Redis的事务:

  1. 开启事务;
  2. 命令入队;
  3. 执行事务。

编译型异常:执行事务时报错,所有的命令都不会被执行。
运行时异常:其他命令可以正常执行,错误命令抛出异常。因为不需要对回滚进行支持,所以 Redis 的内部可以保持简单且快速。

利用watch监视数据是否修改,若数据变化了则整个事务会被取消,实现乐观锁操作。

LUA脚本

如果想逻辑性的一起执行多条指令,在执行过程不被别的请求打断,那么Redis提供了Lua脚本,Redis服务器会单线程原子性执行lua脚本,保证了在执行过程中不会被其他请求打断。

优点:

  • 减少网络开销。可以将多个请求通过脚本的形式一次发送,减少网络时延。
  • 原子操作。Redis会将整个脚本作为一个整体执行,中间不会被其他请求插入。因此在脚本运行过程中无需担心会出现竞态条件,无需使用事务。
  • 复用。客户端发送的脚本会永久存在redis中,这样其他客户端可以复用这一脚本,而不需要使用代码完成相同的逻辑。

JAVA

JAVA客户端

官方推荐的java客户端有Jedis、Lettuce、Redisson。

  • Jedis: redis的Java实现客户端,提供了比较全面的Redis命令的支持。
  • Lettuce:Lettuce是一个高性能基于Java编写的Redis驱动框架,底层集成了Project Reactor提供自然的反应式编程,通讯框架集成了Netty使用了非阻塞IO,5.x版本以后融合了JDK1.8的异步编程特性,在保证高性能的同时提供了十分丰富易用的API。
  • Redission:Redisson是一个在Redis的基础上实现的Java驻内存数据网格(In-Memory Data Grid)。它不仅提供了一系列的分布式的Java常用对象,还提供了许多分布式服务。其中包括(BitSet, Set, Multimap, SortedSet, Map, List, Queue, BlockingQueue, Deque, BlockingDeque, Semaphore, Lock, AtomicLong, CountDownLatch, Publish / Subscribe, Bloom filter, Remote service, Spring cache, Executor service, Live Object service, Scheduler service) Redisson提供了使用Redis的最简单和最便捷的方法。Redisson的宗旨是促进使用者对Redis的关注分离(Separation of Concern),从而让使用者能够将精力更集中地放在处理业务逻辑上。

性能对比:
Jedis的性能比lettuce和Redisson都要差一点,三者的主要差异在于以下:

  • Jedis使用同步和阻塞IO的方式,不支持异步;lettuce和Redisson支持异步,底层是基于netty框架的事件驱动作为通信层;
  • Jedis设计上就是基于线程不安全来设计,一个连接只能被一个线程使用,但是可以结合连接池来提高其性能;lettuce和Redis基于线程安全来设计的,一个连接是被共享使用的,但是也提供了连接池,主要用于事务以及阻塞操作的命令;
  • lettuce和Redisson支持异步流的方式。

功能对比:

  • Jedis: 提供比较全面的redis原生指令的支持,上层封装比较弱,集群特性支持度非常低,高级特性几乎没有。
  • Lettuce: 高级redis客户端,支持各种模式的redis连接和操作,高级特性几乎没有。
  • Redisson: 高级redis客户端,支持各种模式的redis连接和操作,同时提供一大堆的实用功能。

SpringBoot整合

SpringBoot初始配置
SpringBoot2.x版本后,其内置的Redis中间件从Jedis换成了lettuce。

#自动配置类  RedisAutoConfiguration
#配置文件   RedisProperties

序列化配置_1
序列化配置_2
因为java使用ISO-8859-1编码进行传输数据的,那么传输字符串的话,编解码会不一致,一定会出现乱码,所以需要自己重写一个RedisTemplate来修改默认的序列化方式。

@Configuration
public class RedisConfig {

    @Bean

    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(redisConnectionFactory);
        // jackson序列化所有的类
        Jackson2JsonRedisSerializer Jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        // jackson序列化的一些配置
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance);
        Jackson2JsonRedisSerializer.setObjectMapper(om);
        // String的序列化
        StringRedisSerializer stringSerializer = new StringRedisSerializer();

        //key采用String的序列化方式
        template.setKeySerializer(stringSerializer);
        //hash的key也采用String的序列化方式
        template.setHashKeySerializer(stringSerializer);
        //value采用jackson序列化方式
        template.setValueSerializer(Jackson2JsonRedisSerializer);
        //hash的value也采用jackson序列化方式
        template.setHashValueSerializer(Jackson2JsonRedisSerializer);

        template.afterPropertiesSet();

        return template;
    }
}

Redis管道

Redis管道技术可以在服务端未响应时,客户端可以继续向服务端发送请求,并最终一次性读取所有服务端的响应。使用管道技术可以大大提高速度性能。
管道_1
管道_2

redis.conf

https://blog.csdn.net/sinat_25207295/article/details/117925174

Redis持久化

RDB(Redis DateBase)

RDB

在指定时间间隔内将内存中的数据集快照写入磁盘,恢复时将将快照文件直接读入内存里。Redis单独fork一个子进程来进行持久化,先将数据写入一个临时文件中,待持久化过程都结束之后,用临时文件替换上次持久化好的文件。整个过程中主进程不进行IO操作,确保了极高的性能。

如果需要进行大规模数据的恢复,且对于数据恢复的完整性不是非常敏感,那RDB方式要比AOF方式更加的高效。RDB的缺点是最后一次持久化后的数据可能丢失。

默认使用RDB。

触发机制:

  • 满足save的规则,触发RDB;
  • 执行flushall命令,默认触发RDB;
  • 退出Redis,产生RDB文件。

优点:

  • RDB是一个非常紧凑的文件,它保存了redis 在某个时间点上的数据集。这种文件非常适合用于进行备份和灾难恢复;
  • 生成RDB文件的时候,redis主进程会fork()一个子进程来处理所有保存工作,主进程不需要进行任何磁盘IO操作;
  • RDB 在恢复大数据集时的速度比 AOF 的恢复速度要快。

缺点:

  • 作创建子进程,属于重量级操作,如果不采用压缩算法(主进程 fork 出子进程,其实是共享一份真实的内存空间,但是为了能在记录快照的时候,也能让主线程处理写操作,采用的是 Copy-On-Write(写时复制)技术,只有需要修改的内存才会复制一份出来,所以内存膨胀到底有多大,看修改的比例有多大),频繁执行成本过高(影响性能);
  • RDB文件使用特定二进制格式保存,Redis版本演进过程中有多个格式的RDB版本,存在老版本Redis服务无法兼容新版RDB格式的问题(版本不兼容);
  • 在一定间隔时间做一次备份,所以如果redis意外down掉的话,就会丢失最后一次快照后的所有修改(数据有丢失)。

AOF(Append Only File)

AOF

以日志的形式来记录每个写操作(增量保存),将Redis执行过的所有写指令记录下来(读操作不记录), 只许追加文件但不可以改写文件,redis启动之初会读取该文件重新构建数据,换言之,redis 重启的话就根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作。

默认不使用,需要手动设置开启。

优点:

  • 备份机制更稳健,丢失数据概率更低;
    缺点:
  • 比起RDB占用更多的磁盘空间;
  • 恢复备份速度要慢;
  • 每次读写都同步的话有一定性能压力;
  • 重写时会导致系统波动;
  • 存在个别bug,造成恢复不能。

应用

  1. 如果只作为缓存使用,只希望数据在服务器运行的时候存在,可以不适用任何持久化。
  2. 同时使用两种持久化方案时:
    1)Redis重启的时候优先载入AOF文件来恢复原始的数据,以进一步保证数据完整性;
    2)不建议只使用AOF文件。比起AOF,RBD更方便备份、快速重启,也不会有AOF可能有的潜在bug。
  3. 性能建议:
    1)若RDB文件只作后备用途,建议只在salve上持久化RDB文件,而且备份频率不用过高;
    2)如果使用AOF,好处是保证数据完整性,最恶劣的情况下也只会丢失不超过两秒的数据。代价一是带来了持续的IO,二是重写机制会造成阻塞。只要硬盘许可,应尽量减少重写的频率;
    3)如果不使用AOF,仅靠Master-Slave Repllcation实现高可用也可以,能减少很多IO,也减少了重写时带来的系统波动。代价是如果Master/Slave同时倒掉会丢失很久的数据。启动脚本时应该比较两个Master/Slave中的RDB文件,载入较新的那个。

Redis订阅发布

PubSub

Redis发布订阅(pub/sub)是一种消息通信模式:发布者(pub)发布消息,订阅者(sub)接受消息。Redis客户端可以订阅任意数量的频道。
pubsub_1
pubsub_2
pubsub_3

实现原理:

  1. 通过subscribe命令订阅某频道后,redis-server里维护了一个字典,字典的键就是一个个channel,而字典的值则是一个链表,链表中保存了所有订阅这个channel的客户端。subscribe命令的关键,就是将客户端添加到给定channel的订阅链表中。
  2. 通过publish命令向订阅者发送消息,redis-server会使用给定的频道作为键,在它所维护的channel字典中查找记录了订阅这个频道的所有客户端的链表,遍历这个链表,将消息发布给所有订阅者。
  3. Pub/Sub从字面上理解就是发布(Publish)与订阅(Subscribe),在Redis中,你可以设定对某一key值进行消息发布及消息订阅,当一个key值上进行了消息发布后,所有订阅它的客户端都会收到相应的消息。这一功能最明显的用法就是用作实时消息系统,比如普通的即时聊天、群聊等功能。

使用注意:

  • 客户端订阅了channel之后,如果接收消息不及时,可能导致DCS实例消息堆积,当达到消息堆积阈值(默认值为32MB),或者达到某种程度(默认8MB)一段时间(默认为1分钟)后,服务器端会自动断开该客户端连接,避免导致内部内存耗尽。
  • 当连接断开之后,客户端需要使用subscribe或者psubscribe重新进行订阅,否则无法继续接收消息。
  • Redis的pubsub不是一种可靠的消息系统。当出现客户端连接退出,或者极端情况下服务端发生主备切换时,未消费的消息会被丢弃。

针对消息订阅发布功能,市面上很多大厂使用的是kafka、RabbitMQ、ActiveMQ, RocketMQ等这几种,redis的订阅发布功能相比而言,相对轻量,针对数据准确和安全性要求没有那么高可以直接使用。

Stream

Redis Stream 从概念上来说,就像是一个仅追加内容的消息链表,把所有加入的消息都一个一个串起来,每个消息都有一个唯一的ID和内容,这很简单,让它复杂的是从Kafka借鉴的另一种概念:消费者组(Consumer Group) (思路一致,实现不同)。
Stream消费者组
pubsub和stream对比

Redis基于内存存储,这意味着它会比基于磁盘的Kafka快上一些,也意味着使用Redis不能长时间存储大量数据。不过如果想以最小延迟实时处理消息的话,可以考虑Redis,但是如果消息很大并且应该重用数据的话,则应该首先考虑使用Kafka。

Redis主从模式

Redis主从复制

主从复制,是指将一台redis服务器的数据,复制到其他的redis服务器。前者称为主节点(master/leader),后者称为从节点(slave/follower)。数据的复制时单向的,智能由主节点到从节点。主节点以写为主,从节点以读为主。

默认情况下,每台redis服务器都是主节点。一个主节点可以有多个从节点,一个从节点只有一个主节点。

主从复制_1
主从复制_2

配置方法:

  • 通过命令临时配置:slaveof。服务器重启后变回主节点。
  • 在配置文件中配置。

主从复制主要作用:

  • 数据冗余:主从复制实现了数据的热备份,是持久化之外的一种数据冗余方式。
  • 故障恢复:当主节点出现问题时,可以由从节点提供服务,实现快速的故障恢复;实际上是一种服务的冗余。
  • 负载均衡:在主从复制的基础上,配合读写分离,可以由主节点提供写服务,由从节点提供读服务(即写Redis数据时应用连接主节点,读Redis数据时应用连接从节点),分担服务器负载;尤其是在写少读多的场景下,通过多个从节点分担读负载,可以大大提高Redis服务器的并发量。
  • 读写分离:可以用于实现读写分离,主库写、从库读,读写分离不仅可以提高服务器的负载能力,同时可根据需求的变化,改变从库的数量。
  • 高可用基石:除了上述作用以外,主从复制还是哨兵和集群能够实施的基础,因此说主从复制是Redis高可用的基础。

主从复制共有三种模式:全量复制、基于长连接的命令传播、增量复制。

  • 主从服务器第一次同步的时候,就是采用全量复制,此时主服务器会两个耗时的地方,分别是生成RDB文件和传输RDB文件。为了避免过多的从服务器和主服务器进行全量复制,可以把一部分从服务器升级为“经理角色”,让它也有自己的从服务器,通过这样可以分摊主服务器的压力。
  • 第一次同步完成后,主从服务器都会维护着一个长连接,主服务器在接收到写操作命令后,就会通过这个连接将写命令传播给从服务器,来保证主从服务器的数据一致性。
  • 当主从数据库之间的连接断开重连后,Redis2.6及之前的版本会重新进行复制初始化,即便slave仅有几条命令没有收到,master也必须要将数据库里的所有数据重新传送给slave。这使得主从数据库断开重连后的数据恢复过程效率低下,在网络环境不好的时候这一问题尤其明显。Redis2.8版的一个重要改进就是断线之后能够支持有条件的增量数据传输,当slave重新连上master后,master只需将断线期间执行的命令发送给slave,从而大大提高Redis复制的实用性。

不足:主从模式不具备自动容错和恢复功能,主节点故障,集群则无法进行工作,可用性比较低,从节点升主节点需要人工手动干预。

Redis哨兵模式(Sentinel)

Sentinel(哨兵)是用于监控Redis集群中Master状态的工具,是Redis高可用解决方案,哨兵可以监视一个或者多个redis master服务,以及这些master服务的所有从服务。 某个master服务宕机后,会把这个master下的某个从服务升级为master来替代已宕机的master继续工作。即使后来之前的master重启服务,也不会变回master了,而是作为slave从服务。
哨兵模式

这里的哨兵有两个作用:

  1. 通过发送命令,让Redis服务器返回监控其运行状态,包括主服务器和从服务器。
  2. 当哨兵监测到master宕机,会自动将slave切换成master,然后通过发布订阅模式通知其他的从服务器,修改配置文件,让它们切换主机。

然而一个哨兵进程对Redis服务器进行监控,可能会出现问题,为此,我们可以使用多个哨兵进行监控。各个哨兵之间还会进行监控,这样就形成了多哨兵模式。

用文字描述一下故障切换(failover)的过程。假设主服务器宕机,哨兵1先检测到这个结果,系统并不会马上进行failover过程,仅仅是哨兵1主观的认为主服务器不可用,这个现象成为主观下线。当后面的哨兵也检测到主服务器不可用,并且数量达到一定值时,那么哨兵之间就会进行一次投票,投票的结果由一个哨兵发起,进行failover操作。切换成功后,就会通过发布订阅模式,让各个哨兵把自己监控的从服务器实现切换主机,这个过程称为客观下线。这样对于客户端而言,一切都是透明的。

不足:

  • 是一种中心化的集群实现方案:始终只有一个Redis主机来接收和处理写请求,写操作受单机瓶颈影响;
  • 集群里所有节点保存的都是全量数据,浪费内存空间,没有真正实现分布式存储。数据量过大时,主从同步严重影响master的性能;
  • Redis主机宕机后,哨兵模式正在投票选举的情况之外,因为投票选举结束之前,谁也不知道主机和从机是谁,此时Redis也会开启保护机制,禁止写操作,直到选举出了新的Redis主机。

Redis集群模式(Cluster)

Redis Cluster是一种服务器Sharding技术,redis 3.0版本开始正式提供。

Sentinel基本已经实现了高可用,但是每台机器都存储相同内容,很浪费内存,所以Redis Cluster实现了分布式存储。每台机器节点上存储不同的内容。

Redis 集群引入了哈希槽的概念,有 16384 个哈希槽(编号 0~16383)。集群的每个节点负责一部分哈希槽,每个 Key 通过 CRC16 校验后对 16384 取余来决定放置哪个哈希槽,通过这个值,去找到对应的插槽所对应的节点,然后直接自动跳转到这个对应的节点上进行存取操作。

集群模式

Redis Cluster集群具有如下几个特点:

  • 集群完全去中心化,采用多主多从;所有的redis节点彼此互联(PING-PONG机制),内部使用二进制协议优化传输速度和带宽。
  • 客户端与 Redis 节点直连,不需要中间代理层。客户端不需要连接集群所有节点,连接集群中任何一个可用节点即可。
  • 每一个分区都是由一个Redis主机和多个从机组成,分片和分片之间是相互平行的。
  • 每一个master节点负责维护一部分槽,以及槽所映射的键值数据;集群中每个节点都有全量的槽信息,通过槽每个node都知道具体数据存储到哪个node上。

Redis Cluster主要是针对海量数据+高并发+高可用的场景,海量数据,如果数据量很大,那么就建议用redis cluster,数据量不是很大时,使用sentinel就够了。redis cluster的性能和高可用性均优于哨兵模式。

Redis数据淘汰

对于过期的内容,Redis有两种删除方式。一是被动删除,当读/写一个已经过期的key时,会触发惰性删除策略,直接删除掉这个过期key。二是主动删除,由于惰性删除策略无法保证冷数据被及时删掉,所以Redis会定期主动淘汰一批已过期的key。

Redis是内存数据库,全部数据都存放在内存中,但是内存的大小也是有限制的,不能无限使用,所以redis提供了相应的策略。

  • Redis2.0.0之前的版本:内存使用超过配置限制时,写失败,读正常。
  • Redis2.0.0版本开始提供了vm功能:当内存使用超过配置限制时,并且使能vm功能,能将剔除一些数据交换到磁盘文件中。正常读写,数据不丢失,但因为读写文件,性能低,受限磁盘空间大小
  • Redis2.2.0开始提供了5种数据淘汰策略,默认策略为volatile-lru。
  • Redis3.0.0开始默认策略为noeviction,不进行数据淘汰。
  • Redis4.0.0新增2种LFU淘汰策略。
# volatile-lru -> remove the key with an expire set using an LRU algorithm
# allkeys-lru -> remove any key accordingly to the LRU algorithm
# volatile-random -> remove a random key with an expire set
# allkeys-random -> remove a random key, any key
# volatile-ttl -> remove the key with the nearest expire time (minor TTL)
# noeviction -> don't expire at all, just return an error on write operations
# volatile-lfu -> Evict using approximated LFU among the keys with an expire set.
# allkeys-lfu -> Evict any key using approximated LFU

Redis缓存穿透、击穿和雪崩

缓存穿透

缓存穿透就是用户想要查询一个数据,发现redis内数据库没有,也就是缓存没有命中,于是向持久层数据库查询。发现也没有,于是本次查询失败。当用户很多的时候,缓存都没有命中,于是都去请求了持久层数据库。这会给持久层数据库造成很大的压力,这时候就相当于出现了缓存穿透。

布隆过滤器

布隆过滤器是一种数据结构,对所有可能查询的参数以hash形式存储,在控制层先进行校验,不符合则丢弃,从而避免了对底层存储系统的查询压力。布隆过滤器可以由Bitmaps实现,Redis4.0后官方提供了布隆过滤器的插件。
布隆过滤器方法示意

缓存空对象

当存储层不命中后,即使返回的空对象也将其缓存起来,同时会设置一个过期时间,之后再访问这个数据将会从缓存中获取,保护了后端数据源。
缓存空对象方法

但是这种方法会存在两个问题:

  • 如果空值能够被缓存起来,这就意味着缓存需要更多的空间存储更多的键,因为这当中可能会有很多的空值的键。
  • 即使对空值设置了过期时间,还是会存在缓存层和存储层的数据会有一段时间窗口的不一致,这对于需要保持一致性的业务会有影响。

缓存击穿

缓存击穿,是指一个key非常热点,在不停的扛着大并发,大并发集中对这一个点进行访问,当这个key在失效的瞬间,持续的大并发就穿破缓存,直接请求数据库,就像在一个屏障上凿开了一个洞。

当某个key在过期的瞬间,有大量的请求并发访问,这类数据一般是热点数据,由于缓存过期,会同时访问数据库来查询最新数据,并且回写缓存,会导使数据库瞬间压力过大。

解决方案:

  • 设置热点数据永不过期。
  • 加互斥锁。分布式锁:使用分布式锁,保证对于每个key同时只有一个线程去查询后端服务,其他线程没有获得分布式锁的权限,因此只需要等待即可。这种方式将高并发的压力转移到了分布式锁,因此对分布式锁的考验很大。

缓存雪崩

缓存雪崩,是指在某一个时间段,缓存集中过期失效,例如redis宕机、大量数据集中过期等。
缓存雪崩

解决方法:

  • redis高可用:这个思想的含义是,既然redis有可能挂掉,那多增设几台redis,这样一台挂掉之后其他的还可以继续工作,其实就是搭建的集群。
  • 限流降级:这个解决方案的思想是,在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个key只允许一个线程查询数据和写缓存,其他线程等待。
  • 数据预热:数据预热的含义就是在正式部署之前,先把可能的数据先预先访问一遍,这样部分可能大量访问的数据就会加载到缓存中。在即将发生大并发访问前手动触发加载缓存不同的key,设置不同的过期时间,让缓存失效的时间点尽量均匀。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值