标签 PHP 下的文章

Lavarel Eloquent ORM常驻进程下的内存溢出问题

Laravel 的 Eloquent ORM 提供了漂亮、简洁的 ActiveRecord 实现来和数据库交互。每个数据库表都有一个对应的「模型」用来与该表交互。你可以通过模型查询数据表中的数据,并将新记录添加到数据表中。

业务里使用了ORM来进行数据库操作,有个cron运行一段时间后就报内存溢出的错误,之前一直没时间彻底解决,这次有时间彻底搞清楚了。

代码类似:

$capsule = new Capsule;

foreach ($database[$env] as $name => $conf) {
    // 创建链接
    $capsule->addConnection($conf, $name);
}

// 设置全局静态可访问
$capsule->setAsGlobal();

// 启动Eloquent
$capsule->bootEloquent();

while (1) {
    try {
        $service = new PostModel();
        $dataList = $service->where('status', 1)->limit(100)->get()->toArray();
    } catch (Exception $e) {
        continue;
    }

    if (!$dataList) {
        break;
    }

    foreach ($dataList as $data) {
        // do something
        // 最后会更新status字段=2
    }
}

断点调试,发现每查询一次数据,内存就会增长3M左右,运行30次左右,就会报内存溢出。
怀疑是不是ORM这里会记录每次的查询结果,搜索了一下,发现还真有类似功能。
Laravel Eloquent display query log

最终实现代码:

$capsule = new Capsule;

foreach ($database[$env] as $name => $conf) {
    // 创建链接
    $capsule->addConnection($conf, $name);
    // 关闭查询日志,常驻进程会导致内存溢出
    $capsule->getConnection($name)->disableQueryLog();
}

// 设置全局静态可访问
$capsule->setAsGlobal();

// 启动Eloquent
$capsule->bootEloquent();

while (1) {
    try {
        $service = new PostModel();
        $dataList = $service->where('status', 1)->limit(10)->get()->toArray();
    } catch (Exception $e) {
        continue;
    }

    if (!$dataList) {
        break;
    }

    foreach ($dataList as $data) {
        // do something
        // 最后会更新status字段=2
    }
}

让Lavarel Eloquent ORM 支持 mongodb数据库

Laravel 的 Eloquent ORM 提供了漂亮、简洁的 ActiveRecord 实现来和数据库的互动。 每个数据库表会和一个对应的「模型」互动。

组件说明

业务侧还在用PHP5.3,composer.json文件内容如下:

{
    "require": {
        "illuminate/database": "4.1.*",
        "jenssegers/mongodb": "1.3.*"
    }
}

实际安装的版本如下。

组件版本
illuminate/databasev4.1.30
jenssegers/laravel-mongodbv1.3.2

使用方式:

  1. 配置文件conf/database.php新增mongodb配置。

    <?php
    $database = array(
        'mongodb' => array(
            'driver'    => 'mongodb',
             'host'      => '100.98.173.13',
             'port'      => 5566,
             'database'  => 'bigdata',
         ),
    )
  2. 自动加载数据库,新增mongodb数据库类型。

    include conf/database.php;
    
    $capsule = new Capsule;
    
    // 支持mongodb数据库,如果name是mongodb,交给Jenssegers\Mongodb\Connection来处理
    $capsule->getDatabaseManager()->extend('mongodb', function($config)
    {
        return new Jenssegers\Mongodb\Connection($config);
    });
    
    foreach ($database[$env] as $name => $conf) {
        // 创建链接
        $capsule->addConnection($conf, $name);
    }
    
    // 设置全局静态可访问
    $capsule->setAsGlobal();
    
    // 启动Eloquent
    $capsule->bootEloquent();
  3. model继承Jenssegers\Mongodb\Model.

    <?php
    class UserModel extends Jenssegers\Mongodb\Model {
    
        /**
         * table
         *
         * @var string
         */
        protected $table = 'users';
    
        /**
         * connection
         *
         * @var string
         */
        protected $connection = 'mongodb';
    }

上面是按网上找到的资料配置的,然后跑起来后报PHP Fatal error: Call to a member function connection() on a non-object in /web/xxx/vendor/illuminate/database/Illuminate/Database/Eloquent/Model.php on line 2810的错误。

断点定位了半天,才解决掉问题,这里记录一下。

先看下2810行的代码是什么。

2808    public static function resolveConnection($connection = null)
2809    {
2810        return static::$resolver->connection($connection);
2811    }

注意这里用的是static::$resolver,关于php的static和self的区别可以参考这篇文章,这里不做过多描述。
php中self与static的区别

解决方法:

修改vendor\jenssegers\mongodb\src\Jenssegers\Mongodb\Model.php文件,注释下面这行代码。

protected static $resolver;

php输入csv文件的实现(文件内容含json数据)

用输出一个csv文件,内容类似如下,字段3是一个json字符串。

字段1字段2字段3
xy{"200":0,"404":0}

实现:

  1. csv里如果要显示",要用""表示。
    这里通过str_replace('"', '""', $data)进行替换处理。
  2. 下载的文件打开中文乱码。
header("content-type:application/csv;charset=UTF-8");
header("Content-Disposition:attachment;filename=" . $fileName);
echo "\xEF\xBB\xBF"; // 注意,一定要输出这个bom头,否则文件内容会乱码!