惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

P
Palo Alto Networks Blog
Recent Commits to openclaw:main
Recent Commits to openclaw:main
C
CERT Recently Published Vulnerability Notes
C
Cybersecurity and Infrastructure Security Agency CISA
S
Schneier on Security
S
Securelist
酷 壳 – CoolShell
酷 壳 – CoolShell
C
CXSECURITY Database RSS Feed - CXSecurity.com
Cyberwarzone
Cyberwarzone
Apple Machine Learning Research
Apple Machine Learning Research
S
SegmentFault 最新的问题
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
GbyAI
GbyAI
Security Latest
Security Latest
Last Week in AI
Last Week in AI
Microsoft Security Blog
Microsoft Security Blog
云风的 BLOG
云风的 BLOG
Recorded Future
Recorded Future
Webroot Blog
Webroot Blog
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
TaoSecurity Blog
TaoSecurity Blog
C
Cisco Blogs
博客园 - 【当耐特】
Blog — PlanetScale
Blog — PlanetScale
Hugging Face - Blog
Hugging Face - Blog
B
Blog
Hacker News - Newest:
Hacker News - Newest: "LLM"
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
Attack and Defense Labs
Attack and Defense Labs
The Last Watchdog
The Last Watchdog
U
Unit 42
阮一峰的网络日志
阮一峰的网络日志
Project Zero
Project Zero
WordPress大学
WordPress大学
L
LINUX DO - 最新话题
F
Fortinet All Blogs
L
LINUX DO - 热门话题
PCI Perspectives
PCI Perspectives
Simon Willison's Weblog
Simon Willison's Weblog
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
MongoDB | Blog
MongoDB | Blog
Latest news
Latest news
P
Proofpoint News Feed
T
Threat Research - Cisco Blogs
The Hacker News
The Hacker News
爱范儿
爱范儿
O
OpenAI News
J
Java Code Geeks
T
The Exploit Database - CXSecurity.com
H
Hackread – Cybersecurity News, Data Breaches, AI and More

郑文峰的博客

使用dify对接飞书多维表格 使用n8n对接飞书多维表格 服务启动时出现 OOM 一次服务升级时pg表DDL执行超时失败 Go语言高效IO缓冲技术详解 Go语言延迟初始化(Lazy Initialization)最佳实践 Go语言字符串拼接性能对比与优化指南 Go语言结构体内存对齐完全指南 Go语言空结构体:零内存消耗的高效编程 Go语言堆栈分配与逃逸分析深度解析 Go语言原子操作完全指南 Go语言内存预分配完全指南 Go语言不可变数据共享:无锁并发编程实践 Go语言零拷贝技术完全指南 Go语言遍历性能深度解析:从原理到优化实践 Go语言Interface Boxing原理与性能优化指南 Go协程池深度解析:原理、实现与最佳实践 使用etcd分布式锁导致的协程泄露与死锁问题 基于pre-commit的Python代码规范落地实践 初识 MCP Server pulsar阻塞导致logstash无法接入日志 django-prometheus使用及源码分析 kube-proxy源码分析 kubernetes service如何通过iptables转发 tcp缓存引起的日志丢失 django-apschedule定时任务异常停止 理解calico容器网络通信方案原理 理解flannel的三种容器网络方案原理 理解Linux IPIP隧道 理解VXLAN网络 理解Linux TunTap设备 快速了解iptables kafka中listener和advertised.listeners的作用 django rest_framework 分页 django后端服务、logstash和flink接入VictoriaMetrics指标监控 python中import原理 docker容器单机网络 手动实现docker容器bridge网络模型 mysql之MVCC原理 mysql之日志 使用java开发logstash的filter插件 使用python实现单例模式的三种方式 redis之分片集群 redis之哨兵机制 redis之主从库同步 redis之持久化 redis之五种基本数据类型 go中如何处理error pod中将代码与运行环境分离 ddt源码分析 python装饰器的使用方法 读书笔记:如何阅读一本书 使用ddt实现unittest的参数化测试 使用kubeadm安装k8s 优化gin表单的错误提示信息 gin中validator模块的源码分析 go简单使用grpc python简单使用grpc k8s之PV、PVC和StorageClass k8s之StatefulSet k8s之DaemonSet k8s之Job和CronJob k8s之ConfigMap和Secret k8s之Service k8s之Pod k8s之Deployment 容器的本质 docker容器 python迭代器与生成器 python元编程 python垃圾回收机制 python上下文管理器 django rest_framework使用jwt django rest_framework异常处理 django rest_framework 自定义文档 django压缩文件下载 django rest_framework使用pytest单元测试 django restframework choice 自定义输出数据 django Filtering 使用 django viewset 和 Router 配合使用时报的错 django model的序列化 django中使用AbStractUser django.core.exceptions.ImproperlyConfigured Application labels aren't unique, duplicates users django 中 media配置 django 外键引用自身和on_delete参数 django 警告 while time zone support is active Flask使用flask_socketio实现websocket flask结合mongo tornado 文件上传 tornado 使用jwt完成用户异步认证 tornado 用户密码 bcrypt加密 tornado 结合wtforms使用表单操作 tornado finish和write区别 tornado 使用peewee-async 完成异步orm数据库操作 pyspark streaming简介 和 消费 kafka示例 使用hue创建ozzie的pyspark action workflow count的性能优化 django rest_framework Authentication django celery 结合使用 网站
redis之缓存
zhengwenfeng · 2022-12-01 · via 郑文峰的博客

