缓存
从微观角度:寄存器、内存层面来看
关键词: 分级策略、指令预读、指令区、数据区、时钟周期、LRU、LFU
存储结构的分级策略,从快到慢的排序为: 寄存器, L1-cache, L2-cache, L3-cache, 内存。
制造出存储的物件,要考虑成本、散热、性能等等情况。CPU内的速度非常快,但是内存在离CPU在物理上看还是有一定的距离的,速度很明显得受到影响。
时钟周期的概念:
CPU 是用 石英晶体产生的脉冲 转化为 时钟信号 来驱动的,每次 高低电平 的转换的就是一个 时钟周期。
CPU 的主频, 说的就是 时钟信号的频率。 一条指令,通常需要 2、4、6个时钟周期。
用数据来感受下:
1GHz的CPU,1G就是10个亿,所以一个时钟周期就是 1 / 10 亿秒;
而光速是 3*10的8次方米每秒,就是每秒可以走3亿米。
所以,在一个时钟周期内,连光速都只能走30厘米。而现实中还达不到这么快的速度,还有各种阻塞损耗。
所以,存储的分级策略就很有必要了。 如果把内存放到CPU里,这样散热性能就会收影响,而且我们不能个性化组装内存了,因为在CPU出厂时内存就定制好了。
都是些线性存储结构。
寄存器: 有几十个到几百个之间,速度非常快,一般要求在半个时钟周期内完成读写任务。
L1-cache: 在几十kb到几百kb不等,速写速度在2到4个CPU时钟周期。
L2-cache:大小具体看CPU的型号,读写速度在10到20个CPU时钟周期。
L3-cache:读写速度在20到60个CPU时钟周期。
内存:材料主要是半导体硅,用总线和CPU进行交流。读写速度大概在200到300个CPU时钟周期。
有了分级策略之后,再来看看,CPU执行一条指令的时候,需要2、4、6个CPU时钟周期,而读写内存需要200到300个CPU时钟周期。所以,我们不能每次从内存中只读取一条指令,这样等待的时间太久了!
所以,每次读的时候,都预读 几十条或者上百条的指令 到L1-cache中,这里是可以跟得上CPU的速度的。对于此,在L1-cache里,指令缓存和数据缓存是分开放的,如果数据覆盖了指令,是很糟糕的。
但是在L2-cache、L3-cache里是不需要区分缓存和指令的,因为根本就没有指令存在,不需要协助预读问题。
查询某个数据时,我们先去寄存器找,没有在去L1-cache找,再去L2-cache找,再去L3-cache找,再去内存中找。
怎么查找一个数据是否在缓存中?或者说缓存里的数据结构是怎样的?
可以就存储一个key-value结构的数据, key是内存地址,value是该地址上的值,也就是缓存的值;比较key的值看有没有在缓存中。这样需要遍历整个缓存空间进行查找,显然是不好的。
或者,我们可以通过类似 HashMap 的结构来存储,来查找,不用遍历整个数据。
缓存中满了,进来新的数据,怎么淘汰旧的数据呢?
一般有算法来进行解决: 先进先出FIFO、LRU、LFU
从宏观角度: 应用层面来看
可以缓存的地方
浏览器
当HTTP响应允许缓存的时候,会缓存一些静态资源,比如图片、css、html、js文件
网络服务提供商ISP : 访问网络的第一跳,加快访问速度。
反向代理:
在服务器之前,用户请求时,可以直接返回 反向代理 里的缓存。
本地缓存、分布式缓存
数据库的缓存:
MySQL 里可以查询缓存,但是之后好像取消这个设计了,因为每次增删改都会改变缓存,很频繁。
Java里的缓存
字符串常类池里的缓存、 Byte、Short、Character、Integer、Long、Boolean 包装类型的缓存
CPU的分级缓存
就是上面微观的层面。 通过 MESI 等缓存一致性协议 来解决 多核CPU缓存数据一致性的问题。
内容分发网络,CDN,content distribution network。
利用更靠近用户的服务器,将静态资源分发给用户。比如音乐、图片、视频、html、css、js等。
多台服务器可以看成是一种冗余机制,具备高可用性。
缓存带来的一些问题
缓存穿透
请求一定不存在的数据,会直接穿透缓存到达数据库。
解决:
对这类请求进行过滤; 对不存在的数据缓存一个空数据。
缓存雪崩
大量请求到达数据库,造成数据库崩溃。
原因及解决方法:
对数据来没来得及加载到缓存中情况: 进行缓存预热,提前加好缓存。
对缓存数据大面积同一时间失效情况: 设置合理的缓存过期时间。
对缓存服务器宕机情况: 用分布式缓存,保证其他节点的缓存可用,每个节点只缓存部分数据。
分布式缓存的 数据分布
顺序分布
哈希分布
根据哈希值,分配到对应的节点上。 但是当节点数增加,哈希的计算就有变化,会导致大量的数据迁移。
解决: 一致性哈希算法,DHT,distributed hash table
看 2 张图,
是一个环状结构,哈希计算之后,分布到顺时针看最近的哪个节点。
当增删节点时,也只会影响到前面的哪个节点。
但是对于节点少的时候,就会用数据分布不均匀的情况。解决方法是增加 虚拟节点,映射到真实的节点上,虚拟节点有很多。