Mysql在大量行

时间:2015-09-24 09:14:06

标签: mysql sql performance doctrine query-optimization

我尝试在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

第一个数字显示自请求开始以来经过的微秒,然后是以毫秒为单位的相同时间,然后是正在进行的操作。

2 个答案:

答案 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)