# 1. 前言

reids 是基于内存的数据库,它的特性之一就快,缓存是其最主要的应用场景,本文主要介绍 redis 的缓存特性,以及该如何正确的使用它。

# 2. 缓存类型

# 2.1 只读缓存

当写入数据时,直接操作后端数据库,进行增删改。删和改操作,如果 redis 已经缓存了对应的数据,则需要进行删除。当应用读取数据时,发生缓存缺失,则会从后端数据库读取到 redis 中使用。

好处是所有的数据都在后端数据库中,而后端数据提供可靠性保障,不会有丢失数据的风险。

# 2.2 读写缓存

最新的数据在 redis 中。在写入数据时,优先写入到 redis,并且因为缓存的高性能访问,可以快速返回给业务应用。

但是由于 redis 是内存数据库存在数据丢失的风险,所以还是需要写入到后端数据库中保证可靠性。有两种写入策略:

  • 同步直写:在写 redis 时,同步写入到后端数据库,完成后再返回给业务。会增加响应延迟。
  • 异步直写:等待增改的数据要被从缓存淘汰时。再写回后端数据库。

# 2. 缓存和数据库的数据一致性

# 2.1 哪些情况会导致数据不一致 ?

  • 我们假设应用先删除缓存,再更新数据库,如果缓存删除成功,但是数据库更新失败,那么,应用再访问数据时,缓存中没有数据,就会发生缓存缺失。然后,应用再访问数据库,但是数据库中的值为旧值,应用就访问到旧值了。

  • 如果应用先完成了数据库的更新,但是,在删除缓存时失败了,那么,数据库中的值是新值,而缓存中的是旧值,这肯定是不一致的。这个时候,如果有其他的并发请求来访问数据,按照正常的缓存访问流程,就会先在缓存中查询,但此时,就会读到旧值了。

# 2.3 队列+重试机制

可以把要删除的缓存值或者是要更新的数据库值暂存到消息队列中(例如使用 Kafka 消息队列)。当应用没有能够成功地删除缓存值或者是更新数据库值时,可以从消息队列中重新读取这些值,然后再次进行删除或更新。

如果能够成功地删除或更新,我们就要把这些值从消息队列中去除,以免重复操作

但是在并发情况下,无论是先删数据库还是先删缓存操作失败的情况下,还是会有读取到不一致数据的情况。

  • 情况一,先删除缓存,再更新数据库。

在更新数据库失败的情况下,另一个线程进来读取数据,发现缓存缺失,查询数据库的数据,并更新到缓存中,最终缓存中存储的是旧的数据。

延迟双删

在线程 A 更新完数据库的值以后,再让它 sleep 一会儿,再删除缓存。目的是为了让线程 B 可以将数据库的值写入到缓存中,然后再删除它。 伪代码如下:

