redis 缓存设计和性能优化

China☆狼群 提交于 2020-01-22 17:31:30

请求缓存:

https://blog.csdn.net/xiaowangku/article/details/90409224

 

缓存的受益与成本

通过缓存加速读写速度:CPU L1/L2/L3 Cache、Linux page Cache加速硬盘读写、浏览器缓存、Ehcache缓存数据库结果。

  • 降低后端负载

后端服务器通过前端缓存降低负载:业务端使用Redis降低后端MySQL负载等。

成本

  • 数据不一致:缓存层和数据层有时间窗口不一致,和更新策略有关。
  • 代码维护成本:多了一层缓存逻辑
  •  

使用场景

对高消耗的SQL:join结果集/分组统计结果缓存

加速请求响应:利用Redis、Memcache优化IO响应时间

大量写合并为批量写:如计数器先Redis累加再批量写DB

缓存的更新策略:

LRU方法,LFU方法FIFO方法: 例如maxmemory-policy。

超时提出: 例如:例如 expire

主动更新:开发控制生命周期:

 

策略:                            一致性         维护成本

LRU/LIRS算法剔除        最差            低

超时剔除                       较差           低

主动更新                      强              高

 

缓存在细粒度的控制:

1、从MySQL获取用户信息:

select * from user where id={id}

 

2、设置用户信息缓存:

set user:{id} `select * from user where id={id}`

 

3、缓存粒度:

全部属性:set user:{id} `select * from user where id={id}`

set user:{id} `select importColumn1...importColumnK from user where id={id}`

 

 

三个角度

  • 通用性:全量属性更好
  • 占用空间:部分属性更好
  • 代码维护:表面上全量属性更好
  •  

缓存穿透问题—大量请求不命中

正常情况下,当访问到cache,它会把结果返回给request,如果cache没有命中的话,它就会把流量往下引到存储层,如果存储层正常的话,存储层拿到结果,回写到cache,返回给request,当下次request再次访问的时候,就可以直接从cache中获取,那么就不需要访问到存储层了。
 

但是如果存储层的数据根本就不存在,那会有什么问题?

即它会返回给request为空的结果,当下次再来request请求,再访问cache层,cache层还是没有,还要再访问存储层,所有的流量都被导入到存储层,这就是缓存穿透的一个基本过程,实际上这就已经失去了缓存的意义

 

产生的原因

  • 业务代码自身问题
  • 恶意攻击、爬虫等

如何发现

  • 业务的响应时间
  • 业务本身问题
  • 相关指标:总调度数、缓存层命中数、存储层命中数

 

解决方法1-缓存空对象

即如果存储层返回的结果为空,那么就把它当作一个结果,存储到cache中。

两个问题

1、需要存储更多的键(更多无效的键)。

2、缓存层和存储层数据“短期”不一致。

 

缓存雪崩优化

由于cache服务承载大量请求,当cache服务异常/脱机,流量直接压向后端组件(例如DB),造成级联故障。

缓存雪崩-优化方案

1、保证缓存高可用:个别节点、个别机器、甚至是机房。

2、依赖隔离组件为后端限流。

3、提前演练:例如压力测试。

 

 

无底洞问题优化

问题描述:

2010年,Facebook有了3000个Memcache节点

发现问题:“加”机器性能没能提升,反而下降

无底洞问题关键点

 

更多的机器!=更高的性能

批量接口需求(mget,mset等)

数据增长与水平扩展需求

 

参考博文:https://blog.csdn.net/xiaowangku/article/details/9040922

 

===========================================================

redis 缓存设计和优化

缓存的优缺点及使用场景

好处

1.加速读写:通过缓存加速读写,如 CPU L1/L2/L3 的缓存、Linux Page Cache 的读写、游览器缓存、Ehchache 缓存数据库结果。

2.降低后端负载:后端服务器通过前端缓存来降低负载,业务端使用 Redis 来降低后端 MySQL 等数据库的负载。

缺点

  1. 数据不一致:这是因为缓存层和数据层有时间窗口是不一致的,这和更新策略有关的。
  2. 代码维护成本:这里多了一层缓存逻辑,就会增加成本。
  3. 运维费用的成本:如 Redis Cluster,甚至是现在最流行的各种云,都是成本

 

