Skip to content

Latest commit

 

History

History
157 lines (96 loc) · 9.21 KB

Cache.md

File metadata and controls

157 lines (96 loc) · 9.21 KB

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

https://memcached.org

简介

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无法备份和持久化,崩溃或重启之后数据将全部丢失。

LocalCache

存在本地的缓存

访问流程

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.先更新数据库,后删除缓存 缓存删除失败,只有等到缓存失效才能得到正确的值。

未完待续