redis.delKey(X)
db.update(X)
Thread.sleep(N)
redis.delKey(X)

1
2
3
4

网上有使用这种方案来解决,个人是不推荐这种方案,在高并发的情况下,这个 sleep 时间不好确定,并不知道其他线程什么时候执行和结束。

  • 情况二,先更新数据库值,再删除缓存值。

在删除缓存值失败的情况下,并发时,会有多个线程拿到旧的数据情况。

这两种并发场景,个人感觉唯一的办法就是使用同步来完成这两个操作,可以使用分布式锁或者其他。

# 3. 淘汰策略

缓存的容量是有限的,那么当缓存满了,可以是用淘汰策略来将部分数据淘汰。

缓存设置成多大比较合适? 按照二八原理,一般 20%数据会占用 80%的访问,所以建议将缓存的容量设置为总数据量的 15%或者 30%会比较合理。

淘汰策略有哪些?

  • 不淘汰
    • noeviction,如果缓存已满再有写请求,则返回错误
  • 对设置过期时间的数据进行淘汰
    • volatile-ttl 在筛选时,会针对设置了过期时间的键值对,根据过期时间的先后进行删除,越早过期的越先被删除。
    • volatile-random 就像它的名称一样,在设置了过期时间的键值对中,进行随机删除。
    • volatile-lru 会使用 LRU 算法筛选设置了过期时间的键值对。
    • volatile-lfu 会使用 LFU 算法选择设置了过期时间的键值对。
  • 对所有数据进行淘汰
    • allkeys-random 策略,从所有键值对中随机选择并删除数据;
    • allkeys-lru 策略,使用 LRU 算法在所有数据中进行筛选。
    • allkeys-lfu 策略,使用 LFU 算法在所有数据中进行筛选。

# 4. 缓存雪崩

缓存雪崩是指大量的应用请求无法在 Redis 缓存中进行处理,紧接着,应用将大量请求发送到数据库层,导致数据库层的压力激增。

产生的原因

  • 缓存中有大量数据同时过期,导致大量请求无法得到处理
  • redis服务宕机

发生后有损解决办法

  1. 是在业务系统中实现服务熔断或请求限流机制。

  1. 服务降级。非核心数据,直接返回预定义信息,错误或空值。核心业务仍然查询缓存。减少数据库压力

提前预防

上面的都是发生后有损的解决措施,所以最好的办法是提前预防,让它不要发生。

  1. 避免大量数据设置相同点额过期时间,如果有,可以加个随机数。
  2. 搭建高可用集群,主节点故障宕机,从节点可以切换成主节点,继续提供缓存服务。

# 5. 缓存击穿

缓存击穿是指,针对某个访问非常频繁的热点数据的请求,无法在缓存中进行处理,紧接着,访问该数据的大量请求,一下子都发送到了后端数据库,导致了数据库压力激增,会影响数据库处理其他请求。缓存击穿的情况,经常发生在热点数据过期失效时

产生的原因

  • 热点数据发生过期被淘汰导致访问后端数据库

发生后有损的解决办法

  • 接口限流与熔断,降级。

提前预防

  • 设置热点数据永远不过期。

# 6. 缓存穿透

缓存穿透是指要访问的数据既不在 Redis 缓存中,也不在数据库中,导致请求在访问缓存时,发生缓存缺失,再去访问数据库时,发现数据库中也没有要访问的数据。

产生的原因

  • 业务层误操作:缓存中的数据和数据库中的数据被误删除了,所以缓存和数据库中都没有数据;
  • 恶意攻击:专门访问数据库中没有的数据。

应对方案

  • 在请求入口做合法性校验,把恶意请求过滤掉。
  • 布隆过滤器,快速校验数据是否存在,避免从数据库中查询数据是否存在,减轻数据库压力。
  • 一旦发生数据不存在的情况,可以缓存一个缺省值,下次还使用该 ID 访问时,可以返回缺省值。

# 7. 参考文章

  • 本文主要是学习《极客时间-redis 核心技术与实战》专栏总结而来