联系支持

我们通过电子邮件回复,通常两天内。

为防止滥用,Google reCAPTCHA 会检查这次提交,其间会向 Google 传输数据。脚本只在你打开此表单时才加载。

← 全部文章

住在内存里的缓存

本服务绘制的计数器图片里,将近五分之一已经什么都不改变了。上一代留下的鼠标追踪脚本会在每一次移动时重新加载图片,而自从这些请求不再让任何数字增加,连着两次就会产出同一张图片,逐字节相同。

这是缓存的一个明显候选。有意思的是两个决定:把它放在哪里,以及键该基于什么。

不放在磁盘上

这跑在一台树莓派上,而磁盘是一张 SD 卡。把画好的图片缓存到那里,意味着每天大约 70 MB 的写入,换来的是实测每天 1,6 分钟的 CPU 时间

这是一笔糟糕的交换。SD 卡是被写坏的,而买到手的不过是一台并不受 CPU 限制的机器上的舍入误差。所以缓存住在 /dev/shm 里,那是 tmpfs,也就是内存。在那里实测:读 0,027 毫秒,写 0,036 毫秒,剩余 1,9 GB。

重启之后一切归零。对缓存来说这不是损失,而是常态。

同一台树莓派上跑着 Redis。没有采用它,理由和技术毫无关系:它属于另一个应用,并为那个应用设了密码。共用一个实例会把两个不相干的服务绑在一起,让其中一个的坏日子变成另一个的坏日子。

键里含着那些数字

通常的缓存键是一个身份加一个过期时间,而过期时间是一场赌博:过去五分钟里没有重要的东西变过。对计数器来说,会变的东西恰恰就是要显示的东西。

所以显示的数值被放键里。数字一变,就是另一个键,图片重画。这场赌博直接消失了,而不是被小心地下注。每一次被计入的访问至少会让总计加一,所以任何被计入的访问都不可能拿到过时的图片。剩下的过期时间只是那些不在键里的数值的上界——周、月、年的数字。

动手之前必须先核实的事

缓存只有在所存之物是其键的函数时才安全。这是一个关于每一个样式的断言,而它是被核实过的,不是被假定的:

  • 没有任何样式使用 rand()mt_rand()shuffle()uniqid()
  • 没有任何样式读取比小时更细的时钟
  • 旧脚本的那些追踪参数在整棵代码树里都没有被读取过

最后这一条造出了整件事里最漂亮的一次失败。当那些参数还是键的一部分时,每个请求带着略有不同的鼠标坐标,于是每个键都是独一无二的。缓存整整跑了一天,记录到 13 次命中。它运转得完美无缺,什么也没做,而这是最难被察觉的一种坏。

而如果这里有任何环节出问题——目录不可读、磁盘写满、条目损坏——图片就照常绘制。缓存绝不能成为计数器不出现的原因。

广告