redis理论八股的学习记录

介绍一下Redis

Redis是开源的内存数据存储系统,支持多种数据结构,可以用作缓存、实时队列和消息中间件,在性能、可扩展性和灵活性方面表现出色。

redis为什么这么快?

  1. 内存:redis的所有数据都在内存中,因此不需要访问磁盘,极大的降低了访问延迟;内存操作(读写)性能高,支持每秒百万级操作。

  2. 单线程:redis以单线程模式运行,避免了多线程上下文切换的开销问题和多线程竞争问题,提高了CPU利用效率。

  3. 高效的数据结构:redis利用了几个高效的底层数据结构来提高数据操作效率。

  4. 非阻塞I/O多路复用机制:Redis使用非阻塞I/O,这种机制使得redis可以处理多个连接而不阻塞其他操作,从而能快速处理请求,实现高并发和高吞吐量。

redis如何实现持久化?

为什么要持久化?redis是内存数据库,它的数据都存储在内存中,如果不把数据保存到磁盘里,那么服务器进程一旦退出,服务器的数据就丢失了。

通过两种方式:

  1. RDB(Persistence DB):RDB类似快照,它只能持久化某一时刻的服务器数据。RDB持久化后,文件将被保存至同文件夹下的dump.rdb文件中,且以压缩的二进制文件表示,服务器重启都会加载该文件以加载数据。

    1. 持久化部分

      Redis有俩个命令进行RDB持久化:

      • SAVE:将会阻塞redis进程,此时服务器不能处理任何命令请求,直到RDB文件创建完毕。

      • BGSAVE:派生子进程(创建子进程执行fork函数时服务器也将会阻塞,不过用时会很短),由子进程创建RDB,服务器进程继续

      2. 载入数据: 服务器启动时启动的,检测到RDB文件就会自动载入RDB中的数据。

  2. AOF(Append Only File):AOF可以支持实时的数据持久化。AOF持久化功能通过保存redis服务器所执行的写命令来记录数据库状态,默认存储在appendonly.aof中,AOF文件中存储的都是redis相关命令。

    1. 持久化部分: AOF持久化功能的实现可以分为命令追加、文件写入和同步。

      1. 命令追加:当服务器完成一个写命令之后,并不会直接将这些命令写入AOF文件,而是以Redis协议格式将被执行的写命令追加到服务器缓冲区的末尾。

      2. 文件写入与同步:根据设置的数据同步策略(always、everysec、no),当满足同步条件或缓冲区满时,redis会将AOF缓冲区中的数据一次性写入磁盘的AOF文件。

      写入类型的区别:

      • always:每次执行写命令都将缓冲区的内容写入AOF文件。效率最慢,也最安全,故障停机只会丢失一个事件循环中的数据命令。

      • everysec:如果上次同步AOF文件时间距离现在已经超过了1秒,就进行文件同步。该同步由一个线程单独负责效率够快,且故障停机也只会丢失一秒钟的命令数据。

      • no:由操作系统决定何时进行AOF文件的同步。一般来说,os可能会等到缓冲区的空间被填满、或者超过了指定时限之后,才真正地将缓冲区的数据写入到磁盘里面。fsync和fdatasync两个同步函数,可以强制让os立即将缓冲区中的数据写入到磁盘里面。总体写入AOF文件的速度最快,但是单次同步时长是三种模式中最长的。

      2. 载入数据: AOF文件只包含了重建数据库状态所需的所有命令,因此服务器只需要重新执行一遍AOF文件中保存的写命令即可恢复服务器状态。大致步骤如下:

      • 创建一个不带网络连接的伪客户端,用来模拟客户端发送写命令。

      • 从AOF文件中执行并分析出一条写命令

      • 使用伪客户端执行被读出的写命令。

      • 重复读取和写入,直到AOF文件被处理完毕。

      3. AOF持久化会出现阻塞: Redis每次执行写命令都将缓冲区的内容写入AOF文件,此时线程可能会因为持久化操作(磁盘IO)而阻塞。

说说内存淘汰机制?有哪些策略?

当redis内存超过maxmemory的限制时,就会根据maxmemory-policy选择的内存淘汰机制(eviction policy)尝试删除键。

maxmemory-policy默认值为noeviction

策略内容如下:

  • noeviction:在达到内存限制并且客户端尝试执行可能导致使用更多内存的命令时返回错误(一般出现在写入命令时,但删除和一些其他操作时也有可能引发异常)。

  • allkeys-lru:尝试在键空间中先删除最近最少使用的(LRU)键

  • volatile-lru:在设置了过期时间的键空间中,尝试在键空间中删除最近最少使用(LRU)的键

  • allkeys-random:随机删除键

  • volatile-random:在设置了过期时间的键空间中随机删除键

  • volatile-ttl:在设置了过期时间的键空间中,尝试首先逐出具有较短生存时间(TTL)的密钥(即更早过期的键)

