网站缓存好是好,权衡利弊更重要
你搭建了一个动态博客,比方说Typecho吧,或者WordPress之类的,觉得访问速度有些慢。上网搜索,很多人都说『网站慢,是因为动态博客资源占用大,装个缓存插件就好』。于是,你找到了适合自己所用的CMS(即俗称的『网站后台程序』)的缓存插件。装好之后测试发现,访问速度真的快了些,于是你很开心。
好了,笔者有个问题想要问你:是药三分毒,缓存插件又有几分呢?
网站加载速度也差不多1. 前言
搭建博客站,或者公司官网站,什么站都好,关于选择动态CMS还是静态CMS的争论,大概是从鸽子衔枝之年就已经开始了。两方各有各的优点,比如说动态站方便更新易于操作,静态站占用更小并发更高,等等。一般来说,不管怎么争论都好,有几个点双方一般都会达成一些共识的,这其中就包括:动态CMS对资源(CPU算力等)的占用量更大,或者说,在相同可用资源的环境下,静态站可以承受更高并发。
究其原因,动态CMS里,用户访问网站的过程,就是CMS读取数据库并生成页面的过程。由于要执行动态程序,后台还要跑一个数据库,再加上其他的一些逻辑计算,最后才能拼接页面,这自然就比静态站点『直接读取文件并输出』要消耗更多资源。因此,很自然的想法就是,如果页面内容不变的话(比如博文写好之后几乎不改动),与其反复进行这样的生成,还不如把生成结果缓存起来,下次用户来了直接输出,这样就能在保留动态站易于使用的优点的同时,把性能开支降低到接近静态站的程度,页面响应速度也会更快。
所以只要你随手一搜,就能找到一大批针对各种CMS的缓存插件,其卖点基本上都是加速网站访问,减少性能占用,提高QPS(每秒查询/请求数),等等:
包括国内外流行的CMS系统听起来,缓存是个一本万利的好买卖,既能提高这个又能提高那个。不过,仔细想想就会明白,如果真的有这么好的话,本文也不会存在了,直接告诉你去找一个名字好听的插件装上就可以坐和放宽,甚至直接把缓存功能内置进CMS,遇事不决直接启动,何乐而不为呢?
在开始聊之前,提醒一下,本文主要还是侧重于动态内容的缓存,所以下文所说的『缓存』,指的都是对于动态生成的页面的缓存。对于静态文件(图片,CSS等)的缓存,除非专门指出,否则并不包括在内。
2. 啊,缓存!
虽然这一段可能有些像营销号,或者AI生成,但笔者还是觉得,有必要先把目前的缓存插件技术先过一遍,这样才能更好地说明标题里所谓『需要权衡利弊的点』到底何在。如果你已经了解了底层技术,可以直接跳到第三大节。
缓存主要分两种,全页缓存(也有叫全站缓存的),就是在CMS生成好页面之后,整页存下来,下次直接输出缓存,不再重新生成。这是最符合直觉的,也是最简单的方式;还有一种是分区缓存,就是保持页面生成流程不变,只在某些需要计算的区域(比如标签云,比如最新评论列表等)做缓存。这种缓存方式对页面影响较小,可以实现更细粒度的缓存控制,一般多见于大站点,当用户量起来之后,即使只优化一部分,也能节省很多资源。
至于落到具体实现,就可以有以下的几种方式了。
2.1 CMS 缓存插件
这个应该是很多站长首先接触到的几种缓存方式之一,因为他即插即用,而且真的就是如引言所说,找到支持你所用的CMS的插件安装,就可以获得一定的缓存效果。例如笔者正在使用Typecho,就有个很有名的缓存插件叫TpCache的,虽然并没有亲自使用,但看其他站长反馈效果不错,那就当他不错吧。
所谓缓存插件,具体到内部实现,也分为很多种方式。为了避免混淆起见,先给本节要讨论的类型下个定义:基于动态程序(如PHP)来判断缓存状态,并输出缓存内容,的插件。比如,下面的示例代码,就构成了一个可用的缓存功能:
<?php
$cacheFileName = 'cache-demo.html';
// 判断是否有缓存
if(file_exists($cacheFileName)) {
include $cacheFileName;
die();
}
// 启动并清空缓冲区
ob_start();
ob_clean();
// 模拟页面逻辑
echo('这是动态页面测试<br>');
echo('现在是:');
echo(date('Y-m-d H:i:s', time()) . '<br>');
echo('模拟耗时的业务逻辑:');
sleep(2);
echo(bin2hex(random_bytes(32)) . '<br>');
// 写缓存
file_put_contents($cacheFileName, ob_get_contents());
?>运行此程序后,如果没有缓存,就执行业务逻辑,并在同目录下生成缓存文件,下次再运行的时候(比如刷新页面),会发现输出速度快了很多,内容也固定不变了,就是因为程序检测到缓存文件的存在,不再执行繁重的业务逻辑,直接输出缓存文件。这就是最简单(也最通用的)的全页缓存插件原理,本质上就是借助了PHP的输出缓冲区(OB)来捕获输出到页面的内容,然后保存到文件,或其他的缓存源。
基本上,各类所谓『高兼容性,即插即用,无需额外配置』的缓存插件,他的全页缓存功能,都是这样的核心原理。比如上面提到的TpCache,在他的Plugin.php里面可以找到关于缓存的逻辑,对照Typecho的Hook列表,整理之后取关键部分如下:
// 读缓存,Hook在index.php/begin下
function C() {
$data = self::getCache(); // 接缓存引擎
$html = $data['html'];
header("TpCache: HIT");
echo($html);
die();
}
// 写缓存,Hook在index.php/end下
function S($html = '') {
if (empty($html)) {
$html = ob_get_contents();
}
$data = array();
$data['time'] = time();
$data['html'] = $html;
self::setCache($data); // 接缓存引擎
}逻辑很简单粗暴,就是获取缓存后直接输出,然后结束运行(是整个CMS都会停止运行)。其他的都只是为了辅助这段核心代码而已,比如对接不同的缓存引擎(Redis,Memcached),比如判断是否需要缓存,比如在某些操作(新增评论,修改文章后)清除缓存,等等。查看Typecho的核心代码可以发现,这个插件使用的的index.php/beginHook点非常靠前,甚至比分发路由还要早,此时Typecho核心也只是完成了最基本的初始化而已,所以在缓存命中的情况下,能避免后续一大堆的逻辑运算,数据库查询等,因而确实可以减少相当一部分的资源消耗。
2.2 try_files 的妙用
常用的HTTP服务器Nginx,有一个配置项叫做try_files,意思是,依次检查后面列出来的文件,如果找到了某一个文件,就不再向后查询,直接读取这个文件(或把控制权交给脚本)并输出。所以,理论上来说,有一个很取巧的配置方法:
# 这里只给出关键部分
location / {
try_files /cache$uri.html $uri $uri/ /index.php?$query_string;
}理论上来说,如果用户来访,Nginx会先检查缓存文件(比如访问/,那就在网站根目录检查/cache/.html,是的,就是这样直接拼接文件路径)是否存在,存在就直接返回给用户,不再拉起动态页面处理程序,实现了全页缓存;缓存文件不存在,就回落到动态脚本,由动态脚本来生成页面,写入缓存,以备下次使用。
不过,理想很丰满,现实似乎很骨感。不确定是笔者运气不好还是配置不对,反正在真机跑实际测试的时候,上述方法的缓存效果并不佳(比如,无法区分不同的GET查询参数,如果加入$query_string,似乎又无法识别到缓存文件),甚至有时候完全不可用(尤其是还配置了rewrite规则的时候),总是会回落到动态脚本。因为笔者并没有使用这种缓存方式,所以也就没有深入研究到底是怎么回事,也懒得打日志了,反复尝试几次之后,干脆放弃了事。
虽然实验并不成功,但这个思路其实没什么问题:要想实现高效缓存,需要尽量把缓存点向前(用户侧)移动,避免启动更多的程序。对于Nginx来说,要实现缓存,其实不必如此折腾,还有个更加根正苗红的东西:fastcgi_cache。
2.3 fastcgi_cache 的效用
PHP通过FastCGI与Nginx交互,所以可以让Nginx去缓存FastCGI输出的内容,这也属于全页缓存的一种。查阅官方文档可知,可以在一个正常工作的PHP站点上,添加这些配置(仅供参考,不要直接拷贝到生产环境下使用):
http {
# 配置缓存路径
# levels=1:2 缓存文件路径格式
# keys_zone 缓存键的内存区,这里起名字叫page_cache,容量50MB
# inactive 缓存超时时间
# max_size 最大占用空间
fastcgi_cache_path /var/fastcgi_cache levels=1:2 keys_zone=page_cache:50m inactive=120m max_size=1G use_temp_path=off;
# 在更新缓存,上游错误等情况下,继续使用缓存
fastcgi_cache_use_stale updating error timeout invalid_header http_500;
# 缓存过期时,只让一个请求触发后端
# 其他的等着,超时5s,缓解缓存击穿问题
fastcgi_cache_lock on;
fastcgi_cache_lock_timeout 5s;
# ... 省略其他配置项 ...
}
server {
# ... 省略其他配置项 ...
location ~ \.php$ {
# ... 省略其他配置项 ...
# 启用缓存,指向page_cache缓存区
fastcgi_cache page_cache;
# 只缓存GET HEAD请求
fastcgi_cache_methods GET HEAD;
# 缓存key格式
fastcgi_cache_key $request_method$scheme$host$request_uri;
# 针对不同状态码,设置不同缓存有效期
# 如果需要动态调整缓存有效期
# 可以在PHP中动态返回X-Accel-Expires这个内部标头
fastcgi_cache_valid 200 301 302 30m;
fastcgi_cache_valid any 1m;
# 如果存在某些请求头,则绕过缓存
fastcgi_cache_bypass $http_pragma $http_authorization $http_cache_control;
fastcgi_no_cache $http_pragma $http_authorization $http_cache_control;
# 缓存状态指示
add_header X-Cache-Status $upstream_cache_status;
}
}就可以在不改动原来的程序的情况下实现缓存了。而且此时是Nginx在处理缓存,缓存命中(比如上述配置,有人访问30分钟内,无人访问120分钟内)的情况下,PHP根本不会被拉起来,也就不会有数据库连接的那堆事情,因此性能更为节省。
这次同样是在真机上跑了实际测试,这个的缓存效果就稳定很多了,尤其是Nginx内部处理好缓存Key的情况下,不用担心上面try_files的那种问题。另外正如上面注释所说,更新缓存时,后端程序可以动态返回一个X-Accel-Expires来调整当前URL的缓存时间,因此有一种玩法是,不同页面缓存时长不同,例如首页缓存5分钟,RSS缓存120分钟,文章页缓存1天,等等。
不过需要注意的是,添加缓存容易,主动清缓存要付费订阅(也就是Nginx Plus)的版本才支持。如果不想付钱的话,一般来说有两种替代方法:要么安装第三方模块,例如ngx_cache_purge;要么是定向清除缓存文件,如此教程所示,需要计算Nginx存储缓存文件的具体路径。计算步骤写成PHP就是:
<?php
$host = "请求的Host字段,如 example.com";
$request_method = '请求方法,应大写,如 GET';
$scheme = '协议,应小写,如 http或https';
$request_uri = '要删除的原路径,如 /';
$cachePath = '上面配置里设置的缓存目录,末尾加个斜杠';
# 按照设定的缓存Key的格式,计算缓存Key
$key = hash('md5', $request_method.$scheme.$host.$request_uri);
# 路径格式设置为 levels=1:2
# 即,取末尾第一个字符,和接下来的两个字符(取出后保持正序)
$l_1 = substr($key, -1, 1);
$l_2 = substr($key, -3, 2);
echo('应删除:'.$cachePath.$l_1.'/'.$l_2.'/'.$key);
?>
填入对应参数后执行,就能看到输出的缓存路径:
测试示例用文本编辑器打开路径,确实可以看到Nginx写入的元数据,以及被缓存的页面内容:
不可见字符是正常现象接下来就好说了,要么把上述echo()换成unlink(),要么手动删除,都可以实现定向清除缓存的功能。虽然有些不太优雅,但好歹能用,说不准人家付费功能也是这样实现的呢,只是可能更深入一点,有状态同步(比如避免删除和读取产生竞态什么的)罢了。
最后提一嘴,有一些所谓的『高效缓存插件』其实也是类似的原理,即缓存操作由HTTP服务器完成,插件本身只负责传递缓存规则,判断什么时候该清除缓存罢了。比如LiteSpeed Cache这个WordPress插件,就有两种模式:通用模式适配任何HTTP服务器,专用模式就需要LiteSpeed系列的HTTP服务器,例如OpenLiteSpeed(或者付费版的LiteSpeed)来实现前置缓存。
2.4 边缘缓存
上述讨论的都是在源站实现的缓存,这对于使用单一服务器的站点来说是很好的优化。然而,如果你的站点还使用了CDN服务(包括有些站长可能使用前置线路机反代,也算),那么还有一个更优解:让边缘节点来负责缓存。此时,请求在边缘搞定了,根本不会打到源站,因此比上述各类方法都要更节省资源。
一般来说,CDN服务不会帮你缓存动态内容,但如果你需要,一般都有提供类似的设置项。比如笔者的站点在使用Cloudflare,就可以在缓存选项卡下面配置缓存规则,比如你可以设置为对首页缓存,文章路径缓存,但绕过API路径,等等。甚至如果你愿意的话,还可以设置为『缓存POST请求』——我不知道什么情况下会用到这种东西,但确实可以这么干。
其他服务商也会有类似的设置页面如果你是自己搭建CDN/反代机,要实现缓存也很容易,比如你用Nginx做服务器的,就可以用Proxy Cache来实现类似功能,概念和上面讲到的FastCGI Cache差不多,具体请参考官方文档,这里不再赘述。
2.5 修改主题实现分区缓存
一般而言,动态CMS的核心与主题是分离的。因为修改主题的复杂度,要比修改核心代码低,兼容性也会好一些(核心更新的时候可以直接把主题套上去用),所以有些访问量较大的站点,会在成品的主题上魔改,要么是更加适合站点风格,要么是添加了功能(如本站右侧的广告区以及『每日推荐』),当然,也可以用来魔改分区缓存功能。虽然效果肯定没有全页缓存那么显著,但毕竟能省一点是一点,节省下来的性能就可以用来提高QPS了。
举个例子,博客站的侧边栏,有文章数量,归档,最新文章和评论的显示,这些内容同样是需要读取数据库来计算的,但重要性一般不如正文大,其在主题中的代码一般类似这样:
<?php $data = getData(); ?>
<div>
<span>文章数:<?= $data['post_count'] ?></span>
<span>评论数:<?= $data['comment_count'] ?></span>
<div>
最新文章:<?= $data['new_post']['title'] ?>
</div>
</div>不论是如上所述的直接拼接页面,还是有单独的API接口获取信息,取数据的逻辑都是封装在主题自行实现的getData()里面,需要被调用去读取数据库的。显然,与其每次有访客来都读一次数据库,然后计算各类参数,还不如直接魔改这个函数的逻辑,插入缓存相关代码,让他提前把这些东西计算好并存储起来,下次直接从缓存源读取,就实现了分区缓存。
如果你正在用PC端阅读本文,请看右侧的每日推荐栏目,这个就是分区缓存的一个例子:程序每天凌晨四点计算一次每日推荐,存入Redis,后面来的访客只需要从Redis直接读取就行了,免去每次都随机计算带来的无谓的开销。
3. 没有银弹
比较常见的缓存思路,大概就是上面那几种了。
讲完了具体技术,接下来该聊聊标题的后半部分,即,为什么说要权衡利弊。
3.1 真的需要缓存?
很多站长第一次想到要加缓存,大概率是如引言所说,网站加载慢。虽然,添加缓存确实是一个加速方法,但并不是唯一的方法,因为『慢』是综合的因素,而非单一的指标。打个比方,用户看网页就是收快递的过程,现在发现送快递的速度很慢,于是有人建议『把仓库修得离用户近一些』,这当然是好的,不过在大兴土木之前,为什么不先看看,货物上添加的114514层包装纸,会把包裹加重多少呢?
这就是容易陷入的误区。感觉慢,不一定就是动态程序拖慢了,可能是其他方面拖了后腿。很多博客站点,比如本站,日均PV才百来二百个,同时在线人数不过2-3人,按业界惯例的二八定律计算峰值QPS,甚至不会大于0.01。大多数情况下,服务器就是在那坐和放宽,就算是十年前发布的树莓派3,都能扛住这些流量,更何况是最近才购买的VPS呢?信我,动态CMS在很多情况下不是影响速度的主要因素,在排查页面加载慢的时候,建议先检查并优化别的方面,最后再来考虑性能问题,可能比无脑上缓存效果要好很多。
本站服务器,就是在坐和放宽最典型的情况是,页面需要加载的内容本来就多。比如笔者之前讲过的『大体积图片』问题:部分站点主题乍一看很炫酷,背景是动漫美图,文章头图也是二次元精选,还有各种乱飞的特效,好看的字体,背景音乐,页面底下还有个可爱的Live2D小人晃来晃去。这些资源(图片,CSS,JS等)都是实打实占用加载时间的,而且和动态CMS本体没有任何关系,就算缓存到极致,直接用的是静态页面,也一样要加载这些资源,一样要耗费时间。
这里插一句:很多人测缓存后的性能提升,起手就是ab,输入页面地址,开114线程,发514请求,最后列出来一看,喔,性能高了多少多少倍。但问题在于,压测工具不能反映真实用户体验,它只加载HTML,不加载其他资源,不执行js,也不渲染画面,测出来的数据与真实感受差了十万八千倍。有可能实际上页面生成只需要50ms,但上面说的那些资源加载完毕却需要3s,浏览器渲染又要300ms,总体加载时间是3.35s,现在加上缓存,页面生成时间变成1ms,快了50倍,但总体的页面加载时间是3.301s,有什么区别?
又比如,部分博客站点可能托管在较为低价的机房。购买此类服务器的时候,看到宣传说『高频多核心CPU,大容量内存』,我们假设商家确实没有虚假宣传,真的给你一台很牛逼的16C32G的服务器来托管博客,然而买来仔细一看,机房位于罗马尼亚,去程直连250ms,回程绕美300ms,已经足以触发TCP LFN问题,SSH打个字要卡半天,更别提还要慢吞吞建立TLS连接。这种情况下,速度瓶颈就是访客和服务器的网络连接质量了,半路出了问题,在源站使劲搞缓存又有什么用呢?
这还仅仅只是两个简单的例子,继续往下挖还能有很多因素,例如,页面资源是否有卡住渲染导致长时间白屏的情况?是否有外部资源(例如各类js/css库的第三方CDN)加载缓慢的情况?服务器网速是否有跟不上的情况(尤其是各种1Mbps的国内服务器)?等等等等。如果只盯着缓存看,恐怕优化到全站静态化了,速度提升还是寥寥无几。
3.2 莫非定律
缓存两个字,说来容易,做来难。
从技术上来看,缓存一旦生成,不主动清除或被动过期,就会一直生效,动态程序也就一直不会执行。这本来就是缓存的设计目标,也是很多站长关注的重要因素:想着自己的站点上,博文写好之后就几乎不更新了,首页也仅仅只是更新文章的时候才有变化,于是放心地上了缓存。然而,使用了一段时间之后,一些小问题就浮现了出来:
- 更新文章/评论,不刷新缓存
- RSS,Sitemap 有延迟
- 页面计数器不准确,或直接失效
- 即使接口不缓存,评论系统也失效
- 评论系统出现了别人的评论信息
- set-cookies 不小心被缓存了,共享登录信息
出现以上问题的时候,你是会怀疑缓存,还是怀疑其他部分,还是一起怀疑?
有个东西叫墨菲定律,意思是『当一个东西有出错的可能性时,就一定会出错』。这里稍微扩展下,叫他『莫非』定律:碰到问题,你会怀疑各个地方,莫非是这里,莫非是哪里,莫非......这并不是说缓存就一定要不得,而是说,这不是简简单单就能适配好的东西。
缓存,本质上还是对原有的系统,在多个角度上实现修改,于是整个就会牵一发而动全身。比如说,要实现更新后清除缓存,就需要把插件挂在准确的Hook点上,所以你要找到更新文章的Hook,添加评论的Hook,添加页面的Hook,分别加上,一个都不能漏。假如某天Typecho更新,增加了卖可乐功能,你就要找到卖可乐的Hook,也给加上,否则访客来访之后,页面缓存没有被刷新,于是访客杯子里就不能接满可乐。
再比如,评论系统好像挺人畜无害的,评论区缓存,接口不缓存,评论后刷新缓存,不就不影响正常评论了吗?但如果真要这么做的话,部分评论系统就要罢工了,不论怎样都提交不了。你打开源码一看,这个评论区是这么实现的:
<form action="/archive/<id>/comment" method="post">
<!-- 下面仨哥们对缓存没有太大意见 -->
<input type="text" name="nickname">
<input type="text" name="email">
<textarea name="comment"></textarea>
<!-- 我们中出了一个叛徒 -->
<input type="hidden" name="csrf_token" value="some-random-value">
<!-- 他对缓存也没有意见 -->
<button type="submit">提交评论</button>
</form>csrf_token就是用来防止CSRF(跨站请求伪造)的token,其值由后端程序动态生成。提交评论时,后端会检查此值是否与自己原来生成的值相等,如不相等,判定为非法请求,拒绝添加评论。很显然,如果对这个页面使用全页缓存,这个原本应该要随机生成的值就会被缓存下来,而后端也没有执行相应的生成逻辑。于是在后续的流程中,系统就会拒绝合法的评论请求。
要规避这个问题,目前主流的方法是做动静分离,也就是页面该缓存的缓存,该动态生成的,就通过js跑fetch请求,实时获取最新数据。又或者,部分系统会做SSI(服务器包含)或者ESI(边缘包含),在缓存好的页面中留下占位符,以便实时替换。反正最终的目的就是,确保静态页面中的部分动态元素能得到实时更新。
3.3 『插件』作用大?
有的朋友可能会说,我不想考虑这么多,我就找个插件装上,让他帮我代劳。尤其是WordPress那种,甚至有官方版缓存插件WP Super Cache,这么多用户使用,兼容性问题应该是不大的。
当然,这个说法没有太大问题,不过有一点必须要注意一下:缓存插件所谓的『高兼容性』,或者『普通模式』,是让所有逻辑都交给PHP来处理,保留一定的动态能力,因此上述所说的边边角角情况都能很好地规避过去,缓存也就没有问题。但另一方面,缓存插件,作为一个插件,是需要等CMS核心启动之后才能运行的,也就是说,CMS核心仍然需要连接数据库,加载基本框架,看看插件有没有启用,看看这个Hook点有没有Hook,最后才把控制权交给插件代码。所以资源消耗虽然会有减少,但因为PHP-FPM还会启动进程来处理请求,脚本还会建立数据库连接,部分核心代码还要运行,这部分的性能开销无法消除。
所以这就是又一个需要权衡利弊的点:想要缓存效果好,就需要做各种优化,比较费力;光使用一个插件,缓存效果是有的,但比不上前面的各种优化后的结果。当然,如果只是想安安静静写博客,那么缓存插件也不失为一个较好的选择,毕竟能节约掉生成页面的资源消耗,总比完全没有缓存要好。
3.4 缓存命中率?
即便是你想省事,如果可以的话,笔者也建议你关注一下缓存命中率的问题。
设想这么一个场景:缓存插件为了避免串用户,设置为对登录用户不缓存,只对游客缓存。然而,插件并没有接入CMS的登录体系,只能通过有无cookies,来判断用户是否登录。此时,另外安装的某个插件,为了方便自身使用的方便,往Cookies里写了点东西(最经典的,只要用到了PHP的Session,就会存在PHPSESSID)。于是就是这个无心之举,让一大片页面的缓存命中率瞬间归零,缓存插件装了等于没装。
部分缓存插件允许你修改判定字段,例如排除掉PHPSESSID,或只把特定的cookies认为是登录cookies,其他的照样缓存。不过,这毕竟也是一个需要关注的点,有可能下次安装什么别的插件,又会出现漏缓存的情况。目前来看,解决这个问题的一个方法,就是对站点做变更后,模拟正常访客,手动检查一下各页面的缓存状态,如返回的状态标头是否为HIT而非BYPASS。除此之外的方法,因笔者并未使用相关缓存措施,因此并不是很了解。有需要的朋友,可以自行查阅相关的一些资料,这里不再赘述。
除此之外,还有一些别的问题可能会降低缓存命中率。这里再举一个例子:路径覆盖不完全。假如,设定为对/feed做缓存,结果部分客户端访问的是/feed/,或者/feed/atom等(不同协议),如果核心处理不当的话,上述路径都可以返回正确内容,但只有第一条路径是被缓存的,其他都触发了动态构建,降低了缓存命中率。
3.5 微微地缓存一下
最后再介绍一个比较反直觉,但在某些场景下也有用的缓存策略:微缓存(Microcaching),也就是设置极短的缓存时间,例如首页缓存5秒,文章页缓存10秒,RSS缓存30秒,其他配置项(管理员页绕过缓存等)和普通缓存一样。
乍一看,这样短时间的缓存似乎没有什么意义,尤其是访客稀少的情况下,来访的时候缓存早就失效了,和没缓存没区别。但如果是热门站点,比如新闻网站,不停有访客访问,事情就会变得很微妙:比如,5秒内来了100个首页请求,这100个请求里面只有一个会打到真正的后端上,其他的99个请求都可以从缓存拿,这就降低了不少性能开销,而且几秒钟的延迟也可以接受。再比如,如果订阅RSS的人足够多的话,即使每个阅读器都是一小时拿一次订阅,叠加起来也会有性能高峰期(都是整点拿)或者持续的访问周期(错开时间拿),如果缓存30秒,就可以在不落后进度太久的同时,吸收掉整点更新的高峰期,以及把平时的连续访问控制在30秒刷一次,也能大大降低后端压力。
而且还有个好处:不必太担心忘记清缓存的事情,反正忘了也只是几秒钟时间,一眨眼就过去了,所以可以作为应急兜底方案,不至于造成太大的问题。
4. 写在最后
这篇文章其实很早之前,有意识去优化博客性能的时候,就打算动笔了。但研究了很久,实验了几次,才最终确定了博文主体内容。
也许,以后也会应用一部分到博客上吧。
不说了,今天六一国际儿童节,祝各位小朋友节日快乐。以及,崩铁更4.3版本了,溜了溜了,赶紧过剧情去了,先停笔几天。
(完)
我现在的架构就是由香港的前端机Nginx全量缓存文章页,仅在有新文章/修改文章,文章被评论后,通知后端生成一份新的对应页面缓存(写了一堆hook点)。顺带一提对于评论防SPAM的token,确实和你说的一点,如果按照原生状态直接就炸了,所以我改成用JS生成一个时间戳,并提交后端页面打开时刻和提交评论时刻的时间差值,后端发现小于 6 秒的直接干掉。为了这套缓存架构也是折腾1个多月,前端机的Nginx配置,后端机的Nginx配置,后端WP的对应代码,磨了好久才算勉强能用了。并不存在装一个缓存插件就能搞定一切的情况。
是的,『并不存在装一个缓存插件就能搞定一切的情况』,这个也是我的观点之一,主题不同,细节实现不同,硬要套用同一个插件,必然会碰到各种小问题。(而且,硬要说的话,假如真的可以一劳永逸,为啥CMS不直接集成进去,而是要让用户自己来选择呢?)
而且我目前也在顾虑一点:Nginx算是基础设施部分了,如果贸然修改,可能又会跟上次一样炸站(切换官方源更新后配置文件被覆盖了),所以我还在评估如何加缓存,或者和你一样,用一台香港前端机(手头有便宜机器)来做高速缓存。
不过这也从侧面说明了一点:网站加载提速并不一定只有『添加缓存措施』这一条路子,目前站内缓存措施可以说是跟没有差不多,但依然不影响其拥有可观的加载速度。
「换官方源更新后配置文件被覆盖了」我上个月就这样炸了一次(估计有10分钟),前端机之前用的PPA源,最近源无了,只能切到官方源,结果官方源配置文件的默认位置和命名变了,只能说幸好只是位置和命名变化,知道原因后,删了之前的软连接复制一份配置到正确位置并改名就好了。
你这个还好,我那个当时真的就是上Google现找默认配置项给顶起来了。只能说幸好Debian的习惯是单开一个目录放站点配置,而且被覆盖的也只是主配置,因此只要补上默认配置(主要是几个include),以及一些零碎部分就好了
看完了,学习到了不少,干货满满~
谢谢支持