V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
gouchaoer
V2EX  ›  PHP

关于 cache 的 expire 问题

  •  
  •   gouchaoer · 2018-02-19 16:40:26 +08:00 via Android · 3129 次点击
    这是一个创建于 2467 天前的主题,其中的信息可能已经有所发展或是发生改变。
    给应用设置一个 有 expire 的 cache,此时系统流量很大而且每个请求都用到了这个 cache,那么到了时间在 cache 过期的瞬间就会有许多请求打到 db 层,直到某个请求成功从 db 返回结果从新设置 cache 为止,为什么大家允许这种惊群效应?如果让我来设计 cache,我一定在 cache 过期之后只给某个请求去 db 更新缓存,别的请求继续用旧 cache
    15 条回复    2018-02-27 10:19:26 +08:00
    jhdxr
        1
    jhdxr  
       2018-02-19 22:05:28 +08:00
    知乎:先问是不是再问为什么

    这么设计然后造成 DB 被打爆的架构师早就被拖出去剁了好么。。。
    gouchaoer
        2
    gouchaoer  
    OP
       2018-02-20 00:03:14 +08:00 via Android
    @jhdxr php 的一堆框架的 cache 组建里都没考虑这种情况好吧
    chenxytw
        3
    chenxytw  
       2018-02-20 11:40:23 +08:00
    这得分离出一个单独的 cache 服务来处理这个逻辑吧 0 0
    有些业务系统对于这种是分出两层来,一层专门做业务逻辑,一层专门做数据服务;
    业务逻辑层从数据层取数据,不管数据层是从哪里取的;
    而数据层来维护如贴中所说的缓存逻辑。
    gouchaoer
        4
    gouchaoer  
    OP
       2018-02-20 11:50:55 +08:00 via Android
    @chenxytw 搜到这篇文章讲了这个问题: https://huoding.com/2015/09/14/463
    gouchaoer
        5
    gouchaoer  
    OP
       2018-02-20 11:57:47 +08:00 via Android
    实际上 apcu 也有原子的设置一个 expire 的缓存的 apcu_add,用这个就可以实现只有一个请求去更新缓存的策略,而这一切完全可以封装到原来的缓存中不用改一行逻辑代码
    MeteorCat
        6
    MeteorCat  
       2018-02-21 10:20:18 +08:00 via Android
    这个问题区分看,如果是数据后台这种少数人管理的但是查询量大,过期所引起的 DB 层查询不足以让其崩溃;而大量请求接口级别,也需要区分,如登录接口这种单一用户请求,保存必然是 account:uid:1 这种单独某个用户的缓存信息,除非是这个用户不断恶意请求,否则该用户对于 DB 层面也不过是 where account='user'(一般有索引),基于这种情况对于 DB 层问题都不是太大,因为这是针对唯一性数据的处理;但是多人共享的数据就需要谨慎对待,同理在于抢票和限购这几种情况
    mengzhuo
        7
    mengzhuo  
       2018-02-21 11:29:00 +08:00 via iPhone
    楼主可以参考 redis 的实现,或者直接用 redis
    dangyuluo
        8
    dangyuluo  
       2018-02-21 12:27:38 +08:00
    如果你非得这么想的话,那么换个思路,每次请求都查询一下缓存的 TTL,10s 之内算作一个限。第一个在倒数 10s 内访问这个 cache 的,将另外一个 flag 标记为 1,然后开始从数据库里读并更新缓存,这个操作甚至可以是 block 的。其余的访问先检查下 flag,如果为 1 的话说明有别的程序正在更新缓存,我还是返回这个生命不到 10s 的缓存。
    很蹩脚,不错没办法
    sagaxu
        9
    sagaxu  
       2018-02-21 13:42:49 +08:00 via Android
    “系统流量很大而且每个请求都用到了这个 cache ”

    这种超热数据,一般量是不会太大的,我们会把它预先读入进程内 cache,用共享内存,然后定时从 db 读取刷新这些 cache,根本不走 redis,也不存在过期失效。
    gouchaoer
        10
    gouchaoer  
    OP
       2018-02-21 15:39:49 +08:00 via Android
    @sagaxu 用一个命令行程序去刷新可以啊,可是逻辑写在一起比较好吧,而且增加运维复杂度。。
    inkedawn
        11
    inkedawn  
       2018-02-21 16:42:55 +08:00
    重建过程加锁
    or
    缓存单独更新
    lolizeppelin
        12
    lolizeppelin  
       2018-02-22 01:01:52 +08:00 via Android
    可以抄 mysql query cache 实现
    msg7086
        13
    msg7086  
       2018-02-22 01:26:22 +08:00
    流量很大还用过期 hit DB 设计?架构师怕不是施乐纸。
    难道不是应该有一个第三方服务来监视生命周期然后主动发起数据刷新的吗?

    至于你说 PHP 框架的,PHP 程序本来就是设计成非持久运行的,做不到完美更新。
    你要是旁路跑一个程序专门监视生命周期,那什么都好说。

    就这么裸着撸 PHP 框架里的 cache 层的应用,本来就流量小到不可能惊群。
    sagaxu
        14
    sagaxu  
       2018-02-22 02:21:34 +08:00 via Android
    @gouchaoer php 里是不大好弄,其它语言里随便开个定时器就搞定了。php 碰到这种需求,只能用 swoole 这类方案了,2.0 开始大量借鉴了 Go 的特性。单进程的优越性在这种场景下特别明显。
    puritania
        15
    puritania  
       2018-02-27 10:19:26 +08:00
    换种思路? cache 永不过期,数据变更时你主动更新 cache。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   968 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 21:35 · PVG 05:35 · LAX 13:35 · JFK 16:35
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.