无处不在的缓存机制与几何分形学
本文内容
- 前端 Cache 机制
- Web 系统和几何分形学
缓存机制在实际项目中被极其广泛应用,通过缓存机制来提升系统交互的效率。简单地说,在两个环节或系统之间,会引入一个 Cache/Buffer 做为提升整体效率的角色。
有趣的是,缓存机制令人惊奇并且优美的遵循着“几何分形”的规律,也就是几何分形学的“自相似性”:
法国数学家曼德尔勃罗特对分形几何产生了重大的推动作用。他在1975、1977和1982年先后用法文和英文出版了三本书,特别是《分形——形、机遇和维数》以及《自然界中的分形几何学》,开创了新的数学分支——分形几何学。客观事物具有自相似的层次结构,局部与整体在形态、功能、信息、时间、空间等方面具有统计意义上的相似性,称为自相似性。例如,一块磁铁中的每一部分都像整体一样具有南北两极,不断分割下去,每一部分都具有和整体磁铁相同的磁场。这种自相似的层次结构,适当的放大或缩小几何尺寸,整个结构不变。
等同类比的一个概念,我们常常说的“空间换时间”,牺牲一部分空间代价,来换取整体效率的提升。
例如,A和B两者之间的数据交换,为了提升整体的效率,引入角色C,而C被用于当做热点数据的存储,或者是某种中间处理的机制。
我们先从 Web 前端开始,看看哪些比较关键的缓存机制?它们又是怎样协调工作的呢?
前端 Cache 机制
1,域名转为IP地址(域名服务器DNS缓存)
域名只是一个服务器真实 IP 地址的别名。实际中都是通过IP访问,而域名仅仅是为了便于记忆。
获得 IP 地址需要查询 DNS 映射表。虽然这是一个非常简单的查询, 但如果每次用户访问一个 URL 都去查询 DNS,会产生一个可怕的访问量级。DNS 服务器会告诉你,你别老是经常过来,万一我挂了,我们就无法愉快地玩耍了。
各个浏览器的缓存时间有一定的差别。例如,在 chrome 浏览器中查看 DNS 缓存时间的方式是:
chrome://net-internals/#dns。
浏览器一般会在本地建立一个 DNS 缓存,在一段比较长的时间里,都是使用本地的缓存映射。例如,在 Win7 系统,在 cmd 运行“ipconfig /flushdns ”,立刻刷新本地 DNS。
优点:域名映射为IP非常快。
成本:消耗一定的浏览器空间来存储映射关系。
2,访问服务器,获取静态内容(地理位置分布式服务 CDN)
CDN 原理是将离你很远的东西,放在离你很近的地方,从而提高用户的访问速度。可以理解用空间换时间,本质上也是一种特殊的中间 Cache。
腾讯、阿里等这些大的一线互联网公司一般倾向于建立自己的 CDN 系统,中小型企业也经常使用第三方的 CDN 服务。
优点:解决用户离服务器太远的时候,网络路由中跳来跳去的严重耗时。
成本:全国各地部署多套静态存储服务,管理成本比较高,发布新文件的时候,需要等待全国节点的更新等。
3,浏览器本地缓存(无网络交互类型)
前端优化原则,其中一条就是尽量减少请求,以降低服务器压力和提升用户体验的效果。静态文件,例如,Js、html、css、图片等内容,很多内容可以一次请求,之后就可以直接访问本地缓存,不再请求 Web 服务器。
常用的方法是通过Http协议头中的expire和max-age来控制,这两者的使用方法和区别,我这里就不赘叙了。还有一种HTML5中很热的方式,则是localStorage,尤其在移动端也被做为一个强大的缓存,甚至当做一种本地存储来广泛使用。
优点:减少网络传输,加快页面内容展示速度,提升用户体验。
成本:占用客户端的部分内存和磁盘,影响实时性。
4,浏览器和 Web 服务协议缓存(有网络交互类型)
浏览器的本地缓存是存在过期时间的,一旦过期,就必须重新向服务器请求。这个时候,会有两种情形:
- 服务器的文件或者内容没有更新,可以继续使用浏览器本地缓存。
- 服务器的文件或者内容已经更新,需要重新请求,通过网络传输新的文件或者内容。
这里的协商方式也可以通过Http协议来控制,Last-Modified和Etag,这个时候请求服务器,如果是内容没有发生变更的情况,服务器会返回 304 Not Modified。这样的话,就不需要每次访问服务器都通过网络传输一个比较大的文件或者数据包,只要简单的http应答就可以达到相同的请求文件效果。
下图中的例子,是腾讯的自建CDN(imgcache.gtime.cn):
优点:减少频繁的网络大数据包传输,节约带宽,提升用户体验。
成本:增加了服务器处理的步骤,消耗更多的CPU资源。
5,浏览器中间代理
上面的几种 Cache 机制都是非常常见。但是,在移动互联网时代,流量昂贵是很多用户心中深深的痛。于是,又出现了一种新型的中间 Cache, 也就是在浏览器和web服务器再架设一个中间代理。这个代理服务器会帮助手机浏览器去请求 Web 页面,然后将 Web 页面进行处理和压缩(例如压缩文件和图片),使页面变小,然后再传输给手机端的浏览器。
部分手机浏览器(如 Chrome)号称可以节省流量,提升访问速度,就是上述做法。但也分为两种情况:
-
用户的网络和手机配置都比较差,因为页面被压缩变小,加载和传输速度变快,并且节约了流量。
-
用户的网络和手机配置都比较好,本身直连速度已经很快了,反而因为设置了中间代理,加载速度变慢,也可节约流量。
下图是 Chrome 手机浏览器中,开启和不开启中间代理的对比图:
优点:节约用户流量,大部分情况下提升了加载速度。
成本:需要架设中间代理服务器,对各种文件进行压缩,有比较高的服务器维护成本。
6,预加载缓存机制
这种加载方式主要流行在移动端,为了解决手机网速慢和浏览器加载性能问题,浏览器会判断页面的关联内容,进行“预加载”。 也就是说,在用户浏览A页面的时候,就提前下载并且加载B页面的内容。给用户的体验就是,B页面一瞬间就出现了,中间没有任何延迟的感觉,从而带来更好的用户体验。
这种实现机制,往往由浏览器来实现,当然,手机页面本身,也可以通过JS来自身实现。而这种机制也存在一些问题,浏览器需要预判用户的浏览行为,在一些场景下,这个预判算法本身不一定准确,如果不准确则带来一定的流量、内存和系统资源的浪费。
优点:给用户带来极佳的页面展示体验。
缺点:预判实现比较复杂,占据一定的内存和手机系统资源,可能产生流量和资源浪费。
前端 Cache 当然不仅仅如此简单,如果细致到每一个小环节和组成部分,我们会发现实际上是无处不在的,例如浏览器的渲染行为、网络网卡的传输环节,小环节和小环节之间也有无数这种类型的cache角色。
这个就如同几何分形学中的自相似性:从整体上看符合某种组成规律或者特性,同时,从局部看,仍然符合某种组成的规律或者特性。如叶子,每个局部都和主干组成结构相似。
Web 系统和几何分形学
1,Web 系统中的缓存机制
看完上面的前端cache,我们会感觉到缓存机制在前端中的确无处不在,那么它在其他地方和环节,是否也无处不在?
可以看看这张图:
实际上,每一个环节本身是可以又再次被放大的,放大以后,我们又看见了更多缓存机制的“特性”存在。从一个整体来看,符合该规律,从组成部分来看,仍然符合该规律。
每一个组成缓存机制的“成员”的内部,又存在着更多的缓存机制。
Apache 内部的一些“缓存机制”:
-
url映射缓存mod_cache(有mode_disk_cache和mod_mem_cache,后者官方已不推荐)
-
缓存热点文件打开描述符mod_file_cache(对于静态文件的情况,减少打开文件中open行为的耗时)
-
启动的时候,通过prefork模式设置的StartServers服务进程池,牺牲内存空间。
MySQL 内的一些“缓存机制”:
-
数据库的索引,牺牲磁盘空间(组合索引等会占据很大的磁盘空间)
-
innodb_buffer_pool_size,热点数据的缓存,牺牲内存空间
-
innodb_flush_method写入磁盘的机制,可以配置成缓冲写入的方式
-
query_cache_size查询缓存,牺牲内存空间
-
thread_cache_size数据库连接池的缓存个数,牺牲内存空间
2,接近硬件层面的“空间换时间”
那我们再来看更细小的一个环节,计算机写的操作。我们会发现,在内存和物理磁盘之间,还有一个磁盘缓冲区(页高速缓存),这是内存和磁盘之间的“缓存”。当然,读取的操作也是同理。如果没有磁盘缓冲区,就会要求每传一个字就需要读或写一次磁盘。
但磁盘缓冲区与缓存还不同,它是容量固定的硬件,而缓存可以动态分配。
下图是 MySQL 的写入缓冲,简单说,如果每次 insert 数据都访问磁盘,而访问磁盘就意味着 IO 操作,这样的性能会很差:
MySQL 对于非聚集类索引的插入和更新操作,不是每一次都直接插入到索引页中,而是先插入到内存中。如果该索引页在缓冲池中,直接插入;否则,先将其放入插入缓冲区中,再以一定的频率和索引页合并,此时,就可以将同一个索引页中的多个插入合并到一个IO操作中,大大提高写性能。
此外,还有介于 CPU 与内存之间也存在缓存机制——高速缓存,常用指令会存在放在寄存器中,因为CPU访问寄存器会远快于访问内存,中间为了缓冲它们之间差距,设置了多级高速缓存,以及目录项高速缓存(存放描述文件系统路径名的目录项对象),索引节点高速缓存(存放描述磁盘索引节点的索引节点对象)。
例如下图是 Intel i7 920 的各级缓存大小:
我们可以看出,计算机系统从大的层面看,遵循“缓存机制”,同时,在每个局部层面,同样遵循该规律。
3,现实世界中的“缓存机制”
我们喝水通常使用杯子,而杯子实际上扮演着一个 Cache 的角色。比如,一个人距离饮水机较远,他要喝水,有两种方式:
-
不用杯子,每次渴了直接去饮水机喝(细节就算了)。结果:频繁跑动,浪费体力。
-
使用杯子,渴了先喝杯子(Cache)里的水,喝完了,再去接。结果:比较少跑动,节省体力。
也就是人类这么干,你什么时候看到动物也这么做的。喝水存在一个缓存机制,用杯子的空间换取喝水效率。
还有客运,火车也行,假设我们从深圳去广州,坐客车。客运车的座位就那么多,相当于一个“队列”。网络传输也用相同的规律,队列满或者超时则发送。客车也有两种运作方式:
- 来了一个人,用只能容纳一个人的小车,不等待直接去广州。
- 来了一个人,先上车(Buffer),等人满或者达到班车约定时间(队列超时)再出发。
显然,第一种太浪费资源。
看到这里,你会发觉,计算机的一些原理,在现实世界里有无处不在……
几何分形学是个非常有趣的东西,某些规律,实际上还贯穿在整个宏观和微观世界中。