我有一个巨大的产品表(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内存。
我能做些什么来提高速度和减少内存?
这是我的实体:
/**
* Product
*
* @ORMTable()
* @ORMEntity(repositoryClass="NamespaceStockBundleEntityProductRepository")
*/
class Product
{
/**
* @var integer
*
* @ORMColumn(name="id", type="integer")
* @ORMId
* @ORMGeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @var string
*
* @ORMColumn(name="naam_nl", type="string", length=255)
*/
private $naamNl;
/**
* @var string
*
* @ORMColumn(name="naam_fr", type="string", length=255)
*/
private $naamFr;
/**
* @var string
*
* @ORMColumn(name="naam_en", type="string", length=255)
*/
private $naamEn;
/**
* @var string
*
* @ORMColumn(name="productnummer", type="string", length=255)
*/
private $productnummer;
/**
* @var float
*
* @ORMColumn(name="prijs", type="float")
*/
private $prijs;
/**
* @var string
*
* @ORMColumn(name="merk", type="string", length=255)
*/
private $merk;
/**
* @ORMOneToOne(targetEntity="NamespaceStockBundleEntityProductInventory", cascade={"persist"})
* @ORMJoinColumn(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;
现在,您正在调用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));
}
您最好使用Symfony2 profiler
来调试您的查询,您应该首先在productinventory_id
字段上设置add index on your fields
和foreign key
。
索引可以这样使用:
/**
* @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;
最后,您应该启用缓存工具,如apc
或memcache
释放您的sql
服务器。