1
Tianpu OP <?php
// error_reporting(0); $time_start = getmicrotime(); session_start(); $key = $_GET['key']; $time = $_GET['time']; $files = array('csdn.sql'); $keyminlen = 4; $step = 16000; $limit = 1024000; $lend = "\n"; $path = './temp/'; $data = './data/'; if($_GET['action']=='clear'){ unlink($path.$_SESSION['sid']); $_SESSION['sid'] = ''; } $sid = $_SESSION['sid']; if($sid==''){ $sid = session_id(); $_SESSION['sid'] = $sid; } function getmicrotime(){ list($usec, $sec) = explode(" ",microtime()); return ((float)$usec + (float)$sec); } function cache($file,$str){ global $sid,$path,$limit,$key,$time; $ssize = filesize($path.$sid); if($ssize>$limit){ echo "<a href=temp/$sid> result right click to save as</a><br>"; echo "<br><br><a href=?action=clear>get another keyword</a><br><br>processed in $time seconds"; die('too many result, contact us if you do need it'); } if(preg_match_all("/(.*?)".$key."(.*?)\\n/i",$str,$match)){ if($ssize>0) file_put_contents($path.$sid,$match[0],FILE_APPEND); else file_put_contents($path.$sid,$file."\n".$match[0],FILE_APPEND); } print_r(preg_last_error()); } function large($file,$start,$repeat,$plus){ global $step,$data,$key; $r = array(); $return = $temp = ''; $i = 0; $handle = fopen($data.$file,'r'); while($i<$repeat && !feof($handle)){ fseek($handle, $start, SEEK_SET); if($i==0) $temp = $plus.fread($handle,$step); else $temp = fread($handle,$step); if(strpos($temp,$key)){ $r[1].= $temp; $r[2] = true; $r[3] = substr($temp,-60); } $r[0]+= $step; $start+= $step; $i++; } fclose($handle); return $r; } function process($file,$start,$lnum){ global $step,$lend,$fid,$time,$time_start,$key,$data; $r = array(); $filesize = filesize($data.$file); $block = 51200000; $repeat = 10; $tstr = ''; $count = 0; while($count<$block && $start<$filesize){ $temp = large($file,$start,$repeat,$temp[3]); $start+=$temp[0]; $count+=$temp[0]; if($temp[2]) cache($file,$temp[1]); } $time+=getmicrotime()-$time_start; echo "<br><br>processed in $time seconds<br> continue<br ><br>"; if($start>=$filesize){ $fid++; die($file.' KO.<br>continue to another<br><meta http-equiv="Refresh" content="0;url=?key='.rawurlencode($key).'&time='.$time.'&fid='.$fid.'">'); } $r[] = $start; $r[] = $lnum; return $r; } $start = $_GET['start']; $lnum = $_GET['lnum']; $fid = $_GET['fid']; if($fid<1) $fid = 0; if($fid>count($files)-1){ if(filesize($path.$sid)>0){ echo "<a href=temp/$sid> result right click to save as</a><br>"; echo '<pre>'.file_get_contents($path.$sid).'</pre>'; } else echo 'no result'; echo "<br><br><a href=?action=clear>get another keyword</a><br><br>processed in $time seconds"; die(); } $file = $files[$fid]; if($key=='') die('<form>keyword: <input type=text name=key> <input type=submit></form>'); else{ if(strlen($key)<$keyminlen) die('keyword too short'); if($start<1) $start = 0; if($lnum<2) $lnum = 1; $t = process($file,$start,$lnum); $time+=getmicrotime()-$time_start; echo '<meta http-equiv="Refresh" content="0;url=?key='.rawurlencode($key).'&fid='.$fid.'&time='.$time.'&start='.$t[0].'&lnum='.$t[1].'&file='.$file.'">'; } ?> |
2
Tianpu OP 文件说明:
./x.php data目录放入各种数据 ./data/xxx.sql ./data/xxx.txt ./temp/ ./temp需要写权限 用于保存用户临时数据 $files = array('csdn.sql'); 这里用数组调用各种数据 我还在测试 估计没有太多改进的余地了 毕竟磁盘IO是省不了的 基本运行也要点时间 欢迎提出改进意见 |
3
Tianpu OP 最终结果 天涯+csdn 6666条数据 返回113K的结果 耗时7.8s 还好了 睡觉
|
4
vibbow 2011-12-26 05:18:40 +08:00
相信我,搜索时大部分时间不是浪费在硬盘时间上,而是strpos过程上。
|
5
vibbow 2011-12-26 05:20:28 +08:00
也就是说硬盘性能根本不是瓶颈,而是CPU性能。
|
6
Tianpu OP :) 我一次只读16k左右的一个块 这些因素应该已经规避了 可能是VPS烂吧 晚安啦
|
7
vibbow 2011-12-26 06:43:26 +08:00
我没觉得我的搜索速度有多快啊...
既不支持正则,也不支持行号输出... 我搜索服务端算法大体改了3次,我整理整理代码加一下注释,稍后发上来。 性能信息就是:在E5400处理器上,只使用1核心(PHP一次也只能使用一个核心),7200转普通sata硬盘,3G内存上(根据Process Explorer的记录,httpd进程峰值占用了650M的内存),对所有9个数据库搜索(纯文本文件,总大小4.6G),单关键字搜索大约需要3分钟,10关键字并发搜索大约需要5分钟... |
8
vibbow 2011-12-26 06:44:42 +08:00
当然了,运行时间也是和结果数量是成正比的。
如果结果数量特别多的话就需要七八分钟了。 |
9
vibbow 2011-12-26 08:21:51 +08:00
发现一个问题诶。
你的代码,搜索正常的csdn文件速度是很快 但是如果我自己创建个文件,每行都是 "vibbow\r\n" 重复上两三万行 那么用你的代码搜索vibbow,一个结果都木有... |
10
vibbow 2011-12-26 08:33:25 +08:00
而且你的代码貌似区分大小写来着...
|
11
areless 2011-12-26 09:23:18 +08:00
存MYSQL,全匹配很快的。模糊查询不用外部索引那是慢的不得了 =_____,=
|
13
vibbow 2011-12-26 09:49:38 +08:00
呃...
终于读懂了你的代码,不过你的代码貌似没有考虑读取步进的问题。 举例来说,比如说我有这么 12345678这么一串字符串,我想搜索456。 你的代码先读取了1234,发现没有匹配,然后直接读取了5678,发现还是没有匹配。 于是就认为不匹配了。 我觉得这就是你 large 函数里发生的错误... 呃... 刚才用XDebug对我的代码进行了一下性能分析,发现最耗性能的居然是strtolower函数... 看来有必要做两份数据库了... |
14
dndx 2011-12-26 09:55:55 +08:00
@vibbow 目前我大概能做到118860631行记录7-8秒正则遍历完,同时8个并发,但是在明文问题不解决之前我先把服务关了,否则跨省可不是玩的。
|
16
dndx 2011-12-26 09:59:07 +08:00
@vibbow 问题是不同文件格式不一,不好判断哪部分是密码。我没有对文件做任何预处理。你有木有Gtalk,我倒很想跟你交流交流。
|
17
vibbow 2011-12-26 10:01:52 +08:00
@dndx GTalk ... 木有... 我直接开Gmail吧.... [email protected]
|
18
Tianpu OP @vibbow $r[3] = substr($temp,-60); 极少有一行超过60字节的 用这个临时数据来保证不遗漏 当然会有不好看的多余数据 理论上不会少
|
20
Tianpu OP 还可以这样测试下 if(preg_match_all("/\\n(.*?)".$key."(.*?)\\n/i","\n".$str."\n",$match)) 行内匹配 无论如何不出结果都是没天理了
|
21
Tianpu OP 我只是测试下php读取大文件的能力 实际结果非常满意 因此可以利用php做更多的事情了
我这边测试出来的是一次少量读取 迅速处理 然后PHP是具备处理大文件能力的 还有就是资源的节省很重要 感谢热心的vibbow 感谢所有参与本帖的人 |
22
alsotang 2011-12-27 01:51:16 +08:00
这就开始感谢了?.....我还要冒个泡....
以后发代码可以发github的gist,这样indent和highlight都方便。具体可以在V2EX搜一下方法,贴个gist地址就行。 |
23
vibbow 2011-12-29 03:37:37 +08:00
http://vsean.net/blog/post/98
我的全文搜索代码整理完成。 |
24
kojp 2011-12-29 14:34:03 +08:00
我也感谢大家~~~~ (以没有看到这个帖子,还特意开了一个帖子)
|
25
vibbow 2011-12-30 08:03:13 +08:00
又改进了一遍代码,现在瓶颈在硬盘了。
10G数据,170秒全文搜完。 |
26
lyxint 2011-12-30 08:30:00 +08:00 via Android
预先生成索引啊
|
28
delectate 2011-12-30 08:49:13 +08:00
10,000/170=58,硬盘了。
|