Redis面试题,双写一致、持久化

目录

双写一致性

简单方法:延时双删

问题1:先删除缓存,还是先修改数据库

问题2:为什么要删除两次缓存?

问题3:为什么要延迟删除?

保证强一致性的方法

可以保证短暂不一致的方法

MQ实现数据一致性的步骤:

介绍:Canal

面试回答1(读写锁,延时双删)

面试回答2(canal)

数据持久化

RDB

RDB的执行原理 

那主进程和子进程是如何同步数据呢?

脏数据问题(copy-on-write)

AOF

AOF的命令记录的频率(处理策略)

 RDB与AOF的比较

面试回答(数据持久化)


双写一致性

双写一致性:当修改了数据库的数据也要同时更新缓存的数据,缓存和数据库的数据要保持一致

读操作:缓存命中,直接返回;缓存未命中查询数据库,写入缓存,设定超时时间

写操作:延时双删

简单方法:延时双删

延时双删可以极大的控制了脏数据的风险,可以提高缓存的命中率。但是也有脏数据的风险,没法保证强一致性并且代码耦合度高

问题1:先删除缓存,还是先修改数据库

无论是先删除缓存,还是先删除数据库都无法保证数据的一致性,如上图。

但是在大多数情况下,最好先更新数据库,然后再删除缓存。

这是因为在更新数据库之前删除缓存可能会导致一些问题。例如,如果您在删除缓存之前从数据库中读取数据,那么您就会读到旧数据,这将导致您的应用程序中的数据不一致。

问题2:为什么要删除两次缓存?

如问题1的情况,在前面的步骤中,可能会存在脏数据,所以会进行第二次删除缓存,降低脏数据的出现

问题3:为什么要延迟删除?

一般来说,数据库是主从模式,他是读写分离的,我们需要延时一会,让主节点把数据同步到从节点,

但是延时的时间不好控制,仍然有可能出现脏数据,

所以延时双删极大的控制了脏数据的风险,但也只是一部分,也有脏数据的风险,

没法保证强一致性

保证强一致性的方法

既然使用缓存,那我们默认是读多写少的情况,如果只是采用上图方法,效率太低,我们可以使用读写锁来完成数据一致性

共享锁:读锁readLock,加锁之后,其他线程可以共享读操作     

排他锁:独占锁writeLock也叫,加锁之后,阻塞其他线程读写操作

注意:并不是所有的应用程序都适合使用读写锁解决数据一致性问题。读写锁本身有一定的开销,有时候会影响应用程序的性能。在一些并发读写比较少,而写操作相对频繁的场景中,使用读写锁的效果并不明显。

可以保证短暂不一致的方法

异步通知保证数据的最终一致性

消息队列(MQ)是一种通过异步通信实现解耦和可靠消息传递的技术。在分布式系统中,使用MQ可以帮助实现数据的最终一致性

MQ实现数据一致性的步骤:

1.发送消息。当有新数据需要写入系统时,将消息写入消息队列。

2.异步处理。接收消息的服务异步处理消息。这意味着消息不会立即被处理,但它们将被分配给服务,以便在以后的某个时间点进行处理。

3.事务处理。消息队列允许将消息与事务绑定在一起。如果在处理消息时出现错误,将撤消整个事务并将消息重新放回队列。

4.保证顺序。使用消息队列可以通过在消息发送时指定顺序键来确保消息以正确的顺序处理。

5.重复消费。使用消息队列可以为每条消息分配唯一的ID。在消息被消费后,可以将其标记为“已消费”,以避免重复消费。

这些步骤的结合,可以帮助实现分布式系统中数据的最终一致性。通过使用消息队列,可以确保数据最终会被处理,并允许在处理数据时出现错误时进行回滚。

无论是发送消息还是接收消息,都是有延迟的,但是可以保证最终的一致性

还有另一种方法,那就是可以通过canal进行异步通知

 这种方法是对业务是0侵入的,可以很完美的解耦

介绍:Canal

Canal 是一款基于 MySQL 数据库的增量日志解析和消费框架,可以通过将自己伪装成 MySQL 的从节点来监听 MySQL 数据库,从而实现实时获取 MySQL 数据库的增量更新。

在这种情况下,你可以使用 Canal 将自己伪装成 MySQL 的从节点,然后通过监听 MySQL 数据库的 binlog,获取到数据库的增量数据,并将数据更新到相应的缓存中。由于 Canal 监听 MySQL 数据库的 binlog 是一种低耗性能的方式,因此不会影响系统的性能。

通过使用 Canal,你可以在不更改原业务代码的情况下,实现在数据库更新时自动更新缓存,适用于很多需要对数据维护缓存数据的场景。不过,在部署 Canal 服务时,还需要考虑一些其他因素,如部署环境、配置参数等,需要谨慎操作。

