目录
缓存介绍
早期cpu,内存设计上都有缓存
存储将被用到的数据,让数据访问更快
布隆过滤器(效率更高,牺牲缓存命中率)
- 命中:在缓存中找到了请求的数据
- 不命中/穿透:缓存中没有需要的数据
- 命中率:命中次数/总次数
- 缓存大小:缓存中一共可以存多少数据
- 清空策略:如果缓存空间不够数据如何被替换
清空策略(FIFO)
1.先进先出
思考:如果是Javascript缓存,用Map还是Array(Map易读取,Array易清空)
2.LFU-Least Frequently used
优先清除命中次数少的,根据使用频率
内部实现用数组还是优先级队列?(量大的话用数组遍历效率低,可以考虑用优先级队列)
3.LRU-Least recently used
优先清除太久没有使用的,保留最近被使用的缓存,更新最近调用时间
思考:内部实现用数组还是优先级队列?
实战:fifo的memory函数
缓存成立的环境,会做哪些事情
通常是在浏览器上,在端上,整体缓存设置大小限制的
// 先进先出实现方法
function memory(f, maxSize = 10) {
// [{hash, value}]
const cache = []
return (...args) => {
const hash = args.join(',')
const item = cache.find(x => x.hash === hash)// 循环遍历,找到x.hash === hash 的x
if(item) {
return item.value
}
const result = f(...args)
cache.push({
hash,
value: result
})
if(cache.length > maxSize) {
cache.shift() // 移除第一个值
}
return result
}
}
// 1 1 2 3 5 8 13
// 斐波那契数列(Fibonacci sequence),
// 又称黄金分割数列
function fib(n) {
if(n === 1 || n === 2) {
return 1
}
// 递归,前面2个数的和
// 因为是递归函数所以,下面mfib用外面那个使用缓存的mfib
return mfib(n-1) + mfib(n-2)
}
const mfib = memory(fib, 10)
2^n // 2的n次方
console.log(fib(40))
实战:LRU算法
缓存越来越多的话,会采取的策略
更新最近调用时间
// 优先清除太久没有使用的,保留最近被使用的缓存,更新最近调用时间
function memory(f, maxSize = 10) {
// [{hash, value}]
let cache = [] // 重新附过值用let
//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,minItem
min = item.time
minItem = item
}
}
cache = cache.filter(x => x !== minItem) // 保留除了时间戳最小值的数据
}
return result
}
}
// 1 1 2 3 5 8 13
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(40))
建议用优先级队列,更新一下,上面方法里的on循环
HTTP缓存
Cache-Control
定义所有缓存都要遵守的行为
可缓存性
- public:允许所有方缓存
- private:只允许浏览器缓存
- no-cache:每次必须先询问服务器资源是否已经更新
- no-store:不使用缓存
缓存期限
- max-age:秒(存储周期)
- s-maxage:秒(共享缓存如代理等,存储周期)
强制缓存
强制使用缓存,不去服务器对比;(缓存生效不再发送请求)
Cache-Control: max-age=600(多用这个)
Expires:(用的少)
const express = require('express')
const app = express()
app.get('/x', (req, res) => {
// max-age=0 === no-cache
res.set("Cache-Control", 'max-age=600') // 强制缓存,一般用于一定时间内不会变的静态文件
res.send("x6")
})
app.listen(3000)
// fetch("/x") // Code: 200 OK (from disk cache)
请求页面,请求接口会用304协商缓存no-cache
协商缓存
协商使用缓存,每次需要向服务器请求对比,缓存生效不传回body
返回: Last-Modified:
请求:If-Modified-Since:
const express = require('express')
const app = express()
app.set('etag', false)// etag也是一种协商缓存,关掉干扰因素
app.get('/x', (req, res) => {
res.set("Last-Modified", 'Tue Sep 28 2021 23:41:43 GMT+0800') // 协商缓存,数据更新的最后时间,比较好用
res.send("x6")
})
app.listen(3000)
协商缓存-2(用的最多的)
返回:E-Tag:1234567
请求:If-None-Match:1234567
小结
- 发布新的静态资源的时候,如何更新缓存?
1.每次发布的静态资源文件名都不同(大厂最多策略)
- HTTP缓存有大小限制吗?FIFO还是LRU
HTTP有大小限制,用端的时候要和端上的同学协商用多大的缓存,
浏览器有自己的限制;CDN也会有限制,只是通常触发不到