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

  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. LFULeast 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,并设置永不过期。

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

点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部