我尝试在mysql数据库中导入相对较多的数据(大约600万个来自文本文件的条目)。
如果数据库中没有类似的记录,我必须检查每个条目,方法是将它与两个文本字段进行比较:
`ref` varchar(30) COLLATE utf8_unicode_ci NOT NULL
`labelCanonical` varchar(15) COLLATE utf8_unicode_ci DEFAULT NULL
文件由N个条目的批处理(对于此示例10),我执行单个查询以检查批处理中的所有重复项,如下所示:
SELECT p.`ref`, p.`labelCanonical`
FROM `rtd_piece` p
WHERE (p.`ref` = "6569GX" AND p.`labelCanonical` = "fsc-principal")
OR (p.`ref` = "6569GY" AND p.`labelCanonical` = "fsc-principal")
OR (p.`ref` = "6569GZ" AND p.`labelCanonical` = "fsc-principal")
OR (p.`ref` = "6569H0" AND p.`labelCanonical` = "fsc-habitacle")
OR (p.`ref` = "6569H1" AND p.`labelCanonical` = "support-fsc")
OR (p.`ref` = "6569H2" AND p.`labelCanonical` = "fsc-injection")
OR (p.`ref` = "6569H4" AND p.`labelCanonical` = "fsc-injection")
OR (p.`ref` = "6569H8" AND p.`labelCanonical` = "faisceau-mot")
OR (p.`ref` = "6569H9" AND p.`labelCanonical` = "faisceau-mot")
OR (p.`ref` = "6569HA" AND p.`labelCanonical` = "fsc-principal")
我使用Doctrine 2(没有Symfony),我使用" NativeQuery"来执行此查询。
这个问题是,即使数据库中有600k条目,此查询也会执行 730ms (或批量为100条记录的时间为6.7秒)并且会增加记录被添加到数据库中时显着。
我没有#34;参考"或" labelCanonical"现在的领域,我不确定添加一个是否会对我的请求有任何好处。
我对这个方法错了,所以它太慢了?
编辑以添加有关该流程的更多信息。
我为每个批次执行ajax查询,也向用户提供反馈。 在服务器端(PHP)时,我执行以下过程:
1)我在处理当前文件时寻找并提取下N个记录
2)我解析每一行并将引用和slugified标签添加到两个不同的数组
3)我尝试从数据库中获取这些记录以避免重复:
$existing = array();
$results = getRepository('Piece')->findExistingPieces($refs, $labels);
for ($i = 0, $c = count($results); $i < $c; ++$i) {
$existing[] = $results[$i]['ref'].'|'.$results[$i]['labelCanonical'];
}
public function findExistingPieces(array $refs, array $labels)
{
$sql = '';
$where = array();
$params = array();
for ($i = 0, $c = count($refs); $i < $c; ++$i) {
$params[] = $refs[$i];
$params[] = $labels[$i];
$where[] = '(p.`ref` = ? AND p.`labelCanonical` = ?)';
}
$sql = 'SELECT p.`ref`, p.`labelCanonical` '.
'FROM `rtd_piece` p '.
'WHERE '.implode(' OR ', $where);
$rsm = new ResultSetMapping;
$rsm->addScalarResult('ref', 'ref');
$rsm->addScalarResult('labelCanonical', 'labelCanonical');
$query = $this->getEntityManager()
->createNativeQuery($sql, $rsm)
->setParameters($params);
return $query->getScalarResult();
}
4)我遍历先前解析的数据并检查重复项:
for ($i = 0; $i < $nbParsed; ++$i) {
$data = $parsed[$i];
if (in_array($data['ref'].'|'.$data['labelCanonical'], $existing)) {
// ...
continue ;
}
// Add record
$piece = new PieceEntity;
$piece->setRef($data['ref']);
//...
$em->persist($piece);
}
5)我在批次结束时冲洗
我已经添加了一些&#34;分析&#34;用于跟踪每个步骤花费的时间的代码,结果如下:
0.00024509429931641 (0.245 ms) : Initialized
0.00028896331787109 (0.289 ms) : Start doProcess
0.00033092498779297 (0.331 ms) : Read and parse lines
0.0054769515991211 (5.477 ms) : Check existence in database
6.9432899951935 (6,943.290 ms) : Process parsed data
6.9459540843964 (6,945.954 ms) : Finilize
6.9461529254913 (6,946.153 ms) : End of process
6.9464020729065 (6,946.402 ms) : End doProcess
6.9464418888092 (6,946.442 ms) : Return result
第一个数字显示自请求开始以来经过的微秒,然后是以毫秒为单位的相同时间,然后是正在进行的操作。
答案 0 :(得分:1)
经过一些重构,这就是我的意思:
我使用名为“hash”的新字段检查重复项,如下所示:
$existing = array();
$results = getRepository('Piece')->findExistingPiecesByHashes($hashes);
for ($i = 0, $c = count($results); $i < $c; ++$i) {
$existing[] = $results[$i]['hash'];
}
public function findExistingPiecesByHashes(array $hashes)
{
$sql = 'SELECT p.`ref`, p.`labelCanonical`, p.`hash` '.
'FROM `rtd_piece` p '.
'WHERE (p.`hash`) IN (?)';
$rsm = new ResultSetMapping;
$rsm->addScalarResult('ref', 'ref');
$rsm->addScalarResult('hash', 'hash');
$rsm->addScalarResult('labelCanonical', 'labelCanonical');
$query = $this->getEntityManager()
->createNativeQuery($sql, $rsm)
->setParameters(array($hashes));
return $query->getScalarResult();
}
哈希在模型中自动更新,如下所示:
// Entities/Piece.class.php
private function _updateHash()
{
$this->hash = md5($this->ref.'|'.$this->labelCanonical);
}
我的哈希字段没有FULLTEXT索引,因为我使用了InnoDB引擎和MySQL版本5.5,从我读过的内容来看,InnoDB自MySQL 5.6以来只支持FULLTEXT索引。
我现在没有更新MySQL的感觉,运行的数据库和网站太多,如果更新出错将会是灾难性的。
但是,即使没有索引字段,性能提升也是不可思议的:
0.00024199485778809 (0.242) : Initialized
0.00028181076049805 (0.282) : Start doProcess
0.0003199577331543 (0.320) : Read and parse lines
0.088779926300049 (88.780) : Check existence in database
0.8656108379364 (865.611) : Process parsed data
0.94273900985718 (942.739) : Finilize
1.3771109580994 (1,377.111) : End of process
1.3795168399811 (1,379.517) : End doProcess
1.3795938491821 (1,379.594) : Return result
这是一批1000张,桌面上有650k记录。
在优化之前,检查100条记录需要6.7秒,所以它快9倍!
在这个速度下,我应该可以在1h30-2h中导入所有数据。
非常感谢你的帮助。
答案 1 :(得分:0)
首先,我建议你使用行构造函数来编写它:
SELECT p.`ref`, p.`labelCanonical`
FROM `rtd_piece` p
WHERE (p.`ref`, p.`labelCanonical`) IN ( ('6569GX', 'fsc-principal'),
('6569GY', 'fsc-principal'),
. . .
);
这不会影响性能,但更容易阅读。然后,您需要一个索引,rtd_piece(ref, labelCanonical)
或rtd_piece(labelCanonical, ref)
。