Redis作为缓存

为什么关系型数据库性能不高

  1. 数据库把数据存储在硬盘上,硬盘的IO速度并不快,尤其是随机访问。

  2. 如果查询不能命中索引,就需要进行表的遍历,这就会大大增加硬盘IO次数。

  3. 关系型数据库对于SQL的执行会做一系列的解析、校验、优化工作。

  4. 如果是一些复杂查询,比如联合查询,需要进行笛卡尔积操作,效率更是降低很多。

mysql等数据库,效率比较低,所以承担的并发量有限,一旦请求数量多了,数据库的压力就会很大,甚至容易宕机----服务器每次处理一个请求,一定都要消耗一些硬件资源(CPU,内存,硬盘,网络...),任意一种资源的消耗超出了机器能提供的上限,机器就很容易出现故障。

如何提高mysql能承担的并发量?(客观需求)

  1. 开源:引入更多的机器,构成数据库集群。

  2. 节流:引入缓存,把一些频繁读取的热点数据保存到缓存上。后续查询数据时,如果缓存中已经存在了,就不再访问mysql了。

怎么知道哪些数据应该存储在Redis中(怎么知道哪些数据是热点数据)?

定期生成

把访问的数据,以日志的形式记录下来

通常是写一套离线的流程(往往使用 shell,python 写脚本代码...),然后通过定时任务来触发。

  1. 完成统计热词的过程。

  2. 根据热词,找到搜索结果的数据。

  3. 把得到的缓存同步到缓存服务器上。

  4. 控制这些缓存服务器自动重启。

优点:上述过程实现起来比较简单,过程更可控(缓存中的数据比较固定),方便排查问题

缺点:实时性不够,如果出现一些突发性事件,有一些本来不是热词的内容成了热词,新的热词就可能会给后面的数据库带来较大的压力

实时生成

  • 如果在 Redis 中查到了,就直接返回

  • 如果 Redis 中不存在,就从数据库查,把查询的结果同时写入 Redis

但是不停地写入 Redis,就会使 Redis 的内存占用越来越多,逐渐达到内存上限(不一定是机器内存上限,Redis 中也可以配置最多使用多少内存)。这时如果继续插入数据,就会触发问题。

为了解决上述情况,Redis 引入了"内存淘汰策略"。重要!!!面试题!!!

  1. FIFO (First In First Out)先进先出,把缓存中存在时间最久的(也就是先来的数据)淘汰

  2. LRU(Least Recently Used)淘汰最久未使用的,记录每个 key 的最近访问时间,把最近访问时间最老的 key 淘汰

  3. LFU ( Least Frequently Used )淘汰访问次数最少的,记录每个 key 最近一段时间的访问次数,把访问次数最少的淘汰

  4. Random 随机淘汰,从所有的 key 中抽取幸运儿被随机淘汰

Redis 中也可以通过配置改变具体使用的淘汰策略:

  • volatile-lru:当内存不足以容纳新写入的数据时,从设置了过期时间的 key 中使用 LRU(最近最少使用)算法进行淘汰。

  • allkeys-lru:当内存不足以容纳新写入的数据时,从所有 key 中使用 LRU(最近最少使用)算法进行淘汰。

  • volatile-lfu:4.0版本新增,当内存不足以容纳新写入的数据时,在过期的 key 中使用 LFU 算法进行删除 key。

  • allkeys-lfu:4.0版本新增,当内存不足以容纳新写入的数据时,从所有 key 中使用 LFU 算法进行淘汰。

  • volatile-random:当内存不足以容纳新写入的数据时,从所有 key 中随机淘汰数据。

  • volatile-ttl:在设置了过期时间的 key 中,根据过期时间进行淘汰,越早过期的优先被淘汰。(相当于 FIFO,只不过是局限于过期的 key)

  • noeviction 默认策略:当内存不足以容纳新写入的数据时,新写入的操作会报错。

缓存预热(Cache preheating)

上述"定期生成"的情况中不涉及"预热",在"实时生成"中,由于客户端先查询 Redis,如果没有才会查询 mysql,再把查到的数据写入 Redis 中。但是第一次查询时,Redis 中是空的,就注定要在 mysql 中查找,随着后续 Redis 中数据越来越多,mysql 的压力才会慢慢减小。但是也无法改变"万事开头难"的事实。

