【问题标题】:Why is CakePHPs cake_core_method_cache file getting bigger over time?为什么 CakePHPs cake_core_method_cache 文件会随着时间变大?
【发布时间】:2014-06-08 00:48:40
【问题描述】:

我们有一个相当大的 CakePHP(目前是 2.4.7)SAAS 应用程序。它已经相当成熟了,直到上周我们还没有遇到任何关于 CakePHPs-core 提供的功能的真正问题。

上周末我们遇到了一个非常令人担忧的情况,我们的应用程序变得非常缓慢且出现故障。我们查看了所有类型的服务器监控,发现 i/o 统计数据在过去几个小时内飙升。经过进一步调查,我们将问题隔离到 Cake 的“cake_core_method_cache”文件中。平均每天,此文件的大小为 200 kb。上周末,它上升到了 ~ 40 mb。

我们部署例程的一部分是从 app/tmp/cache 和 app/tmp/persistent 文件夹中删除所有缓存文件。因为我们经常部署(一天几次),所以缓存会经常被清除。不幸的是,上周的假期暂停了我们的部署,看起来这是核心/缓存功能的问题。

所以,我的两个问题是:

a) 有谁知道,为什么 cake_core_method_cache 会随着时间变大?

b) 是否有任何其他方法可以解决这个问题,比如不断删除这个文件?

【问题讨论】:

  • 这听起来不像是一个正常的场景——你可以通过查看文件的内容找到一些信息。例如,相同的内容是否重复了多次?与正常/小状态相比 - 是否充满了不相关的垃圾?
  • 看起来有模型的数据库实例直接存储在这个文件中——当然是序列化的。我不知道这些数据为什么以及如何到达那里。

标签: php cakephp ubuntu cakephp-2.4


【解决方案1】:

这个缓存是如何使用的

CakePHP 中对 method_cache 的唯一引用是在 Dbo Source 中。这是一个在该类中广泛使用的缓存,用于缓存(relatively) expensive quoting operations 的结果,或者如a relevant docblock 所示:

缓存来自查询解析操作的结果。 DboSource::name() 和 DboSource::conditions() 的缓存结果都将存储在这里。

考虑以下外壳:

<?php

class SoShell extends AppShell {

    function main() {
        $this->Post = ClassRegistry::init('Post');

        $this->Post->find('all', array(
            'conditions' => array(
                'find-this-text' => 'x'
            )
        ));

        debug(DboSource::$methodCache);
    }

}

运行它,产生以下输出:

> Console/cake so
########## DEBUG ##########
array(
    'name' => array(
        'f5442d1f57be5d9d8ac80ae9613d5ff9' => '`database_name`',
        'cfacfed443d6f30e67edf9bbcb06ce30' => '`posts`',
        'e13d78515036382daf55fa083088c902' => '`Post`.`id`',
        'aafd056e6d53878a75ab4ee3f18645a1' => '`Post`.`title`',
        '4084e974416e3a7fb06e6a280721637b' => '`Post`.`body`',
        'b4f1ad1de4cdc3a3f82e905f8dfd8673' => '`Post`.`created`',
        'bfeffce70e344d7606e17c9ff24530b5' => '`Post`.`modified`',
        'e42609d9744a7d1ce80c8d838b9ea07c' => '`find-this-text`', # <-
        'c0a33fa0f04ac4682dcdc4b25167b3a8' => '`Post`'
    ),
    'fields' => array(
        '952d56c2bf037c8195bdd5fba57b1024' => array(
            (int) 0 => '`Post`.`id`',
            (int) 1 => '`Post`.`title`',
            (int) 2 => '`Post`.`body`',
            (int) 3 => '`Post`.`created`',
            (int) 4 => '`Post`.`modified`'
        )
    )
)

请注意,缓存包含“用户”(或开发人员)输入,该数组是写入缓存 at the end of a request 的内容。

这个例子还应该强调为什么缓存会随着时间增长 - 缓存依赖于迄今为止发出的查询,随着时间的推移,更多的查询排列由应用程序发出,导致方法缓存增长(但通常不会很多MB)。

轻松修复

对所描述的问题有一个非常简单的解决方法:禁用方法缓存。

