bigywater.tech
Flask · 75

Flask-Caching 缓存机制

Flask-Caching 的缓存键是怎么来的?查询参数会不会串?多 worker 部署时 SimpleCache 还管用吗?缓存刚过期时并发请求会怎样?这篇用真实的 gunicorn、自己起的 redis 和计时把这些逐项量出来。

53 → 0.6 ms
未命中与命中的请求耗时(慢计算 50 ms)
2 个进程
3 个 worker 下 SimpleCache 这次在 2 个进程里各算了一次
8 次
缓存过期瞬间 8 个并发请求触发的慢计算次数

cached 的命中与过期

实测 · 本机执行输出
  连续 3 次: [('cached:call#1:pid59551', 54), ('cached:call#1:pid59551', 1), ('cached:call#1:pid59551', 1)]
  过期后(2.2 秒后):cached:call#2:pid59551(56 ms)
  不缓存的 /plain 连续 2 次: ['plain:call#3:pid59551', 'plain:call#4:pid59551']

上一篇:Flask-Migrate 基础;下一篇:Flask-Limiter。

查询参数与缓存键

实测 · 本机执行输出
  默认(query_string=False): ['qs:1:call#1:pid59571', 'qs:1:call#1:pid59571']
  query_string=True      : ['qs:1:call#2:pid59571', 'qs:2:call#3:pid59571']

memoize

实测 · 本机执行输出
  memo(1)、memo(1)、memo(2)、memo(1, y=5): ['memo(1,1):call#1:pid59582', 'memo(1,1):call#1:pid59582', 'memo(2,1):call#2:pid59582', 'memo(1,5):call#3:pid59582']
  delete_memoized(compute, 1) 后 memo(1): memo(1,1):call#4:pid59582 | 另一个参数组合 memo(1, y=5) 仍命中: memo(1,5):call#3:pid59582

后端存储

实测 · 本机执行输出
  redis 里的键: ['lab_app.compute_memver', 'lab_vfv01u2GF+lT1QdbyvVSNb', 'lab_view//cached']
  视图缓存键 lab_view//cached | TTL = 2 秒 | 值的前几个字节: b'!\x80\x05\x95\x1a\x00\x00\x00\x00\x00\x00\x00\x8c\x16cached'
  FileSystemCache 目录里的文件: ['165d5f7b72af…', '79bc207ee04a…']

本篇的 redis 是自己在非默认端口起的临时实例,验证完就关掉了。

多个 worker

实测 · 本机执行输出
  SimpleCache  3 个 worker、12 个相同请求:不同的响应内容 2 种(= 被计算了几次);响应来自 2 个不同进程
  RedisCache   3 个 worker、12 个相同请求:不同的响应内容 1 种(= 被计算了几次);响应来自 1 个不同进程

缓存击穿

实测 · 本机执行输出
  缓存过期后 8 个并发请求:慢计算被执行了 8 次(总耗时 0.32 秒);8 个响应里不同的 call 编号 1 个

实验里响应正文里的 call 编号看起来都一样,是因为我的计数器在慢计算结束后才读取,所以这里只用「计算被执行了几次」这个计数,不用响应内容判断。

命中与未命中的耗时

实测 · 本机执行输出
  未命中(计算 50 ms)中位数 53.1 ms;命中中位数 0.59 ms

分步演示

Flask-Caching
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / Python 3.14.7 / Flask 3.1.3 / Werkzeug 3.1.9 / Jinja2 3.1.6 / SQLAlchemy 2.1.1 / gunicorn 26.2.0(都装在自己的虚拟环境里);大多数实验用 Flask 的 test_client(直接调用 WSGI 应用,不经过网络),需要真实 HTTP 时另起 gunicorn 或开发服务器(端口 9600 以上,本机 127.0.0.1);计时用 time.perf_counter,数字每次运行会有小幅抖动。Flask-Caching 2.5.1、redis-py 8.1.0、自己起的 redis(非默认端口)
    • 没有验证:Memcached 后端、cache.cached 的 unless/make_cache_key 自定义、CACHE_KEY_PREFIX 在多应用共用 redis 时的冲突、缓存雪崩、加锁防击穿的具体方案
    京ICP备2026067502号-1京公网安备11010502063487号