7

我有一个像这样的基本代码集(在控制器内):

$sql = 'select * from someLargeTable limit 1000';
$em = $this->getDoctrine()->getManager();
$conn = $em->getConnection();
$statement = $conn->prepare($sql);
$statement->execute();

我的困难是,当结果集只有几条记录时,内存使用情况还不错。我在运行$statement->execute();之前和之后回显了一些调试信息;部分代码,并为我的实现发现我有以下内容:

pre-execute... rowCount :: 0 memory: 49.614 MB
post-execute... rowCount :: 1000 memory: 50.917 MB

当从 1000 条记录上移到 10k 时,MB 使用量的差异增长到 13 MB

pre-execute... rowCount :: 0 memory: 49.614 MB
post-execute... rowCount :: 10000 memory: 62.521 MB

最终,检索大约 50k 条记录时,我接近了我的最大内存分配:

pre-execute... rowCount :: 0 memory: 49.614 MB
post-execute... rowCount :: 50000 memory: 114.096 MB

有了这个实现,我就无法编写一个控制器(甚至是命令)来让我检索 CSV 数据。当然,50k+ 条目听起来很多,问题是为什么,但这不是问题。

我的最终问题是:是否可以告诉 DBAL/Connection 或 DBAL/Statement 在执行时缓冲 SQL 中的数据而不是整个 PHP 中的数据。例如,如果我有 1000 万行,只将前 10k 行发送到 PHP...让我通过@statement->fetch();来查看它们。当光标到达 10k 的末尾时,截断数组并从数据库中获取下一个 10k?

4

3 回答 3

14

我刚遇到同样的问题,想分享一个可能的解决方案。您的 DBAL 可能使用 PDO 库并将其PDO::MYSQL_ATTR_USE_BUFFERED_QUERY设置为 true,这意味着查询中的所有结果都缓存在 mysql 端并由 PDO 缓冲到内存中,即使您从未调用$statement->fetchAll(). 要解决这个问题,我们只需要设置PDO::MYSQL_ATTR_USE_BUFFERED_QUERY为 false,但 DBAL 没有给我们提供方法 - 它的 PDO 连接类受到保护,没有公共方法来检索它,并且它没有给我们提供在 PDO 上使用setAttribute的方法联系。

所以,在这种情况下,我只是使用自己的 PDO 连接来节省内存并加快速度。您可以使用您的学说数据库参数轻松地实例化一个,如下所示:

$dbal_conn = $this->getDoctrine()->getManager()->getConnection();
$params = $dbal_conn->getParams();
$pdo_conn = new \PDO(
  'mysql:dbname='.$dbal_conn->getDatabase().';unix_socket='.$params['unix_socket'],
  $dbal_conn->getUsername(),
  $dbal_conn->getPassword()
);
$pdo_conn->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, false);

我使用的是 unix 套接字,但 IP 主机地址也可以轻松使用。

于 2014-11-28T09:58:04.333 回答
11

选择的答案是错误的,@kroky 的答案应该被选为正确的答案。

问题是Buffer vs Unbuffered Queries

现在更改所有查询的行为并不是一个好主意,因为:

除非从服务器获取完整的结果集,否则不能通过同一连接发送进一步的查询。

因此,它只应在必要时使用。这是一个包含 >200k 个对象的完整工作示例:

    $qb = ...->createQueryBuilder('p');

    $this
        ->em
        ->getConnection()
        ->getWrappedConnection()
        ->setAttribute(\PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, false);

    $query = $qb->getQuery();
    $result = $query->iterate();
    $batchSize = 20;
    $i = 0;
    foreach ($result as $product)
    {
        $i++;

        var_dump($product[0]->getSku());

        if (($i % $batchSize) === 0) {
            $this->em->flush();
            $this->em->clear(); // Detaches all objects from Doctrine!
        }
    }

它很可能需要一些改进。

于 2016-03-24T13:43:38.117 回答
0

您可以通过学说配置参数选项禁用查询缓冲区

doctrine:
    dbal:
        # configure these for your database server
        driver: 'pdo_mysql'
        ...
        options:
            1000: false
于 2018-12-03T13:00:06.207 回答