需要注意的是,Canal 不仅支持 MySQL 数据库,还支持其他的 RDBMS,如 Oracle、SQLServer 等,可以根据实际情况选择相应的数据源进行监听。

操作流程

Canal 可以异步地监听 MySQL 数据库的 binlog,当 MySQL 数据库中的数据发生变化时,它会解析 binlog 并将变化数据推送给指定的消息中间件,客户端可以通过订阅消息的方式来获取到数据,从而实现实时更新缓存的功能。下面是具体的流程:

  1. Canal 作为 MySQL 的从节点伪装,通过监听 MySQL 数据库的 binlog 实现对 MySQL 数据库的监听。

  2. Canal 解析 binlog 中的记录,解析出更新的数据内容,这些数据包含了更新的表名、字段名、更新前的值以及更新后的值等信息。

  3. Canal 将更新的数据内容发送给指定的消息中间件,如 Kafka、RocketMQ 等。客户端可以通过订阅消息的方式获取这些数据。

  4. 客户端从消息中间件中获取到更新的数据内容,然后将其更新到相应的缓存中。由于更新缓存是异步的,因此客户端需要通过队列等方式来确保数据的处理顺序和完整性。

  5. 客户端将更新状态写入持久化存储中,以确保在重启应用程序时,可以从持久化存储中恢复更新状态。

  6. 当客户端更新缓存成功后,将响应信息返回给 Canal,表示更新已经完成。

  7. Canal 将更新完成的 Acknowledgement 记录在 ZooKeeper 中,以确保在 Canal 宕机后仍能够恢复更新状态,避免数据丢失。

总的来说,Canal 异步监听 MySQL 数据库的 binlog,通过消息中间件让客户端实时获取数据库的增量数据,从而更新缓存。这个过程需要确保数据的处理顺序和完整性,同时还需要将更新状态持久化,以确保在重启应用程序时可以恢复更新状态。所有操作都是异步完成,避免了对系统性能的影响。

面试回答1(读写锁,延时双删)

面试官:redis做为缓存,mysql的数据如何与redis进行同步呢?(双写一致 性)

候选人:就说我最近做的这个项目,里面有xxxx(根据自己的简历上写)的功能,需要让数据库与redis高度保持一致,因为要求时效性比较高, 我们当时采用的读写锁保证的强一致性。

我们采用的是redisson实现的读写锁,在读的时候添加共享锁,可以保证读读不互斥,读写互斥。当我们更新数据的时候,添加排他锁,它是读写,读读都互斥,这样就能保证在写数据的同时是不会让其他线程读数据的,避免了脏数据。这里面需要注意的是读方法和写方法上需要使用同一把锁才行。

面试官:那这个排他锁是如何保证读写、读读互斥的呢?

候选人:其实排他锁底层使用也是setnx,保证了同时只能有一个线程操作锁住的方法

面试官:你听说过延时双删吗?为什么不用它呢?

候选人:延迟双删,如果是写操作,我们先把缓存中的数据删除,然后更新数据库,最后再延时删除缓存中的数据,其中这个延时多久不太好确定,在 延时的过程中可能会出现脏数据,并不能保证强一致性,所以没有采用它。

面试回答2(canal)

面试官:redis做为缓存,mysql的数据如何与redis进行同步呢?(双写一致 性)

候选人:就说我最近做的这个项目,里面有xxxx(根据自己的简历上写)的功能,数据同步可以有一定的延时(符合大部分业务)

我们当时采用的阿里的canal组件实现数据同步:不需要更改业务代码,部署 一个canal服务。canal服务把自己伪装成mysql的一个从节点,当mysql数据 更新以后,canal会读取binlog数据,然后在通过canal的客户端获取到数据, 更新缓存即可。

数据持久化

RDB

RDB全称Redis Database Backup fileRedis数据备份文件),也被叫做Redis数据快照。简单来说就是把内存中的所有数据都记录到磁盘中。当Redis实例故障重启后,从磁盘读取快照文件,恢复数据

主动备份命令有两个

在Redis中,save 和 bgsave 命令都可以进行数据备份,但它们的执行方式不同。

save命令会阻塞Redis服务器的主进程,直到所有数据都被写入磁盘为止。这意味着,在执行save命令期间,Redis服务器无法响应任何其他命令。这可能会导致Redis服务器性能变差,因为它需要等待数据被写入磁盘,才能继续处理其他请求。

另一方面,bgsave命令会在后台执行快照操作,因此Redis服务器可以继续响应客户端请求。bgsave命令不会阻止主进程,因此在执行此命令期间,Redis服务器仍然可以处理其他命令。然而,bgsave命令需要更多的系统资源来执行,因为它需要创建一个子进程来进行快照操作。因此,在Redis服务器性能不足的情况下,对于bgsave命令的使用需要谨慎考虑。

