【问题标题】:Comparing two large files are taking over four hours比较两个大文件需要四个多小时
【发布时间】:2015-12-20 20:01:09
【问题描述】:

我有一家在线商店,每天都会更新大约 15,000 种产品。目前我每天都上传新列表,但它带来了一些问题(比如停机时间是一个大问题),我想提出一个替代方案。 我创建了一个脚本来移动“昨天”产品列表并下载今天的产品列表。然后我逐行比较这两个文件,看看需要删除、修改和创建什么。这将使我能够以最少的工作量执行更新,无需停机,因为一切都将通过 CRON 作业在幕后发生,这就是它应该完成的方式。

我遇到的问题是这个过程需要四个多小时才能完成,我不确定我正在做的是否是最有效的方法。我的第一个想法是用 C++ 编写一些东西,但我不确定与 PHP 相比会快多少。

我的问题是:

• 这是最有效的方法吗?

• PHP 是最好的语言吗?

这是我编写的处理下载和比较的脚本:

public function __construct($url, $user, $pass)
{
    $this->logger = new KLogger("/opt/lampp/htdocs/lea/logs/master.log" , KLogger::INFO);

    /* increase execution time and server memory limit */
    ini_set('max_execution_time', 14400);
    ini_set('memory_limit', '-1');

    /* set veriables */
    $this->ftp   = ftp_connect($url);
    $this->login = ftp_login($this->ftp, $user, $pass);

    $this->old = file('/opt/lampp/htdocs/lea/products/new/temp/rsr_inventory.txt');
    $this->new = file('/opt/lampp/htdocs/lea/products/new/rsr_inventory.txt');

    $this->list = array();

    $this->start_time = date('Hi');

    $this->counter = 0;
}

public function download($to, $from)
{
    // move current file to new location to get new file ready
    $this->logger->LogInfo('move yesterday\'s products list');
    rename('/opt/lampp/htdocs/lea/products/new/temp/rsr_inventory.txt', '/opt/lampp/htdocs/lea/products/new/rsr_inventory.txt');                

    // get list from rsr    
    $this->logger->LogInfo('get new list from rsr');        
    if(ftp_get($this->ftp, $to, $from, FTP_BINARY))
    {
        return true;
    }
    return false;
}

public function update()
{
    // initialize process
    $this->logger->LogInfo('update process initialized');

    for($i = 0; $i < count($this->new); $i++)
    {
        $new[$i] = explode(';', $this->new[$i]);
        $response = $this->_match($new[$i]);
        if($response[0])
        {
            if(trim($response[2]) != trim($new[$i][5]) || trim($response[3]) != trim($new[$i][8]))
            {
                $this->list[$this->counter][0] = $response[1];
                $this->list[$this->counter][1] = 'update';
                $this->list[$this->counter][2] = trim($response[2]);
                $this->list[$this->counter][3] = trim($response[3]);
                $this->counter++;
            }                               
        }
        else
        {
            $this->list[$this->counter][0] = $response[1];
            $this->list[$this->counter][1] = 'create';
            $this->list[$this->counter][2] = trim($response[2]);
            $this->list[$this->counter][3] = trim($response[3]);
            $this->counter++;
        }           
    }
    if(count($this->list) > 0)
    {
        //csv           
        $this->logger->LogInfo('create update.csv');

        $updates = fopen('/opt/lampp/htdocs/lea/products/new/updates.csv', 'w');
        foreach($this->list as $fields)
        {
            fputcsv($updates, $fields);
        }
        fclose($updates);
    }

    $this->logger->LogInfo('product update process complete');
    $this->__mail();

}

private function _match($item)
{       
    for($j = 0; $j < count($this->old); $j++)
    {
        $old[$j] = explode(';', $this->old[$j]);

        if($item[0] === $old[$j][0])
        {                               
            return array(true, $item[0], $old[$j][5], $old[$j][8]);
        }
    }       
    return array(false, NULL, NULL, NULL);
}

