【问题标题】:Generate 5 million records and insert them into the database in five seconds生成500万条记录,5秒插入数据库
【发布时间】:2017-04-24 23:29:24
【问题描述】:

我有一个任务要练习。生成 500 万条记录:string (15), string (15), int (min: 1 max: 99) 并将它们存储在数据库 postgresql 中。 我的脚本的第一个版本是 500 万的估计时间:16120 秒。

编辑(2015 年 12 月 12 日 11:50):目前经过多次优化:80 秒。

为了达到预期的效果,我应该改变什么?

下面我展示了代码的最优化版本。

// In Source Code -> function insertDataFive

$countRecords = 5000000;

for ($i = 0; $i < ($countRecords/100000); $i++) // $i < 50
{
    echo "Loop one \$i: " . $i . "\n";
    try
    {
        echo "Memory used (before) real: " . (memory_get_peak_usage(true) / 1024 / 1024) . " MiB\n";

        $sql = "INSERT INTO users
    (first_name, last_name, user_age)
    VALUES ('" . randomString(15) . "','" . randomString(15) . "'," . randomAge() . ")";

        for ($j = 1; $j < 100000; $j++)
        {
            $sql .= ",('" . randomString(15) . "','" . randomString(15) . "'," . randomAge() . ")";
        }
        $dbh->exec($sql);
        
        
        echo "Memory used (after) real: " . (memory_get_peak_usage(true) / 1024 / 1024) . " MiB\n\n";
        unset($sql);
    } catch (PDOException $e)
    {
        echo 'PDO error: ' . $e->getMessage();
    }
}

也许我云使用了除 INSERT 之外的其他东西?

编辑:问题解决了! 2016年12月19日 script relase 1.0 source

重要!在 cmd 底部命令中运行此脚本之前执行一次

mkdir /tmp/ram

mount -t tmpfs -o size=512m tmpfs /tmp/ram

生成数据:

function createDataFile($task, $rows = 1250000)
{
    try
    {
        global $fileName, $fileExtension, $timeFileCreate;
        $startTime = microtime(true);
        $fileCSV = $fileName . $task . $fileExtension;
        $fileHandler = fopen('/tmp/ram/' . $fileCSV, 'w');
        if ($fileHandler != false)
        {
            if (DEBUG)
            {
                echo "Memory used (before) fwrite: " . getMemoryUsage() . " MiB\n";
            }
            for ($i = ($task - 1) * $rows; $i < $task * $rows; $i++)
            {
                fwrite($fileHandler, (( $i + 1 . ","
                        . generateRangomString(15) . ","
                        . generateRangomString(15) . ","
                        . generateRangomAge()) . "\n"));
            }
            fclose($fileHandler);
            if (DEBUG)
            {
                echo "Memory used (after) fwrite: " . getMemoryUsage() . " MiB\n";
            }
        }
        else
        {
            echo "File open error";
        }
        $timeFileCreate += (microtime(true) - $startTime);
    }
    catch (Exception $ex)
    {
        echo "File Error: " . $ex->getMessage();
    }
}

插入行:

function insertSingleDataFile($dbh, $task)
{
    global $fileName, $fileExtension, $timeSqlBulk;
    $startTime = microtime(true);
    $fileCSV = $fileName . $task . $fileExtension;
    $sqlBulk = "COPY users (user_id, first_name, last_name, user_age)
    FROM '/tmp/ram/$fileCSV'
    DELIMITER ','";

    try
    {
        $dbh->query($sqlBulk);
    }
    catch (PDOException $e)
    {
        echo 'PDO error: ' . $e->getMessage() . "\n\n";
    }
    $timeSqlBulk += (microtime(true) - $startTime);
}

【问题讨论】:

  • 在文件中加载数据并在插入后添加索引是改进点
  • 500 万条记录?? ⊙▃⊙
  • 我认为这个问题更适合 codereview stackexchange,因为我们不是在处理代码中的错误,而是在优化。
  • 377 秒 - 运行脚本时间 362 秒 - 运行插入 12 秒 - randomString() 1.5 秒 - randomAge()

标签: php postgresql optimization pdo transactions


【解决方案1】:

您可以尝试将所有内容集中到一个循环中。无需先收集记录,例如

$dbh->beginTransaction();

$sql = 'INSERT INTO users
    (first_name, last_name, user_age)
    VALUES (?, ?, ?)';

$sth = $dbh->prepare($sql);

for ($i = 0; $i < $countRecords; $i++) {
    $sth->execute(array(
        randomString(15),
        randomString(15),
        randomAge(),
    ));
}

$dbh->commit();

这样可以节省大数组的额外内存,以及从关联数组到简单数组的打包和解包。

【讨论】:

  • 类似的东西? link
  • 这是同一时间 - 没有任何变化
  • 然后我们可以排除数组的内存需求作为一个问题,并且像load data infilepg_copy_from这样的cmets中的建议之一可能会提高性能。
  • 无法想象一个更漏掉问题的答案。
  • @YourCommonSense 随意发布您自己的好答案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-01-31
  • 2015-09-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多