提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
前言
在互联网系统中缓存是必不可少的组件之一,用好缓存事半功倍,那缓存有什么?又怎么用呢?本文将详细讲述
一、缓存有哪些?你用过或了解哪些缓存?
用户层:
DNS缓存
浏览器DNS缓存
操作系统DNS缓存
本地DNS服务商缓存
DNS服务器缓存
客户端缓存
浏览器缓存(Expires、Cache-Control、Last-Modified、Etag)
App客户缓存(js/css/image...)
应用层:
页面静态化
业务数据缓存(Redis/Memcached/本地文件/guava cache/ehcache/caffeine等)
消息队列
数据层:
NoSQL: Redis、Memcache、SSDB等
MySQL: Innodb/MyISAM等Query Cache、Key Cache、Innodb Buffer Size等
代理层:
CDN缓存(一般基于ATS、Varnish、Nginx、Squid等构建,边缘节点-二级节点-中心节点-源站)
接入层 - Nginx为例:
Proxy_cache: 代理缓存,可以存储到/dev/shm或者SSD
FastCGI Cache
Nginx+Lua+redis: 业务数据缓存
系统层:
CPU : L1/L2/L3 Cache
内存
磁盘:磁盘本身缓存、dirtyratio/dirtybackground_ratio、阵列卡本身缓存
二、如果让你加一个缓存,你最先想到怎么做?
1.本地缓存使用
本地服务的内存缓存(简单的HashMap、HashSet、ConcurrentMap)
基于本机内存缓存框架(guava cache、ehcache、caffeine等)
Guava Cache:
愿意消耗一些内存空间来提升速度。
预料到某些键会被多次查询。
缓存中存放的数据总量不会超出内存容量。
Spring5,spring boot2.0 中使用了caffeine cache(jdk8及以上版本) 代替了guava cache
服务本地内存缓存
优点:
速度快,简单,不依赖于第三方服务。
缺点:
容量有限,分布式环境无法保证一致性,服务重启缓存失效。
分布式系统下的缓存
Redis、memcached
代码实例:
2.分布式,多集群系统的缓存选型
Redis、memcached对比
一、性能
由于Redis只使用单核,而Memcached可以使用多核,所以平均每一个核上Redis在存储小数据时比Memcached性能更高。而在100k以上的数据时,Memcached性能要高于Redis
二、内存使用效率
使用简单的key-value存储的话,Memcached的内存利用率更高,而如果Redis采用hash结构来做key-value存储,由于其组合式的压缩,其内存利用率会高于Memcached。
三、Redis支持更丰富的数据类型
Redis相比Memcached来说,拥有更多的数据结构并支持更丰富的数据操作,通常在Memcached里,你需要将数据拿到客户端来进行类似的修改再set回去,序列化再反序列化,这大大增加了网络IO的次数和数据体积。在Redis中,这些复杂的操作通常和一般的GET/SET一样高效。所以,如果需要缓存能够支持更复杂的结构和操作,那么Redis会是不错的选择。
四、数据备份恢复
memcached挂掉后,数据不可恢复;redis数据丢失后可以通过aof或rdb持久化文件恢复,Redis支持数据的备份,即master-slave主从模式的数据备份。
五、数据存储
memcached把数据全部存在内存之中,断电后会挂掉,数据不能超过内存大小;redis支持数据的持久化(RDB、AOF)。同时Redis并不是所有的数据都一直存储在内存中的,当物理内存用完时,Redis可以将一些很久没用到的value交换到磁盘
六、集群、分布式存储及HA
Memcached本身并不支持分布式,因此只能在客户端通过像一致性哈希这样的分布式算法来实现Memcached的分布式存储。
Redis 集群是3.0之后才引入的,在3.0之前,使用哨兵(sentinel)机制
Redis 集群没有使用一致性hash,而是引入了哈希槽的概念。Redis 集群有16384个哈希槽(slot),当需要在 Redis 集群中放置一个 key-value 时,每个key通过CRC16校验后对16384取模来决定放置哪个槽,集群的每个节点负责一部分hash槽。
集群、分布式存储及HA
Memcached HA
借助于第三方memagent代理来实现
Redis HA
支持主从复制模型
3.该怎么选呢
当做简单k-v缓存时,选redis和memcached没有太大区别
Memcached value最大为1M,如果value大于1m,只能使用redis
复杂数据结构(哈希、列表、集合等),只能选择redis
有持久化的需要,只能选择redis
4.redis使用场景
1. 显示最新的项目列表
2. 删除与过滤
3. 排行榜相关
4. 按照用户投票和时间排序
5. 过期项目处理
6. 计数
7. 特定时间内的特定项目
8. 实时分析正在发生的情况,用于数据统计与防止垃圾邮件等
9. Pub/Sub
10. 队列
11. 缓存
5.Redis缓存的三大现象的最佳实现
缓存穿透
缓存穿透是说访问一个缓存中没有的数据,但是这个数据数据库中也不存在。普通思路下我们没有从数据库中拿到数据是不会触发加缓存操作的。这时如果是有人恶意攻击,大量的访问就会透过缓存直接打到数据库,对后端服务和数据库做成巨大的压力甚至宕机。
解决方案:
缓存空对象。如果缓存未命中,而数据库中也没有这个对象,则可以缓存一个空对象到缓存。如果使用Redis,这种key需设置一个较短的时间,以防内存浪费。
缓存预测。预测key是否存在。如果缓存的量不大可以使用hash来判断,如果量大可以使用布隆过滤器来做判断。
缓存击穿
缓存并发这个场景很容易解释:多个客户端同时访问一个没有在cache中的数据,这时每个客户端都会执行从DB加载数据set到缓存,就会造成缓存并发。
解决方案:
缓存预热。提前把所有预期的热数据加到缓存。定位热数据还是比较复杂的事情,需要根据自己的服务访问情况去评估。这个方案只能减轻缓存并发的发生次数不能全部抵制。
缓存加锁。 如果多个客户端访问不存在的缓存时,在执行加载数据并set缓存这个逻辑之前先加锁,只能让一个客户端执行这段逻辑
缓存防雪崩
缓存雪崩是缓存服务暂时不能提供服务,导致所有的请求都直接访问DB。
解决方案:
构建高可用的缓存系统。目前常用的缓存系统Redis和Memcache(magent来支持HA)都支持高可用的部署方式,所以部署的时候不防先考虑是否要以高可用的集群方式部署。
限流。Netflix的Hystrix是非常不错的工具,在用缓存时不妨搭配它来使用。
6.Redis更新问题
数据的更新 ,先缓存,还是先DB?
Cache Aside Pattern
这是最常用最常用的pattern了。其具体逻辑如下:
失效:应用程序先从cache取数据,没有得到,则从数据库中取数据,成功后,放到缓存中。
命中:应用程序从cache中取数据,取到后返回。
更新:先把数据存到数据库中,成功后,再让缓存失效。
思考:
1. 为什么是让缓存失效,而不是更新缓存
1.1设置缓存失效失败了怎么办,增加重试?
1.2服务和缓存不可达---网络不通
发个mq或者监听db的binlog来更新缓存
1.3本机器断网了,或者挂掉了
设置缓存的时候加过期时间
2. 在不考虑网络或服务不可用的情况下,这个模式是否有的问题
7.是否一定需要持久化?
Redis你真的需要持久化么?
AOF和 RDB 两种方式的持久化,默认的开启了AOF持久化。
测试demo,Redis超时表现,
业务QPS(A:94,max:260):
200ms,20-40次超时
500ms,个位数
2000ms,连续记录了9天都无超时
关闭AOF,200ms 无超时
总结
缓存有哪些?
用户层、代理层、接入层、应用层、数据层、系统层
应用层的缓存选型?
本地服务内存缓存:Map、guava cache,caffeine cache
分布式缓存:redis,memcached,从几个方面对两者进行了对比
缓存的最佳实践
缓存穿透、缓存并发、缓存雪崩,redis aof持久的影响(不需要持久化就关闭,可以提高性能)