1.输入网址后,查询浏览器缓存 2.查询浏览器dns缓存 3.查询操作系统dns缓存 4.请求dns服务器,查询dns服务器缓存 5.获得ip,静态资源走cdn缓存。动态数据走服务器 6.如果配置了页面缓存,走页面缓存 7.如果配置了本地缓存(localcache),走本地缓存 8.如果配置了分布式缓存(如redis等等),走分布式缓存 9.数据库操作,数据库缓存
使用场景
缓存类型 | 使用场景 | 使用示例 | 优点 | 缺点 | 读取时间 |
---|---|---|---|---|---|
localcache | 少量数据,对应用程序只读或读多写少 | 后台配置,分区信息 | 无需网络开销,访问速度最快 | 集群机器数据不同步 | 0.00001344ms |
memcache | 海量数据,高并发读写 | 评论内容,账号信息 | 内存占用相对redis少,适合大键存储 | 数据结构单一,不支持备份及持久化,只支持客户端操作 | 0.4437ms |
redis | 海量数据,高并发读写 | 评论id索引,收藏视频信息 | 数据结构丰富,支持备份及持久化,支持服务器操作 | 相对memcache内存效率低 |
- localcache适用于存储少量数据及对应用程序只读或读多写少的场景,例如后台黑白名单、推广信息等,因为应用程序对这些数据几乎只是只读的,数据的修改主要发生在后台管理员更新配置时,且这些数据量很少,完全可以存储在本地内存当中。应用程序只需要定期从数据库load数据进行更新即可。对于分布式集群的部署,每台机器独自维护一份localcache,单后台数据有变动时,不同机器不可能同时load更新,因此存在集群机器数据不一致的情况。 但是这种情况通常是在可接受范围内的。
- memcache适用于存储大量高并发读写的数据,减轻数据库访问压力。如果没有memcache缓存,所有的访问直接打到db,高并发情况下将立马把数据库打挂,由于是直接存储在内存当中,因此访问速度将大大降低,同时数据缓存在memcache集群当中,可以确保应用集群访问数据的一致性,而不会存在localcache当中的问题。由于memcache不支持持久化,一旦集群机器出现宕机,将导致所有数据丢失,但是memcache本身就不是为了持久化数据而存在的,所以这也不是一个问题,需要注意的是,一旦memcache出现宕机等情况需要服务重启时,需要对缓存进行预热,不然大量miss同样也会打挂数据库。
- redis同样也是为了应对高并发读写而存在的。和memcache一样也是k-v类型,但是redis支持更丰富的数据结构,list,set,sortset,hashes。由于redis数据不是完全存在内存当中,当redis内存耗尽时,长期不使用的value将被转移到磁盘,因此redis可以存储比自身内存大的数据。同时redis支持持久化及master-slave模式数据备份。重启时可以再次加载磁盘的数据到内存当中。redis还具有容灾模式,只需要开启aof,即使服务器宕机也可以通过aof文件进行数据恢复。是否使用持久化及开启aof要根据具体业务场景进行选择。
MemCache是一个自由、源码开放、高性能、分布式的分布式内存对象缓存系统。它通过在内存中缓存数据和对象来减少读取数据库的次数,从而提高了网站访问的速度。
MemCaChe是一个存储键值对的HashMap,在内存中对任意的数据使用的key-value存储。
1、应用程序输入需要写缓存的数据 2、API将Key输入路由算法模块,路由算法根据Key和MemCache集群服务器列表得到一台服务器编号 3、由服务器编号得到MemCache及其的ip地址和端口号 4、API调用通信模块和指定编号的服务器通信,将数据写入该服务器,完成一次分布式缓存的写操作
MemCache虽然被称为"分布式缓存",但是MemCache本身完全不具备分布式的功能,MemCache集群之间不会相互通信,所谓的"分布式",完全依赖于客户端程序的实现。
和负载均衡算法一样,路由算法决定着究竟该访问集群中的哪台服务器。
1、余数Hash 把key的hash对服务器集群数量取余。 服务器数量改变的时候会发生缓存无法命中
解决方案:在访问低谷的时候扩容,模拟请求预热数据。
2、一致性Hash算法 先建立一个一致性hash环,环的大小是[0,2^32-1] 给服务器一个hash值,根据值在排在hash环上,排完了之后。 如果要查询一个key,拿到key的hash值,在这个环上顺时针找服务器,最近的服务器就是目标服务器。 如果服务器增加,对缓存服务器影响没那么大,集群中的服务器节点越多,增加节点带来的影响越少。
2^32的一致性环通常使用二叉查找树实现
内存结构
MemCache采用的内存分配方式是固定空间分配。 涉及了slab_class、slab、page、chunk四个概念。
1.slab_class里,存放的是一组组chunk大小相同的slab 2.每个slab里面包含若干个page,page的默认大小是1M,如果slab大小100M,就包含100个page 3.每个page里面包含若干个chunk,chunk是数据的实际存放单位,每个slab里面的chunk大小相同
内存分配方式
Memcached利用slab allocation机制来分配和管理内存,它按照预先规定的大小,将分配的内存分割成特定长度的内存块,再把尺寸相同的内存块分成组,数据在存放时,根据键值大小去匹配slab大小,找就近的slab存放。
存放数据时,首先slab要申请内存,申请内存是以page为单位的。所以在放入第一个数据的时候,无论大小为多少,都会有1M大小的page被分配给该slab。申请到page后,slab会将这个page的内存按chunk的大小进行切分,这样就变成了一个chunk数组,最后从这个chunk数组中选择一个用于存储数据。
就是说,value最大是1M
如果没地方存了,就进行内存回收。
内存回收方式
当数据容量用完MemCached分配的内存后,就会基于LRU(Least Recently Used)算法清理失效的缓存数据(放入数据时可设置失效时间),或者清理最近最少使用的缓存数据,然后放入新的数据。
在LRU中,MemCached使用的是一种Lazy Expiration(惰性失效)策略,自己不会监控存入的key/vlue对是否过期,而是在获取key值时查看记录的时间戳,检查key/value对空间是否过期,这样可减轻服务器的负载。
如果如果MemCache启动没有追加-M命令,则表示禁止LRU,这种情况下内存不够会报Out Of Memory错误。
MemCache的LRU算法不是针对全局的,是针对slab的
MemCache单进程在32位机中最大使用内存为2G,64位机则没有限制。 Key最大为250个字节,超过该长度无法存储 MemCache设置添加某一个Key值的时候,传入expiry为0表示这个Key值永久有效,这个Key值也会在30天之后失效
memcache无法备份和持久化,崩溃或重启之后数据将全部丢失。
存在本地的缓存
1、应用程序输入需要写缓存的数据 2、读取localcache是否存在,如果不存在就去读下一个 3、返回数据
相比memcache 能进一步降低网络请求的开销(5-10ms => 0.01ms)
比如说现在正在用的yac localcache
存储容量有限 每个实例共享16M key + 128M value 数据不保证强一致性, 可能不同实例短时间内会获取到不同的value 对于只允许请求一次的api 结果cache不适用 不能主动delete, LocalCache分布在所有instance上, 单次调用delete没有意义 实现未加锁, 并发写时有极小概率< 1/10,000,000 可能会get到错误数据, 价格等敏感数据勿用
建议使用场景: 用于缓存"最高频访问/不易变/value长度较小"的数据
https://zhuanlan.zhihu.com/p/408515044
先更新缓存还是先删除缓存?
1.先更新缓存,后更新数据库 数据库更新失败,相当于没更新。 2.先更新数据库,后更新缓存 缓存更新失败,只有等到缓存失效才能得到正确的值。
并发引发的一致性问题 两个线程同时更新同一条数据,出现数据库更新成功,缓存更新失败的问题之类的。 通常解决方案是加 分布式锁 ,线程在修改同一条数据之前先申请分布式锁,拿到锁的线程才允许更新数据库和缓存。
删除缓存可以保证一致性吗
1.先删除缓存,后更新数据库 数据库更新失败,等于没更新
2.先更新数据库,后删除缓存 缓存删除失败,只有等到缓存失效才能得到正确的值。
未完待续