缓存

2022-10-11 计算机网络 大约 3 分钟

储存将被用到的数据,让数据访问更快。

# FIFO 缓存策略

fifo的缓存策略是一种先进先出的缓存方式。核心原则:如果一个数据最先进入缓存中,则应该最早淘汰掉。

function memory(f, maxSize = 10) {
  const cache = []

  return (...args) => {
    const hash = args.join(',')

    const item = cache.find(x => x.hash === hash)
    if(item) {
      return item.value
    }

    const result = f(...args)
    cache.push({
      hash,
      value: result
    })

    if(cache.length > maxSize) {
      cache.shift()
    }
    return result
  }
}

function fib(n) {
  if(n === 1 || n === 2) {
    return 1
  }
  return mfib(n-1) + mfib(n-2)
}

const mfib = memory(fib, 10)

console.log(fib(60))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

在不使用缓存函数时,如果你执行 fib(45) 你会明显感觉到等待的时间。但是如果你使用缓存函数 memory 那么等待时间基本无感知。

# LRU 缓存策略

LRU 表示以时间作为参考,淘汰最长时间未被使用的数据。其核心思想是:如果数据最近被访问过,那么将来被访问的几率也更高。

function memory(f, maxSize = 10) {
  let cache = []

  return (...args) => {
    const hash = args.join(',')

    const item = cache.find(x => x.hash === hash)
    if(item) {
      item.time = new Date().getTime()
      return item.value
    }

    const result = f(...args)
    cache.push({
      hash,
      value: result,
      time: new Date().getTime()
    })

    if(cache.length > maxSize) {
      let min = Infinity
      let minItem = null
      for(let item of cache) {
        if(item.time <min) {
          min = item.time
          minItem = item
        }
      }
      cache = cache.filter(x => x !== minItem)
    }
    return result
  }

}

function fib(n) {
  if(n === 1 || n === 2) {
    return 1
  }
  return mfib(n-1) + mfib(n-2)
}
const mfib = memory(fib, 10)

console.log(fib(60))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44

# HTTP 缓存

Cache-Control定义了所有缓存都要遵守的行为。

  • 可缓存性:
    • public: 允许所有方缓存;
    • private: 值允许浏览器缓存;‘
    • no-cache: 每次必须先询问服务器资源是否已经更新;
    • no-store: 不使用缓存;
  • 缓存期限:
    • max-age: 秒(存储周期);
    • s-maxage: 秒(共享缓存如代理等,存储周期);

常见用法:在下面这张图中,首页 index.html 不缓存,但是首页中加载的资源则需要配置缓存,react 可以设置长缓存,crypto2 可以设置仅浏览器缓存,中间商不缓存,其它经常更新的资源则可以设置较短时间的缓存。

1665813091045

# 强缓存

强制使用缓存,不去服务器对比;(缓存生效不再发送请求);缺点:服务器资源更新也获取不到最新的资源。

  • Cache-Control: max-age=600;
  • Expries: <最后期限>

两者二选一即可,推荐使用 Cache-Control

示例代码:

const express = require('express');
const app = express();

app.get('/x', (req, res) => {
  res.setHeader('Cache-Control', 'max-age=600');
  res.send("x6");
});

app.listen(3000)
1
2
3
4
5
6
7
8
9

启动该服务,然后访问 http:localhost:3000/x 可以看到第一次请求的状态码是 200 ,但是第二次是 304(Not Modified)却不是 200 OK (from disk cache)。具体原因尚未搞清楚。可以在控制台发起请求以查看http状态。

相关信息

强制缓存的优先级高于协商缓存。

# 协商缓存一

每次需要向服务器请求对比,缓存生效不传回body。

请求:If-Modified-Since: <昨天>

返回:Last-Modified: <昨天>

If-Modified-Since 小于 Last-Modified 时就会重新获取资源。

示例代码:

const express = require('express');
const app = express();

app.set('etag', false);
app.get('/x', (req, res) => {
  res.setHeader('Last-Modified', 'Sat Oct 15 2022 15:45:14 GMT+0800');
  res.send("x6");
});

app.listen(3000);
1
2
3
4
5
6
7
8
9
10

在第一次请求成功后,后面的请求浏览器就会带上 If-Modified-Since

# 协商缓存二

请求:If-None-Match: W/"2-6O5BPWxo2MyCyXszD2Gvajv3gP0"

响应:ETag: W/"2-6O5BPWxo2MyCyXszD2Gvajv3gP0"

第一次请求不会有 If-None-Match 之后的请求会携带返回的 ETag送给服务器。

上次编辑于: 2023年7月4日 09:36