跳至主要內容
  • Hostloc 空間訪問刷分
  • 售賣場
  • 廣告位
  • 賣站?

4563博客

全新的繁體中文 WordPress 網站
  • 首頁
  • PHP 做数据缓存时遇到一个不停写入缓存的问题,该怎么解决?
未分類
5 2 月 2021

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

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

資深大佬 : frozenway 5

//获取缓存         $tmp = unserialize(file_get_contents('tmp.txt'));         //得到以下缓存数组         $tmp = [             'a' => 'qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq',             'b' => 'wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww',             '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 缓存被不停清空又不停写入         //这就缓存的使用目的了         //这种情况该怎么解决 

大佬有話說 (12)

  • 資深大佬 : chaodada

    加锁啊

  • 資深大佬 : Makoto

    缓存读的时候无限制,写之前检查 /加锁,每次只允许一个线程写

  • 資深大佬 : setsunakute

    加锁 可以用 flock 函数
    https://www.php.net/manual/zh/function.flock.php

  • 資深大佬 : sagaxu

    写临时文件再 rename 过去,rename 是原子操作

  • 資深大佬 : lovecy

    多进程条件下保持唯一性累不累啊。。
    1. 用一个定时进程去刷新缓存文件
    2. 用一个常驻进程去处理所有的缓存读取操作(对,就是让你上 Redis )

  • 資深大佬 : emeab

    缓存雪崩 加锁把.

  • 主 資深大佬 : frozenway

    @Makoto @setsunakute @sagaxu @lovecy 我现在用的是 thinkphp5.1 的 cache 的 file 模式去写缓存,看了源代码,也存在这种情况,好无助

  • 主 資深大佬 : frozenway

    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 = “<?phpn//” . 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

    一下线程,一下进程,搞懵了,fpm 模式不就是多进程单线程吗?

  • 資深大佬 : wangritian

    最佳解决方案:redis

  • 資深大佬 : zzhpeng

    还有,为什么这样设计呢?数组序列化存储,反序列化更新。string 存储不就好了,独立开来。案例来看,你的数据独立性挺强。

  • 資深大佬 : sujin190

    一个 key 一个文件不好么。。

文章導覽

上一篇文章
下一篇文章

AD

其他操作

  • 登入
  • 訂閱網站內容的資訊提供
  • 訂閱留言的資訊提供
  • WordPress.org 台灣繁體中文

51la

4563博客

全新的繁體中文 WordPress 網站
返回頂端
本站採用 WordPress 建置 | 佈景主題採用 GretaThemes 所設計的 Memory
4563博客
  • Hostloc 空間訪問刷分
  • 售賣場
  • 廣告位
  • 賣站?
在這裡新增小工具