redis学习-单机数据库相关

redis单机数据库部分有很多常见问题:

  • 键的过期时间
  • redis的过期键删除策略
  • redis的数据淘汰策略
  • redis的RDB快照持久化和AOF文件持久化
  • redis事件
  • redis和数据库双写一致性怎么保证
  • 如何应对缓存穿透和缓存雪崩
  • redis的并发竞争问题

redis服务器中的数据库

redis服务器将所有的数据库都保存在服务器状态redis.h/redisServer结构的db数组中。每个redisDb结构代表一个数据库。

默认情况下,dbnum为16,即redis服务器默认会创建16个数据库
redis数据库底层就是依照字典(哈希表)来实现的,包括对数据库的增删查改也是基于哈希表之上的。

![alt](https://imgconvert.csdnimg.cn/aHR0cDovL2h1YW5neWlibG9nLmNvbS9yZWRpc1NlcnZlci5wbmc)

每个客户端都拥有自己的数据库,默认Redis客户端的目标数据库为0号数据库,用户可以通过SELECT命令切换数据库。

键的过期时间

  • 设置键的生存时间可以通过EXPIRE或者PEXPIRE命令。
  • 设置键的过期时间可以通过EXPIREAT或者PEXPIREAT命令。

过期键的删除策略

过期键的删除主要有3种策略:

  • 定时删除(对内存友好,对CPU不友好)
    在设置键的过期时间的同时创建一个定时器,让定时器每次在到达键的过期时间是立即执行对键的删除操作。
  • 惰性删除(对CPU友好,对内存不友好)
    每次从键空间中获取键的时候,都检查取得的键是否过期,如果过期了,就删除该键;如果没有过期,就返回该键。
  • 定期删除(折中)
    每隔一段时间程序就对数据库进行一次检查,删除里面的过期键

redis实际是配合使用惰性删除和定期删除两种策略:为了服务器可以更合理的使用CPU时间同时尽量避免浪费内存空间

内存淘汰机制

redis可以设置内存最大使用量,当内存使用量超出时,会施行数据淘汰策略。
具体有6种数据淘汰策略:

策略描述
volatile-lru从已设置过期时间的数据集中挑选最近最少使用的数据淘汰
volatile-ttl从已设置过期时间的数据集中挑选将要过期的数据淘汰
volatile-random从已设置过期时间的数据集中任意选择数据淘汰
allkeys-lru从所有数据集中挑选最近最少使用的数据淘汰
allkeys-random从所有数据集中任意选择数据进行淘汰
noeviction禁止驱逐数据

一般场景下:

使用 Redis 缓存数据时,为了提高缓存命中率,需要保证缓存数据都是热点数据。可以将内存最大使用量设置为热点数据占用的内存量,然后启用 allkeys-lru 淘汰策略,将最近最少使用的数据淘汰。

redis持久化

redis为什么需要持久化?
这个问题比较好理解,我们知道redis的数据是基于内存的,一旦redis重启退出或者发生了什么故障,内存里的数据会丢失。数据丢失这可不是我们希望看到的结果。所以我们需要持久化技术。

redis提供两种持久化技术将数据存储到硬盘里:

  • RDB快照持久化:将某个时间点的数据库状态保存到一个RDB文件中
  • AOF文件追加:当redis服务器执行写命令的时候,将写命令保存到AOF文件中

RDB持久化

redis有两个命令可以用于生成RDB文件:

  • SAVE:SAVE命令会阻塞Redis服务器进程,直到RDB文件创建完毕为止。
  • BGSAVE:BGSAVE命令会派生出一个子进程,然后由子进程负责创建RDB文件,父进程继续处理命令请求

RDB文件的载入不需要特殊命令,而是服务器在启动时检测到RDB文件存在就自动载入RDB文件。(服务器在载入RDB文件时会处于阻塞状态,直到载入完成)

虽然AOF和RDB持久化可以同时使用,但是由于AOF文件的更新频率比RDB文件高,所以如果服务器开启了AOF持久化功能,则会优先载入AOF文件来持久化;如果AOF持久化功能处于关闭状态,服务器才会载入RDB文件来持久化。

如果系统发生故障,将会丢失最后一次创建快照之后的数据。

如果数据量很大,保存快照的时间会很长。

AOF持久化

![alt](https://imgconvert.csdnimg.cn/aHR0cDovL2h1YW5neWlibG9nLmNvbS9hb2YucG5n)

AOF持久化主要分为命令追加、文件写入、文件同步三个步骤:

  • 命令追加:命令写入aof_buf缓冲区,注意一下,redis不是直接把命令写到硬盘里去的,在redis和硬盘之间还有一个Linux OS 缓存区,要先把命令写入缓存区,然后再写入硬盘
  • 文件写入:调用flushAppendOnlyFile函数,考虑是否要将aof_buf缓冲区写入AOF文件中
  • 文件同步:考虑是否将内存缓冲区的数据真正写入到硬盘
AOF重写

为什么redis提供了AOF重写功能?
为了解决AOF文件体积膨胀的问题,因为AOF持久化是通过保存被执行的写命令来记录数据库状态的,所以随着服务器的运行,AOF文件中内容越来越多,文件体积会越来越大。一旦AOF文件体积过大,不仅会对宿主计算机造成影响,对数据还原所需时间也会较长。这里注意下,虽然redis里面的数据也是在不断增长,但是redis本身实现了相应的内存淘汰策略,比如LRU等,以免数据过于庞大,但是对于AOF文件,之前redis淘汰掉的数据也还是会写到AOF里的,所以需要一个重写的机制。

AOF重写就是指redis可以创建一个新的AOF文件来替代现有的AOF文件新旧两个AOF文件保存的数据库状态相同,但是由于新的AOF文件不包含浪费空间的冗余命令,所以新的AOF文件相对来说体积要小很多。

AOF重写由Redis自行触发(参数配置),也可以用BGREWRITEAOF命令手动触发重写操作,AOF重写不需要对现有的AOF文件进行任何的读取、分析。AOF重写是通过读取服务器当前数据库的数据来实现的。也就是说AOF重写是基于redis当前现有的数据。

AOF后台重写是不会阻塞主进程接收请求的,新的写命令请求可能会导致当前数据库和重写后的AOF文件的数据不一致!

为了解决数据不一致的问题,Redis服务器设置了一个AOF重写缓冲区,这个缓存区会在服务器创建出子进程之后使用。

所以AOF后台重写其实主要是为了解决AOF文件和数据库中的数据不一致的问题。

RDB和AOF的各自优缺点

  • RDB优点
  1. RDB会生成多个数据文件,每个文件代表某一时刻的完整的数据快照,适合做冷备份
  2. RDB对redis的读写服务影响较小,因为redis主进程会fork出一个子进程,让子进程进行磁盘IO操作进行持久化
  3. 相比于AOF,直接基于RDB数据文件来重启和恢复redis进程,速度更快
  • RDB缺点
  1. 相比于AOF,如果redis服务器出现故障,那么RDB数据损失的情况会更严重
  2. RDB每次在fork子进程执行数据文件生成的时候,如果数据文件特别大,可能会导致客户端提供服务出现延时,所以一般执行生成RDB文件的间隔时间不要太长。
  • AOF优点
  1. AOF文件以append-only模式写入命令,不会有磁盘寻址的开销,写入性能高
  2. 保证尽量少的数据丢失
  • AOF缺点
  1. AOF文件容易过大

使用持久化

一般需要结合RDB和AOF两种持久化机制来进行redis持久化,通过AOF来保证尽量少的数据丢失,通过RDB来支持不同程度(不同时间间隔)的冷备份。

RDB和AOF对过期键的处理策略

RDB对过期键的处理:

  • 执行SAVE或者BGSAVE命令创建出的RDB文件,程序会对数据库中的过期键检查,已过期的键不会保存在RDB文件中。
  • 载入RDB文件时,程序同样会对RDB文件中的键进行检查,过期的键会被忽略。

AOF对过期键的处理:

  • 如果数据库的键已过期,但还没被惰性/定期删除,AOF文件不会因为这个过期键产生任何影响,当过期的键被删除了以后,会追加一条DEL命令来显示记录该键被删除了
  • 重写AOF文件时,程序会对RDB文件中的键进行检查,过期的键会被忽略。

redis和数据库双写一致性问题

数据库和缓存双写,则必然会出现数据不一致问题,我们可以采取适当的策略,尽可能降低不一致发生的概率。
最经典的缓存+数据库读写的模式,就是 Cache Aside Pattern。

  • 读的时候,先读缓存,缓存没有的话,就读数据库,然后取出数据后放入缓存,同时返回响应。
  • 更新的时候,先更新数据库,然后再删除缓存。
    之所以是删除缓存,是因为在复杂点的缓存场景,缓存不单单是数据库中直接取出来的值
    如果缓存删除失败,可以提供一个补偿措施:利用消息队列

什么是缓存穿透和缓存雪崩

缓存穿透

缓存穿透指刻意请求缓存中不存在的数据,导致所有的请求都到了数据库,从而引起数据库连接异常

解决思路:

  • 考虑对这些请求进行过滤
  • 对这些不存在的数据缓存空数据

缓存雪崩

缓存雪崩指某一时间缓存大面积失效,这是又来了一大波请求,此时请求到达数据库层面,引发数据库连接异常

解决思路:

  • 如果是由于同一时期大面积的数据过期导致缓存雪崩,可以考虑观察用户行为,合理设置缓存的过期时间
  • 如果是由于服务器宕机导致缓存雪崩,可以考虑使用分布式缓存,确保某一节点的服务器宕机时,其余节点的缓存能够正常访问
  • 为了防止系统刚启动大量数据还未进行缓存导致缓存雪崩,可以考虑进行缓存预热处理。

redis怎么保证原子性的

redis能够保证原子性很显然,因为redis是单线程的。
对redis命令的原子性来说,一个操作不可以再分,要么执行,要么执行。而单个线程的任务就是一个一个执行的。

redis并发竞争问题

多客户端同时并发写一个 key,导致数据出错

思路:

  • 某个时刻,多个系统实例都去更新某个 key。可以基于 zookeeper 实现分布式锁。每个系统通过 zookeeper 获取分布式锁,确保同一时间,只能有一个系统实例在操作某个 key,别人都不允许读和写。
  • 要写入缓存的数据,都是从 mysql 里查出来的,都得写入 mysql 中,写入 mysql 中的时候必须保存一个时间戳,从 mysql 查出来的时候,时间戳也查出来。每次要写之前,先判断一下当前这个 value 的时间戳是否比缓存里的 value 的时间戳要新。如果是的话,那么可以写,否则,就不能用旧的数据覆盖新的数据。
  • 考虑消息队列,在并发量过大的情况下,可以通过消息中间件进行处理,把并行读写进行串行化。
    把Redis.set操作放在队列中使其串行化,必须的一个一个执行。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值