How can i upload a csv file in mysql database using multithreads?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //get the csv file $file = $_FILES['csv']['tmp_name']; $handle = fopen($file,"r"); //loop through the csv file and insert into database do { if ($data[0]) { $expression ="/^[_a-z0-9-]+(\.[_a-z0-9-]+)*@[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,3})$/"; if (preg_match($expression, $data[0])) { $query=mysql_query("SELECT * FROM `postfix`.`recipient_access` where recipient='".$data[0]."'"); mysql_query("SET NAMES utf8"); $fetch=mysql_fetch_array($query); if($fetch['recipient']!=$data[0]){ $query=mysql_query("INSERT INTO `postfix`.`recipient_access`(`recipient`, `note`) VALUES('".addslashes($data[0])."','".$_POST['note']."')"); } } } } while ($data = fgetcsv($handle,1000,",","'")); |
首先,我不能给人足够的压力;修复你的缩进 - 它将使每个人的生活更轻松。
其次,答案很大程度上取决于您遇到的实际瓶颈:
- 正则表达式非常慢,特别是当它们处于循环中时。
- 数据库往往适用于WRITES或READS,但不适用于:尝试预先减少查询量。
- 按理说,循环中的PHP代码越少,它的工作速度就越快。考虑减少条件(例如)。
- 为了记录,您的代码对于mysql注入是不安全的:在手[*]之前过滤$ _POST
- [*]说到这一点,访问一个变量比一个数组的索引更快,比如$ _POST。
- 您可以通过让主程序将巨大的CSV文件拆分为较小的CSV文件并将每个文件运行到不同的进程来模拟多线程。
的common.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | class FileLineFinder { protected $handle, $length, $curpos; public function __construct($file){ $handle = fopen($file, 'r'); $length = strlen(PHP_EOL); } public function next_line(){ while(!feof($this->handle)){ $b = fread($this->handle, $this->length); $this->curpos += $this->length; if ($b == PHP_EOL) return $this->curpos; } return false; } public function skip_lines($count){ for($i = 0; $i < $count; $i++) $this->next_line(); } public function __destruct(){ fclose($this->handle); } } function exec_async($cmd, $outfile, $pidfile){ exec(sprintf("%s > %s 2>&1 & echo $! >> %s", $cmd, $outfile, $pidfile)); } |
main.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | require('common.php'); $maxlines = 200; // maximum lines subtask will be processing at a time $note = $_POST['note']; $file = $_FILES['csv']['tmp_name']; $outdir = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'out' . DIRECTORY_SEPARATOR; //make sure our output directory exists if(!is_dir($outdir)) if(!mkdir($outdir, 0755, true)) die('Cannot create output directory: '.$outdir); // run a task for each chunk of lines in the csv file $i = 0; $pos = 0; $l = new FileLineFinder($file); do { $i++; exec_async( 'php -f sub.php -- '.$pos.' '.$maxlines.' '.escapeshellarg($file).' '.escapeshellarg($note), $outdir.'proc'.$i.'.log', $outdir.'proc'.$i.'.pid' ); $l->skip_lines($maxlines); } while($pos = $l->next_line()); // wait for each task to finish do { $tasks = count(glob($outdir.'proc*.pid')); echo 'Remaining Tasks: '.$tasks.PHP_EOL; } while ($tasks > 0); echo 'Finished!'.PHP_EOL; |
sub.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | require('common.php'); $start = (int)$argv[1]; $count = (int)$argv[2]; $file = $argv[3]; $note = mysql_real_escape_string($argv[4]); $lines = 0; $handle = fopen($file, 'r'); fseek($handle, $start, SEEK_SET); $expression ="/^[_a-z0-9-]+(\.[_a-z0-9-]+)*@[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,3})$/"; mysql_query('SET NAMES utf8'); //loop through the csv file and insert into database do { $lines++; if ($data[0]) { if (preg_match($expression, $data[0])) { $query = mysql_query('SELECT * FROM `postfix`.`recipient_access` where recipient="'.$data[0].'"'); $fetch = mysql_fetch_array($query); if($fetch['recipient'] != $data[0]){ $query = mysql_query('INSERT INTO `postfix`.`recipient_access`(`recipient`, `note`) VALUES("'.$data[0].'","'.$note.'")'); } } } } while (($data = fgetcsv($handle, 1000, ',', '\'')) && ($lines < $count)); |
积分
- https://stackoverflow.com/a/2162528/314056
- https://stackoverflow.com/a/45966/314056
将整个循环放在SQL事务中。这将使事情加快一个数量级。
一般建议:加速任何程序的关键是知道哪个部分占用大部分时间。
然后弄清楚如何减少它。有时您会对实际结果感到非常惊讶。
顺便说一句,我不认为多线程会解决你的问题。
最紧迫的事情是确保您的数据库已正确编入索引,以便您为每一行执行的查询查询尽可能快。
除此之外,你可以做的事情并不多。对于多线程解决方案,您必须在PHP之外。
您也可以在mySQL中导入CSV文件,然后使用PHP脚本清除多余的数据 - 这可能是最快的方法。