如何保证数据库和缓存双写一致性#

你只要用缓存,就可能会涉及到缓存与数据库双存储双写,你只要是双写,就一定会有数据一致性的问题,那么你如何解决一致性问题?

一般来说,就是如果你的系统不是严格要求缓存+数据库必须一致性的话,缓存可以稍微的跟数据库偶尔有不一致的情况,最好不要做这个方案,读请求和写请求串行化,串到一个内存队列里去,这样就可以保证一定不会出现不一致的情况。

串行化之后,就会导致系统的吞吐量会大幅度的降低,用比正常情况下多几倍的机器去支撑线上的一个请求。

还有一种方式就是可能会暂时产生不一致的情况,但是发生的几率特别小,就是先更新数据库,然后再删除缓存。

问题场景 描述 解决
先写缓存,再写数据库,缓存写成功,数据库写失败 缓存写成功,但写数据库失败或者响应延迟,则下次读取(并发读)缓存时,就出现脏读 这个写缓存的方式,本身就是错误的,需要改为先写数据库,把旧缓存置为失效;读取数据的时候,如果缓存不存在,则读取数据库再写缓存
先写数据库,再写缓存,数据库写成功,缓存写失败 写数据库成功,但写缓存失败,则下次读取(并发读)缓存时,则读不到数据 缓存使用时,假如读缓存失败,先读数据库,再回写缓存的方式实现
需要缓存异步刷新 指数据库操作和写缓存不在一个操作步骤中,比如在分布式场景下,无法做到同时写缓存或需要异步刷新(补救措施)时候 确定哪些数据适合此类场景,根据经验值确定合理的数据不一致时间,用户数据刷新的时间间隔

缓存击穿

是什么

缓存击穿则是指一个热点的缓存数据突然失效(比如过期),导致大量并发请求直接落在数据库上,可能会瞬间给数据库带来巨大压力。

如何解决
  • 对热点数据设置永不过期,或者使用逻辑过期策略,即数据有过期时间,但即使过期也不立即删除,而是异步更新缓存。

  • 使用双重检查锁(同步锁)保证不会有大量线程同时去数据库加载数据。

缓存雪崩

是什么

当某一个时刻出现大规模的缓存失效的情况,或者缓存服务器断电关机,那么就会导致大量的请求直接打在数据库上面,导致数据库压力巨大宕机。

如何解决
  • 在原有的失效时间上加上一个随机值,比如1-5分钟随机。这样就避免了因为采用相同的过期时间导致的缓存雪崩。

  • 设置好哨兵模式,当缓存服务器断电的时候,另一个服务器立刻顶上。

缓存穿透

是什么

假如发送的请求传进来的key是不存在Redis中的,那么就查不到缓存,查不到缓存就会去数据库查询,数据库也没有该数据,就造成了穿透。

如何解决
  • 参数校验

  • 把无效的Key存进Redis中(记得设置过期时间)。

  • 使用布隆过滤器

redis 的 zset底层实现是什么?

Zset 类型的底层数据结构是由压缩列表或跳表实现的:

  • 如果有序集合的元素个数小于 128 个,并且每个元素的值小于 64 字节时,Redis 会使用压缩列表作为 Zset 类型的底层数据结构;

  • 如果有序集合的元素不满足上面的条件,Redis 会使用跳表作为 Zset 类型的底层数据结构;

在 Redis 7.0 中,压缩列表数据结构已经废弃了,交由 listpack 数据结构来实现了。

为啥要用MQ延迟双删保证mysql和redis一致性?

在正常的数据读取流程中,如果先查询 Redis 缓存,没有命中再去查询 MySQL 数据库,然后将查询结果写入 Redis。但如果有大量针对不存在数据的查询请求,这些请求会直接穿透 Redis 去查询 MySQL,给 MySQL 带来巨大压力。

采用 MQ 延迟双删策略,在更新 MySQL 数据时,先删除 Redis 缓存,然后通过 MQ 发送一条延迟消息,在延迟一段时间后再次删除 Redis 缓存,确保再次删除缓存后,后续的读操作能从数据库中获取到最新数据并正确更新 Redis 缓存,而且这样可以确保在数据更新期间,即使有缓存穿透的请求,也不会因为旧数据存在于 Redis 中而导致数据不一致,同时也能减轻 MySQL 的压力。

你可能感兴趣的:(好记性不如烂笔头,redis,学习,数据库)