缓存预热就是"定期生成"和"实时生成"的结合,先通过离线的方式,通过一些统计的途径,把热点数据找到一部分,导入到 Redis 中,就能帮 mysql 承担很大压力。随着时间推移,就会逐渐使用新的热点数据淘汰掉旧的数据。

缓存穿透(Cache penetration)

是什么

对于"实时生成",如果查询的 key 不在 Redis 中,下一次查询时就一定在了。但前提是这个 key 一定要在 mysql 中能够找到,才会被写入 Redis;但如果出现了很多 Redis 和 mysql 中都没有的"错误数据",就会导致每次都会查一次 mysql,一样会给 mysql 带来很大压力。这种情况称为 缓存穿透。

为什么

原因可能有以下几种:

  • 业务设计不合理,比如缺少必要的参数校验环节,导致非法的 key 也被查询。

  • 开发/运维误操作,不小心把部分数据从数据库上误删了。

  • 黑客恶意攻击。

怎么办

  • 针对要查询的参数进行严格的合法性校验。比如要查询的 key 是用户的手机号,就需要校验当前 key 是否满足一个合法的手机号的格式。

  • 针对数据库上也不存在的 key,也将其存储到 Redis 中,但是 value 设置为一个"",避免后续频繁访问数据库。

  • 使用布隆过滤器先判定 key 是否存在,再真正查询。

缓存雪崩(Cache avalanche)

是什么

短时间内大量的 key 在缓存上失效,导致数据库压力骤增,甚至直接宕机

本来 Redis 是 MySQL 的一个护盾,帮 MySQL 抵挡了很多外部的压力。一旦护盾突然失效,MySQL 自身承担的压力骤增,就可能直接崩溃。

为什么

大规模的 key 失效,可能性主要有两种:

  • Redis 挂了。

  • Redis 上的大量的 key 同时过期

大量的 key 同时过期,可能是短时间内在 Redis 上缓存了大量的 key,并且设定了相同的过期时间。

因为有些数据可能早些时候是热点数据,但后续很久不用,也就自然不再是热点了,所以设定一个过期时间,过期时间结束前没有进行访问,就可以进行热点数据的更新了。

怎么办

  • 部署高可用的 Redis 集群,并且完善监控报警体系。

  • 不给 key 设置过期时间 或者 设置过期时间的时候添加随机事件因子,保证不让太多 key 在同一时间过期。

缓存击穿(Cache breakdown)

相当于缓存雪崩的特殊情况。针对热点 key,突然过期了,导致大量的请求直接访问到数据库上,甚至引起数据库宕机。

解决方法:

  • 基于统计的方式发现热点 key,并设置永不过期。

  • 进行必要的服务降级。例如访问数据库的时候使用分布式锁,限制同时请求数据库的并发数。

相关推荐
这个DBA有点耶4 天前
MVCC深入:Read View、版本链与快照读——InnoDB并发控制的内核
数据库·mysql·架构
DBA_G4 天前
从地面到云霄:GBase数据库在民航三大场景的落地实践
数据库
自由能燃气设备4 天前
商用全预混低氮冷凝锅炉免费方案vs付费方案对比+选型避坑指南
大数据·数据库·人工智能
科创致远4 天前
科创致远 ESOP 系统核心效能与实战价值展示
大数据·数据库·人工智能·精益工程
IT大白鼠4 天前
Redis 系列 · 第 01 篇——认知入门:Redis 是什么
redis·nosql
彧azz4 天前
Linux 环境下 Redis 学习总结:数据类型、持久化、锁、事务、主从与缓存问题
linux·redis·笔记·学习·面试
2601_962218614 天前
万象生鲜系统称重自动多退少补算法解决生鲜非标品痛点
大数据·数据库·人工智能·python·算法
全栈弄潮儿²⁰²⁴4 天前
AI Agent 开发实战(30):限流、缓存与成本控制
人工智能·gpt·缓存·agent·限流·agi·成本控制
张洛闻Eren4 天前
k8s云原生【第十课】:水平 Pod 自动扩缩容
运维·数据库·云原生·kubernetes·github
于平安4 天前
MySQL-触发器
数据库·mysql