Redis缓存不一致问题如何解决
第一章 Redis之缓存可能遇到的问题
前言
在单机、并发量小的情况下,我们可以直接让请求直接访问数据库,单机MySQL查询最大能够支持每秒1万左右的查询请求,3千左右的新增更改的请求。但是在高并发场面下,并发量远远大于这些请求,为了保证系统的安全和高可用,一般会使用缓存作为中间缓存层,避免请求直接访问数据库。
一、Redis是什么?
Redis是现在最受欢迎的非关系型数据库之一,是一个使用C语言编写的,开源的高性能的键值对数据库。
具有以下特点:
- 基于内存运行、性能高效;
- 支持分布式,理论上可以无限扩展;
- key-value存储系统;
- 开源的使用ANSI C语言编写、遵守BSD协议、支持网络、可基于内存可持久化的日志型、并且提供各种API;
相比于其他数据库,Redis具备的特点是:
- C/S通讯模型;
- 单进程单线程模型;
- 丰富的数据类型;
- 操作具有原子性;
- 持久多种持久化方式;
- 高并发读写(读110000次/s,写81000次/s);
- 支持LUA脚本(redis分布式锁就是借助这个来实现的);
二、使用Redis带来的缓存不一致问题
在并发不高的情况下,可以先修改数据库,再更新缓存,这都是没有什么问题的。但是一旦并发量大了,上面说的情况可能就不合适了。具体如何解决呢?以下有几种方案。
1.搭建高可用的分布式缓存
- 使用Redis Sentinel 哨兵模式,监听集群内部的节点运行情况,保证缓存层的服务可用。
- 使用Redis Cluster 集群模式,和哨兵模式相似在都是为了保证服务的高可用,不同点在于集群中的从节点不提供服务,只提供当做数据备份,一旦主节点挂了会升级了新的主节点。
- 先写缓存、再写数据库;
可能会出现缓存写成功,数据库写失败或者响应延迟,则下次读取缓存时,就出现了脏读。
这个是写缓存的方式,本身就是错误额,需要改为先写数据库,把就缓存置为失效;读取数据时如果缓存不存在了,则读取数据库再写缓存; - 先写数据库再写缓存;
可能会出现数据库写成功,缓存写失败,导致下次请求的时候读取不到缓存层的数据。解决办法就是如果缓存读取失败,但是数据库读取成功,把数据写回缓存。 - 先更新数据库,在更新缓存;
可能会出现更新数据库成功,redis宕机导致缓存更新失败。解决办法就是如果缓存读取失败,但是数据库读取成功,把数据写回缓存。 - 先删除缓存,再更新数据库;
可能会出现线程1把缓存删除,此时正在更新数据库,但是还没有写到数据库,但是线程2来查询数据,缓存中没有,则查询到数据库中旧的数据并且更新到了数据库,造成了数据不一致的问题。 - 延迟双删,先删除缓存,再删除数据库;
在更新完缓存之后,延迟几百毫秒之后再次删除缓存。虽然能够解决先删缓存再更新数据库的问题,但是无法确定需要具体延迟多久,有可能延迟的时间不够长还是会导致数据不一致问题,如果延迟过程,又浪费服务器资源; - 订阅binlog日志;
通过订阅数据库binlog日志也是也解决缓存不一致的一种好方案,这个实现逻辑相比前几种会更复杂一些,原理是服务去监听binlog日志,如果数据库发生了增删改,后端通过手写代码的方式去更新缓存,能够避免缓存中更新的为旧数据。
总结
以上就是我对如何解决缓存不一致问题的个人见解,如果有其他方案,欢迎评论区建言献策。