如何提高 KnpPaginatorBundle 和 Doctrine 速度?

Posted

技术标签:

【中文标题】如何提高 KnpPaginatorBundle 和 Doctrine 速度?【英文标题】:How can I improve KnpPaginatorBundle and Doctrine speed? 【发布时间】:2015-08-08 16:41:35 【问题描述】:

我有一个巨大的产品表(100k+ 行),在我的控制器中我有以下功能:

public function indexAction(Request $request)
    
        $findProducts = $this->getDoctrine()
           ->getRepository("StockBundle:Product")->findAll();

        $paginator  = $this->get('knp_paginator');
        $producten = $paginator->paginate(
            $findProducts,
            $request->query->getInt('page', 1)/*page number*/,
            20/*limit per page*/
        );

        return $this->render('StockBundle:Default:index.html.twig', 
             array('producten' => $producten));
    

问题是页面加载大约需要 11-12 秒,并消耗 233MB 的 RAM。

我可以做些什么来提高速度和减少内存?

这是我的实体:

/**
 * Product
 *
 * @ORM\Table()
 * @ORM\Entity(repositoryClass="Namespace\StockBundle\Entity\ProductRepository")
 */
class Product

    /**
     * @var integer
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @var string
     *
     * @ORM\Column(name="naam_nl", type="string", length=255)
     */
    private $naamNl;

    /**
     * @var string
     *
     * @ORM\Column(name="naam_fr", type="string", length=255)
     */
    private $naamFr;

    /**
     * @var string
     *
     * @ORM\Column(name="naam_en", type="string", length=255)
     */
    private $naamEn;

    /**
     * @var string
     *
     * @ORM\Column(name="productnummer", type="string", length=255)
     */
    private $productnummer;

    /**
     * @var float
     *
     * @ORM\Column(name="prijs", type="float")
     */
    private $prijs;



    /**
     * @var string
     *
     * @ORM\Column(name="merk", type="string", length=255)
     */
    private $merk;

    /**
     * @ORM\OneToOne(targetEntity="Namespace\StockBundle\Entity\ProductInventory", cascade="persist")
     * @ORM\JoinColumn(name="productinventory_id", referencedColumnName="id")
     * 
     */
    private $productinventory;

表结构是由教义创建的,如下所示:

CREATE TABLE `product` (
  `id` int(11) NOT NULL,
  `naam_nl` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  `productnummer` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  `prijs` double NOT NULL,
  `merk` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  `productinventory_id` int(11) DEFAULT NULL,
  `naam_fr` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  `naam_en` varchar(255) COLLATE utf8_unicode_ci NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

【问题讨论】:

它不仅取决于教义,您可以发布您的SQL模式表吗? 我添加了实体和表格。 【参考方案1】:

现在,您正在调用findAll(),它将从数据库中检索所有记录,然后将它们合成为对象。这会浪费大量时间,因为这些对象中的大多数都不会再次使用,因为您一次只输出一页。

您应该做的是将查询构建器传递给分页器,然后分页器应该能够创建一个查询,该查询只获取当前页面实际需要的对象。

public function indexAction(Request $request)
    
        $findProducts = $this->getDoctrine()
           ->getRepository("StockBundle:Product")->createQueryBuilder("p");

        $paginator  = $this->get('knp_paginator');
        $producten = $paginator->paginate(
            $findProducts,
            $request->query->getInt('page', 1)/*page number*/,
            20/*limit per page*/
        );

        return $this->render('StockBundle:Default:index.html.twig', 
             array('producten' => $producten));
    

【讨论】:

就是这样,现在我的页面大约需要 300 毫秒(在 app_dev.php 中)并且只使用了大约 19MB 的内存。【参考方案2】:

您最好使用Symfony2 profiler 来调试您的查询,您应该首先使用add index on your fieldsforeign key 字段productinventory_id

索引可以这样使用:

/**
 * @Entity
 * @Table(name="user",indexes=
 *     @Index(name="email_idx", columns="email")
 * )
 */
class User

    ...

要在您的 indexAction 中优化此特定查询,您可以选择只需要在列表中显示的列,还可以编写查询而不是 DQL。 例如:

// Get connection
$conn = $entityManager->getConnection();

// Get table name
$meta = $entityManager->getClassMetadata(User::class);
$tableName = $meta->getTableName();

// Get random ids
$sql = "SELECT id AS id FROM $tableName WHERE active = true ORDER BY RAND()";
$statement = $conn->executeQuery($sql);
$ids = array_map(function ($element) 
    return $element['id'];
, $statement->fetchAll());

return $ids;

最后,您应该启用缓存工具,如 apcmemcache 以释放您的 sql 服务器。

【讨论】:

虽然所有这些都是有效的数据库访问优化策略,但没有一个真正适用于此。主要问题是提问者获取的条目太多。添加索引在这里根本没有帮助,建议在您只需要 20 个对象和每个对象 8 个属性时切换到原始 SQL 查询。 我同意你的观点,你的回答更好!

以上是关于如何提高 KnpPaginatorBundle 和 Doctrine 速度?的主要内容,如果未能解决你的问题,请参考以下文章

ajax查询后如何更新knppaginatorbundle的分页模板

使用 KnpPaginatorBundle 创建和检索可排序的选项列表

KnpPaginatorBundle:排序分页表(由搜索表单参数生成)

安装 KnpPaginatorBundle 后文件夹 css 和 img 内容消失

将数据添加到分页器(KnpPaginatorBundle,Symfony)

使用 KnpPaginatorBundle 在 symfony 4 中进行分页