Symfony2 / Doctrine make $statement->execute() not “buffer” all values

前端 未结 3 1377
滥情空心
滥情空心 2021-02-09 14:06

I\'ve got a basic codeset like this (inside a controller):

$sql = \'select * from someLargeTable limit 1000\';
$em = $this->getDoctrine()->getManager();
$c         


        
相关标签:
3条回答
  • 2021-02-09 14:17

    I just ran into the same problem and wanted to share a possible solution. Chances are your DBAL uses PDO library and its PDO::MYSQL_ATTR_USE_BUFFERED_QUERY set to true which means all the results in your query are cached on mysql side and buffered into memory by PDO even though you never call $statement->fetchAll(). To fix this, we just need to set PDO::MYSQL_ATTR_USE_BUFFERED_QUERY to false but DBAL does not give us a way to do it - its PDO connection class is protected without a public method to retrieve it and it does not give us a way to use setAttribute on the PDO connection.

    So, in such situations, I just use my own PDO connection to save memory and speed things up. You can easily instantiate one with your doctrine db parameters like this:

    $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);
    

    I am using unix sockets but IP host addresses can also be easily used.

    0 讨论(0)
  • 2021-02-09 14:20

    You can disable query buffer by doctrine config param options

    doctrine:
        dbal:
            # configure these for your database server
            driver: 'pdo_mysql'
            ...
            options:
                1000: false
    
    0 讨论(0)
  • 2021-02-09 14:27

    The selected answer is wrong and @kroky's answer should be selected as the correct one.

    The problem is Buffer vs Unbuffered Queries.

    Now it won't be a good idea to change the behaviour for all queries, because:

    Unless the full result set was fetched from the server no further queries can be sent over the same connection.

    Hence, it should only be used when necessary. Here is a full working example with >200k objects:

        $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!
            }
        }
    

    It most likely needs some refinement.

    0 讨论(0)
提交回复
热议问题