使用场景

1.降低后端负载:这是对高消耗的 SQL,join 结果集和分组统计结果缓存

2.加速请求响应:这是利用 Redis 或者 Memcache 优化 IO 响应时间。

3.大量写合并为批量写:比如一些计数器先 Redis 累加以后再批量写入数据库。

 

缓存的更新策略

三种策略

1.LRU、LFU、FIFO 算法策略。例如 maxmemory-policy,这是最大内存的策略,当 maxmemory 最大时,会优先删除过期数据。我们在控制最大内存,让它帮我们去删除数。

2.过期时间剔除,例如 expire。设置过期时间可以保证其性能,如果用户更新了重要信息,应该怎么办。所以这个时候就不适用了。

 

3.主动更新,例如开发控制生命周期。

 

这三个策略中,一致性最好的就是主动更新。能够根据代码实时的更新数据,但是维护成本也是最高的;算法剔除和超时剔除一致性都做的不够好,但是维护成本却非常低。

 

  1. 低一致性:最大内存和淘汰策略,数据库有些数据是不需要马上更新的,这个时候就可以用低一致性来操作。
  2. 高一致性:超时剔除和主动更新的结合,最大内存和淘汰策略兜底。你没办法确保内存不会增加,从而使服务不可用了。

 

缓存的粒度问题:

我们知道,用户第一次的客户端,客户端访问 Redis 肯定是没有的,这个时候只能从数据库 DB 那里获取信息,代码如下:

select * from t_teacher where id= {id}

在 Redis 设置用户信息缓存,代码如下

set teacher:{id} select * from t_teacher where id= {id}

这个时候我们来看看缓存粒度问题。

因为我们要更新全部属性。到底我们是采用 select * 还是仅仅只是更新你需要更新的那些字段呢?如下两段代码:

set key1 = ? from select * from t_teacher

 

set key1 = ? from select key1 from t_teacher

 

缓存粒度控制可以从以下三个角度来观察,通过这三点来决定如何选择。

全量属性更好。上面一个对比 * 和某个字段的查询,最好是通过全量属性,这样的话,select * 具有很好的通用性,因为如果你 select 某个字段的话,未来如果一旦业务改变,代码也要随之改变才可以。

 

2.占用空间:部分属性会更好。因为这样占用的空间是最小的。

3.代码维护上:表面上全量属性会更好。我们真的需要全量吗?其实我们在使用缓存的时候,优先考虑的是内存而不单单只是保证代码的扩展性。

缓存穿透问题

大量请求不命中(即访问缓存拿数据时发现缓存里没有数据,然后再访问数据库拿数据发现还是没有):

 

解决方案1:缓存空对象:

当缓存中不存在,访问到数据的时候,又找不到数据,需要设置给cache的数值为null,这样的下次的访问

id 的时候,就会直接访问缓存中的null  了。

 

解决方案2:布隆过滤器的拦截

布隆过滤器实际上一个很长的二进制向量,和系列的堆积函数。布隆过滤器的用于检索一个元素的是否在

一个集合内。他的优点是:空间效率,和查询的时间,远程超过一般的算法,

缺点: 有一定的错误识别率和删除困难。

 

总结:

缓存的收益: 加快读写,降低后端存储负载降低后端存储负载

缓存的成本:缓存和存储数据不一致性,代码维护成本、运维成本。

更新策略:推荐结合剔除、超时、主动更新三种方案共同完成。

穿透的优化:使用缓存空对象和布隆过滤器来解决,注意各自使用场景和局限性。

 

无底洞问题:分布式缓存中,堆积机器不一定会使得性能更好,有四种批量操作方式:串行命令、串行 IO、并行 IO、hash_tag。

 

雪崩问题:缓存层高可用、客户端降级、提前演练是解决雪崩问题的重要方法。

 

热点key问题:互斥锁和永不过期这两种方式能够在一定程度上解决热点key问题,但各自有自己的缺点需要我们了解

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!