Lub*_*Suk 6 php mysql database search
我正在研究PHP + MySQL应用程序,它将抓取硬盘/共享驱动器并将所有文件和目录索引到数据库中,以提供"全文"搜索.到目前为止,我做得很好,但即时通讯问题,如果我选择如何将数据存储到数据库的好方法.
在下图中,您可以看到我的数据库的部分模式.我认为,我正在保存域名(代表我想要索引的磁盘的一部分)然后有一些链接(代表文件和文件夹(包含内容,文件路径等)然后我有桌面存储鞋底(uniq)关键字,我在文件/文件夹名称或内容中找到.
最后,我有16个表linkkeyword来存储链接和关键字之间的关系.我有16个因为我认为制作像hashtable这样的东西可能会很好,因为我期望链接 < - > 关键字之间存在大量关系.(到目前为止,对于15k链接和400k关键词,我有大约2.5百万的linkkeyword记录).因此,为了避免将如此多的数据存储到一个表中(以及稍后在它们上面搜索),我认为这个hastable可以更快.它的工作方式就像我想搜索单词一样,我计算它md5并查看md5的第一个字符,然后我知道我应该使用哪个linkkeyword表.因此每个linkkeyword表中只有大约150~200k记录(相对于2.5milions)
所以,好奇,如果这种方法可以有任何用途,或者如果将所有linkkeyword信息存储到单个表更好,mysql会处理它(以及它可以工作多少链接 < - > 关键字?)
到目前为止,这对我来说是一个很好的解决方案,但是当我尝试实现正则表达式搜索时,我很努力.因此,用户可以使用例如"tem*",这可能导致临时,临时,寺庙等...在搜索单词时,我将以md5哈希进行争议,然后我知道我需要查看哪个linkkeyword表.但是对于正则表达式,我需要从关键字表中获取所有关键字(与正则表达式匹配),然后逐个处理它们.
我还附加了正常关键字搜索的部分代码
private function searchKeywords($selectedDomains) {
$searchValues = $this->searchValue;
$this->resultData = array();
foreach (explode(" ", $searchValues) as $keywordName) {
$keywordName = strtolower($keywordName);
$keywordMd5 = md5($keywordName);
$selection = $this->database->table('link');
$results = $selection->where('domain.id', $selectedDomains)->where('domain.searchable = ?', '1')->where(':linkkeyword' . $keywordMd5[0] . '.keyword.keyword LIKE ?', $keywordName)
->select('link.*,:linkkeyword' . $keywordMd5[0] . '.weight,:linkkeyword' . $keywordMd5[0] . '.keyword.keyword');
foreach ($results as $result) {
$keyExists = array_key_exists($result->linkId, $this->resultData);
if ($keyExists) {
$this->resultData[$result->linkId]->updateWeight($result->weight);
$this->resultData[$result->linkId]->addKeyword($result->keyword);
} else {
$domain = $result->ref('domain');
$linkClass = new search\linkClass($result, $domain);
$linkClass->updateWeight($result->weight);
$linkClass->addKeyword($result->keyword);
$this->resultData[$result->linkId] = $linkClass;
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
和正则表达式搜索功能
private function searchRegexp($selectedDomains) {
//get stored search value
$searchValues = $this->searchValue;
//replace astering and exclamation mark (counted as characters for regular expression) and replace them by their mysql equivalent
$searchValues = str_replace("*", "%", $searchValues);
$searchValues = str_replace("!", "_", $searchValues);
// empty result array to prevent previous results to interfere
$this->resultData = array();
//searched phrase can be multiple keywords, so split it by space and get results for each keyword
foreach (explode(" ", $searchValues) as $keywordName) {
//set default link result weight to -1 (default value)
$weight = -1;
//select all keywords, which match searched keyword (or its regular expression)
$keywords = $this->database->table('keyword')->where('keyword LIKE ?', $keywordName);
foreach ($keywords as $keyword) {
//count keyword md5 sum to determine which table should be use to match it links
$md5 = md5($keyword->keyword);
//get all link ids from linkkeyword relation table
$keywordJoinLink = $keyword->related('linkkeyword' . $md5[0])->where('link.domain.searchable','1');
//loop found links
foreach ($keywordJoinLink as $link) {
//store link weight, for later result sort
$weight = $link->weight;
//get link ID
$linkId = $link->linkId;
//check if link already exists in results, to prevent duplicity
$keyExists = array_key_exists($linkId, $this->resultData);
//if link already exists in result set, just update its weight and insert matching keyword for later keyword tag specification
if ($keyExists) {
$this->resultData[$linkId]->updateWeight($weight);
$this->resultData[$linkId]->addKeyword($keyword->keyword);
//if link isnt in result yet, insert it
} else {
//get link reference
$linkData = $link->ref('link', 'linkId');
//get information about domain, to which link belongs (location, flagPath,...)
$domainData = $linkData->ref('domain', 'domainId');
//if is domain searchable and was selected before search, add link to result set. Otherwise ignore it
if ($domainData->searchable == 1 && in_array($domainData->id, $selectedDomains)) {
//create new link instance
$linkClass = new search\linkClass($linkData, $domainData);
//insert matching keyword to links keyword set
$linkClass->addKeyword($keyword->keyword);
//set links weight
$linkClass->updateWeight($weight);
//insert link into result set
$this->resultData[$linkId] = $linkClass;
}
}
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
您的问题主要是一种意见,因此您可能需要包含一些标准,以便我们能够更客观地回答“值得”。
看来您已经重新发明了数据库分片的概念(尽管没有将数据分布在多个服务器上)。
我假设您正在尝试优化搜索时间;如果是这样的话,我建议,只要您的查询可以使用索引,现代硬件上的 250 万条记录并不是一个特别大的性能挑战。如果您无法使用索引(例如,因为您正在进行正则表达式搜索),则分片可能根本没有帮助。
我对数据库性能调整的一般建议是从最简单的关系解决方案开始,不断调整直到它打破你的性能目标,然后添加更多硬件,只有当你完成了之后,你才应该选择“异国情调”的解决方案,比如分片。
这并不意味着使用祈祷作为策略。对于性能关键型应用程序,我通常会构建一个测试数据库,可以在其中试验解决方案。在您的情况下,我将使用您的模式构建一个数据库,而不使用“分片”表,然后用测试数据填充它(编写您自己的填充例程,或使用像DBMonster这样的工具)。通常情况下,我会选择至少是我在生产中期望的尺寸的两倍。然后,您可以运行和调整查询来以某种方式证明您的架构是否足够好。听起来工作量很大,但比分片解决方案可能带来的工作量要少得多。
有(如 @danFromGermany 评论)针对文本搜索进行优化的解决方案,您可以使用MySQL 全文搜索功能而不是正则表达式。