即将以下内容放在您的应用程序中的任何位置:

DboSource::$cacheMethods = false;

这当然意味着性能下降,因为不会使用方法缓存,因此每个请求都会发生相对昂贵的基于 preg 的引用操作。

更好的修复

如果缓存被“模型的 db-instances”填充 - 很可能某处存在应用程序问题。这是不正常的,它不应该发生,并且完全取决于应用程序正在做什么。鉴于无法给出具体的解决方案。

但是,如果您可以识别缓存中包含大量数据的任何特定键,则可以使用它来查找负责的查询。例如:

// anywhere
if (isset(DboSource::$methodCache['name']['e42609d9744a7d1ce80c8d838b9ea07c']) {
    // a query with find-this-text has been issued somewhere
}
    

您可以(临时)将这种逻辑添加到where the dbo methodCache property is modified 以便能够“原位”检测修改:

public function cacheMethod($method, $key, $value = null) {
    if ($this->cacheMethods === false) {
        return $value;
    }
    if (!$this->_methodCacheChange && empty(self::$methodCache)) {
        self::$methodCache = Cache::read('method_cache', '_cake_core_');
    }
    if ($value === null) {
        return (isset(self::$methodCache[$method][$key])) ? self::$methodCache[$method][$key] : null;
    }
    $this->_methodCacheChange = true;
    
    // Added
    if ($method === 'name' && $key === 'e42609d9744a7d1ce80c8d838b9ea07c') {
        App::uses('Debugger', 'Utility');
        $this->log("Query with find-this-text in it issued", "wat-debug");      
        $this->log(Debugger::trace(), "wat-debug");     
    }
    // Added end

    return self::$methodCache[$method][$key] = $value;
}

这将允许您确定直接负责将大量数据注入方法缓存的原因;然后更正应用程序代码,以便可以按设计启用和使用方法缓存。

【讨论】:

  • 感谢您的帮助,您成就了我的一周!阅读您的答案后,我检查了我们的应用程序,并在我们的一个 SQL 查询中发现了一个“问题”。而不是有一个 'Model.created BETWEEN ? AND ?',我们有一个 'Model.created BETWEEN '$DATE' AND '$DATE'。看起来,使用第二种语法,我们放入查询中的每个 $DATE-value 都缓存在 cake_core_method_cache_file 中。
  • > DboSource::$cacheMethods = false;做这件事:Access to undeclared static property: DboSource::$cacheMethods 在 CakePHP 2.x 中没有声明为静态 => 这里的正确程序是什么?
  • 获取对该实例的引用 - 和 change the value 标记
  • 但是如何在不强制创建连接的情况下获得它?因为它不是静态的,所以你不能将它设置为全局,并且在像 boostrap/config 这样的常见地方,连接还没有建立。该应用程序可能有多个入口点(控制台、控制器),我不确定该放在哪里。多个数据库连接也没有让这更容易,似乎我错过了什么?
  • 我没有任何 2.x 蛋糕项目可供研究 - 请在您弄清楚时更新答案,因为 php 的行为已经改变 :)
【解决方案2】:

对于最新的旧 CakePHP 2 版本,来自https://stackoverflow.com/a/23287510/47573 的建议修复对我不再有效;该属性不是静态的,因此您不能简单地覆盖其他地方。

我想出的解决方案是创建一个自定义的数据库驱动程序,扩展我使用的 CakePHP 驱动程序:

  • 创建app/Plugin/PostgresCustom/Model/Datasource/Database/PostgresCustom.php
  • 该类扩展了 Cakes Postgres,后者又扩展了定义此属性的 DboSource
  • 在我的PostgresCustom 类中,我只需通过public $cacheMethods = false; 覆盖该属性
  • app/Config/bootstrap.php我通过CakePlugin::load('PostgresCustom');注册插件
  • app/Config/database.php 中我将datasource 的值更改为PostgresCustom.Database/PostgresCustom
  • 利润?

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-01-07
    • 1970-01-01
    • 2021-05-02
    • 1970-01-01
    • 1970-01-01
    • 2014-02-05
    • 2011-12-16
    • 1970-01-01
    相关资源
    最近更新 更多