PHP 做数据缓存时遇到一个不停写入缓存的问题,该怎么解决? - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
frozenway
V2EX    PHP

PHP 做数据缓存时遇到一个不停写入缓存的问题,该怎么解决?

  •  
  •   frozenway 2021-01-19 17:02:50 +08:00 2831 次点击
    这是一个创建于 1727 天前的主题,其中的信息可能已经有所发展或是发生改变。
    //获取缓存 $tmp = unserialize(file_get_contents('tmp.txt')); //得到以下缓存数组 $tmp = [ 'a' => 'qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq', 'b' => 'wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww', 'c' => 'eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee', 'd' => 'rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr', 'e' => 'tttttttttttttttttttttttttttttttttttttttttt', 'f' => 'yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy', 'g' => 'uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu', 'h' => 'iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii', ... ]; //更新缓存 $tmp['b'] = 'ooooooooooooooooooooooooooooooooooooooooo'; file_put_contents('tmp.txt', serialize($tmp)); //那么问题来了,多个线程在读取这个缓存的时候 //当线程 A 在更新缓存时,调用以下方法 $tmp['a'] = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'; file_put_contents('tmp.txt', serialize($tmp)); //而在线程 A 更新缓存的同时,线程 B 在读取 tmp 这个缓存 $tmp = unserialize(file_get_contents('tmp.txt')); //假设 A 线程执行更新缓存时,文件内容刚写入一部分,那么线程 B 读取出来的 //缓存数据是 $tmp = false; //那么线程 B 就会查数据库后得到数据后写入缓存 $tmp['b'] = 'ssssssssssssssssssssssssssssssssssssssss'; file_put_contents('tmp.txt', serialize($tmp)); //然后最终的问题来了 //如果设置这个缓存数据有效期 1 小时, //当几十上百个线程频繁访问这个缓存数据,如果不存在其中的 a 或 b 或其他键的值,就 $tmp['x'] = '......................................'; //x 为 abcdefg..的之中一个键 file_put_contents('tmp.txt', serialize($tmp)); //如果更新其中一个键的值时,刚写入一部分到 tmp.txt,那么其他线程读取 //缓存时得到的值是 false,那么又各自更新写入自己的缓存 //这么一来,只要有一个进程更新写入到 tmp.txt 文件而还没全部写完时,另一线程就 //读取,就会造成清空了所有缓存的情况,最后造成这个 tmp 缓存被不停清空又不停写入 //这就缓存的使用目的了 //这种情况该怎么解决 
    13 条回复    2021-03-18 11:35:55 +08:00
    chaodada
        1
    chaodada  
       2021-01-19 17:06:51 +08:00   1
    加锁啊
    Makoto
        2
    Makoto  
       2021-01-19 17:07:05 +08:00   1
    缓存读的时候无限制,写之前检查 /加锁,每次只允许一个线程写
    setsunakute
        3
    setsunakute  
       2021-01-19 17:08:21 +08:00   1
    sagaxu
        4
    sagaxu  
       2021-01-19 17:11:55 +08:00 via Android   1
    写临时文件再 rename 过去,rename 是原子操作
    lovecy
        5
    lovecy  
       2021-01-19 17:14:12 +08:00
    多进程条件下保持唯一性累不累啊。。
    1. 用一个定时进程去刷新缓存文件
    2. 用一个常驻进程去处理所有的缓存读取操作(对,就是让你上 Redis )
    2kCS5c0b0ITXE5k2
        6
    2kCS5c0b0ITXE5k2  
       2021-01-19 17:14:27 +08:00   1
    缓存雪崩 加锁把.
    frozenway
        7
    frozenway  
    OP
       2021-01-19 17:29:38 +08:00
    @Makoto @setsunakute @sagaxu @lovecy 我现在用的是 thinkphp5.1 的 cache 的 file 模式去写缓存,看了源代码,也存在这种情况,好无助
    frozenway
        8
    frozenway  
    OP
       2021-01-19 17:34:05 +08:00
    TP5.1 的
    ```
    /**
    * 读取缓存
    * @access public
    * @param string $name 缓存变量名
    * @param mixed $default 默认值
    * @return mixed
    */
    public function get($name, $default = false)
    {
    $this->readTimes++;

    $filename = $this->getCacheKey($name);

    if (!is_file($filename)) {
    return $default;
    }

    $cOntent= file_get_contents($filename);
    $this->expire = null;

    if (false !== $content) {
    $expire = (int) substr($content, 8, 12);
    if (0 != $expire && time() > filemtime($filename) + $expire) {
    //缓存过期删除缓存文件
    $this->unlink($filename);
    return $default;
    }

    $this->expire = $expire;
    $cOntent= substr($content, 32);

    if ($this->options['data_compress'] && function_exists('gzcompress')) {
    //启用数据压缩
    $cOntent= gzuncompress($content);
    }
    return $this->unserialize($content);
    } else {
    return $default;
    }
    }

    /**
    * 写入缓存
    * @access public
    * @param string $name 缓存变量名
    * @param mixed $value 存储数据
    * @param int|\DateTime $expire 有效时间 0 为永久
    * @return boolean
    */
    public function set($name, $value, $expire = null)
    {
    $this->writeTimes++;

    if (is_null($expire)) {
    $expire = $this->options['expire'];
    }

    $expire = $this->getExpireTime($expire);
    $filename = $this->getCacheKey($name, true);

    if ($this->tag && !is_file($filename)) {
    $first = true;
    }

    $data = $this->serialize($value);

    if ($this->options['data_compress'] && function_exists('gzcompress')) {
    //数据压缩
    $data = gzcompress($data, 3);
    }

    $data = "<?php\n//" . sprintf('%012d', $expire) . "\n exit();?>\n" . $data;
    $result = file_put_contents($filename, $data);

    if ($result) {
    isset($first) && $this->setTagItem($filename);
    clearstatcache();
    return true;
    } else {
    return false;
    }
    }
    ```
    也没加锁,会不会也有问题?
    zzhpeng
        9
    zzhpeng  
       2021-01-19 17:51:25 +08:00
    一下线程,一下进程,搞懵了,fpm 模式不就是多进程单线程吗?
    wangritian
        10
    wangritian  
       2021-01-19 17:57:32 +08:00   1
    最佳解决方案:redis
    zzhpeng
        11
    zzhpeng  
       2021-01-19 18:12:42 +08:00   1
    还有,为什么这样设计呢?数组序列化存储,反序列化更新。string 存储不就好了,独立开来。案例来看,你的数据独立性挺强。
    sujin190
        12
    sujin190  
       2021-01-19 18:25:05 +08:00   1
    一个 key 一个文件不好么。。
    yavana
        13
    yavana  
       2021-03-18 11:35:55 +08:00
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     5766 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 24ms UTC 06:33 PVG 14:33 LAX 23:33 JFK 02:33
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86