这是我每天得到的 products.txt 文件的示例(我只显示 10 种产品,但大约有 15,000 种产品(缺少很多东西;价格、数量等...,但是我缩短了所有内容,因为显示这些并不重要):

511-10010-019-L-XL;844802282208;5.11 RECON ANKLE SOCK BLK L/XL; 
511-10010-036-L-XL;844802282246;5.11 RECON ANKLE SOCK SHADOW L/XL; 
511-10010-132-LXL;844802334662;5.11 RECON ANKLE SOCK TIMBER L/XL;
511-10010-200-L-XL;844802282222;5.11 RECON ANKLE SOCK FATIGUE L/XL; 
511-10011-019-L-XL;844802276382;5.11 COLD WEATHER OTC SOCK BLK L/XL; 
511-10012-019-L-XL;844802276429;5.11 COLD WEATHER CREW SOCK BLK L/XL; 
511-30012-019-M;844802269650;5.11 WOMENS HOLSTER SHIRT BLK M; 
511-40011-010-L;844802016148;5.11 HOLSTER SHIRT L WHITE; 
511-40011-010-M;844802016131;5.11 HOLSTER SHIRT M WHITE; 
511-40011-010-XL;844802016155;5.11 HOLSTER SHIRT XL WHITE; 
511-40011-010-XXL;844802016162;5.11 HOLSTER SHIRT 2XL WHITE; 

【问题讨论】:

  • 尖叫 - 使用数据库
  • 实际文件的大小是多少?
  • 如何使用 linux diff 命令查找修改区域(假设它在 linux 服务器上运行)?
  • 感谢反馈,使用数据库,难道我每天还要解析新的文本文件并将每一行与数据库中的记录进行比较吗?

标签: php c++ server shopify


【解决方案1】:

我认为您的问题是您正在进行 15000 x 15000 次比较(因此对数据进行了 2.25 亿次操作)。

如果您改为创建一个映射(即 PHP 中的数组),并使用一些唯一标识符作为新旧索引的索引。那是 30k 次操作,然后遍历一个列表,检查另一个列表是否包含相同的东西。那是另外 15K 次操作。总共 45K 操作,而不是 225M 操作。

我并不是说建一个数据库的建议是一个坏主意,但是花费过多的时间显然是由于算法+数据结构的选择不当造成的。

【讨论】:

  • 垫子——我会试试这种方法,看看效果如何。想来想去,这似乎是一个不错的做法。
  • 天哪!使用这种方法,整个过程大约需要 5 分钟。 HUUUGE 节省...这并不夸张。感谢您抽出宝贵时间提供反馈。
  • 我还想指出,我在每次迭代时都在爆炸文件,这是非常昂贵的。在实例化 Products 对象时将每个文件放入一个数组中可以节省大量时间,并且是处理数据的正确方法。
【解决方案2】:

这是 MySQL 的工作。导入您的数据将是一项重大的前期投资,但从长远来看是值得的。数据库旨在有效地更新、合并、删除和插入数据。这种工作在 MySQL 中需要几秒钟。您可以保留 PHP 作为您的脚本语言。

【讨论】:

  • 您是说您可以只导入 CSV 文件并让 MySQL 自动找出更改的内容吗?我原以为你还需要知道发生了什么变化?
  • 您必须编写一些 SQL 来进行比较。将 CSV 导入新表,然后使用 SQL 进行比较和修改。
  • 是的。可以编写一个小的 PHP 脚本,将他每天收到的数据导入到自己的表中,然后通过一些简单的查询,可以快速将其与现有数据进行比较以进行必要的更新。
  • cantalope - 我要试试你的方法和垫子,看看什么会产生最好的时间。我非常感谢您的指导和反馈。
猜你喜欢
  • 2012-02-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-12-02
  • 2017-07-25
相关资源
最近更新 更多