总结起来,save命令会阻塞Redis服务器,但是使用的资源较少。而bgsave命令不会阻塞Redis服务器,但是使用的系统资源较多。因此,根据实际场景需要选择不同的备份方式。

当然也可以自动备份

redis.conf中配置RDB快照周期:内存快照虽然可以通过技术人员手动执行SAVE或BGSAVE命令来进行,但生产环境下多数情况都会设置其周期性执行条件。可以在redis.conf文件中找到,格式如下:

# 周期性执行条件的设置格式为
save <seconds> <changes>

# 默认的设置为:
# 如果900秒内有1条Key信息发生变化,则进行快照
save 900 1
# 如果300秒内有10条Key信息发生变化,则进行快照
save 300 10
# 如果60秒内有10000条Key信息发生变化,则进行快照
save 60 10000

# 以下设置方式为关闭RDB快照功能
save ""

RDB的执行原理 

        在Redis中,主进程要去实现对Redis数据的读写操作(内存),但是在Redis中,所有的进程都没法直接操作物理内存,并且操作系统给每一个进程都分配了一个虚拟内存,主进程只能操作虚拟内存,但是操作系统会维护一个虚拟内存和物理内存的映射关系表(页表),他记录了虚拟地址和物理地址的映射关系。

        当主进程去操作虚拟内存,而虚拟内存基于页表的映射关联到物理内存真正的存储数据的位置,这样,主线程就能实现对数据的读和写了。

那主进程和子进程是如何同步数据呢?

        当我们执行bgsave时,会创建一个子进程去执行RDB,在创建子进程时其实就是拷贝了主进程,并且把页表(映射关系)也拷贝给了子进程

        这样子进程就有了和主进程相同的映射关系了,当子进程操作自己的虚拟内存时,因为映射关系和主线程是一样的,最终一定能映射到相同的物理内存区域,这样就实现了两个进程之内内存空间的共享了,我们也无需拷贝内存中的数据了,直接实现内存共享。

脏数据问题(copy-on-write

        当子进程在写入RDB文件时,主进程也可以接受用户请求,去修改内存中的数据呢?,如果这个时候,主进程在修改数据,子进程同时在读写数据,就会冲突,产生脏数据。

所以,在fork底层,采用了一种叫copy-on-write的技术。

当主进程执行读操作时,访问共享内存。

当主进程执行写操作时,则会拷贝一份数据,执行写操作。

        在fork时,会把共享内存设置为read-only(只读)模式,如果主进程要去写数据,会将数据再次完整的拷贝一份,再完成写操作。

        一旦完成之后,主进程再去执行读操作时,也会读新的数据。页表的映射关系,也会映射到新的数据,从而避免了脏数据的问题

AOF

AOF全称为Append Only File(追加文件)。Redis处理的每一个写命令都会记录在AOF文件,可以看做是命令日志文件

 他会把命令原封不动的记录到当前的日志文件中,并且会记录所有的在Redis执行的写命令,

但是AOF默认是关闭的,我们需要通过配置开启

AOF的命令记录的频率(处理策略

可以通过redis.conf文件来配置: 

比较

        AOF因为是记录命令,AOF文件会比RDB文件大的多。而且AOF会记录对同一个key的多次写操作,但只有最后一次写操作才有意义。我们可以通过执行bgrewriteaof命令,可以让AOF文件执行重写功能,用最少的命令达到相同效果。

 Redis也会在触发阈值时自动去重写AOF文件。阈值也可以在redis.conf中配置:

 RDB与AOF的比较

RDBAOF各有自己的优缺点,如果对数据安全性要求较高,在实际开发中往往会结合两者来使用。

面试回答(数据持久化)

面试官:redis做为缓存,数据的持久化是怎么做的?

候选人:在Redis中提供了两种数据持久化的方式:1、RDB 2、AOF

面试官:这两种持久化方式有什么区别呢?

候选人:RDB是一个快照文件,它是把redis内存存储的数据写到磁盘上,当 redis实例宕机恢复数据的时候,方便从RDB的快照文件中恢复数据。 AOF的含义是追加文件,当redis操作写命令的时候,都会存储这个文件中, 当redis实例宕机恢复数据的时候,会从这个文件中再次执行一遍命令来恢复数据

面试官:这两种方式,哪种恢复的比较快呢?

候选人:RDB因为是二进制文件,在保存的时候体积也是比较小的,它恢复的比较快,但是它有可能会丢数据,我们通常在项目中也会使用AOF来恢复数据,虽然AOF恢复的速度慢一些,但是它丢数据的风险要小很多,在AOF文件中可以设置刷盘策略,我们当时设置的就是每秒批量写入一次命令

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值