【问题标题】:Word Occurrences count in PHP - scaling and optimizationPHP中的单词出现计数 - 缩放和优化
【发布时间】:2019-07-24 21:34:01
【问题描述】:

您在 Mysql 表中有大量非常短(

选择的项目可以少至一个(或无),多至 1M+,因此必须分析的文本大小可能会有很大差异。

目前的解决方案包括:

  • 将选定记录中的文本读取到文本变量中
  • preg_replacecing 删除所有不是“单词”的东西,得到一些清理,较短的文本(主题标签、@mentions、http(s):// 链接、电话号码等数字序列等不是“单词”并被解析出来)
  • 将剩余的内容分解到“缓冲区”数组中
  • 取出短于两个单词的所有内容并将其他所有内容转储到“主”数组中

然后用array_count_values转换结果数组,排序(arsort),第一次拼接以获得更易于管理的数组大小,然后针对几种语言的停用词列表进行解析,再处理和拼接,最后以JSON形式输出为50 个最常用词的列表。

通过跟踪操作序列的各个步骤的时序,最明显的瓶颈在于查询,但随着项目数的增加,它迅速移动到 array_count_values 函数(之后的一切都是或多或少直接)。

在大约 10k 个项目的测试运行中,从开始到结束的总执行时间约为 3 秒;有 20k 个项目需要约 7 秒。

具有 130 万个项目的(相当极端但并非不可能)案例需要 1m 用于 mysql 查询,并且能够每分钟解析大约 75k 个项目(因此估计为 17 分钟左右)。

结果应该被可视化为 AJAX 调用的结果,因此在这种时间安排下,UX 显然被打乱了。我正在寻找尽可能优化一切的方法。 30 秒的加载时间可能是可以接受的(但不太现实),10 分钟(或更多)则不是。

我尝试通过 array_count_values 对块进行批处理,然后通过键将生成的计数数组添加到主数组中,但这仅能起到很大的帮助 - 各部分的总和是相等的(或略大于)时间上的总数。

  • 我只需要列表顶部出现频率最高的 50 个,因此可能有一些改进的余地,可以减少一些角落。

【问题讨论】:

  • 字数很多,可以提供帮助,但少量的上下文代码通常更有价值。
  • 你走错路了。与其在每个 ajax 请求中尝试读取所有数据库、检查替换、排序等,不如一劳永逸地构建一个带有计数列的单词表,并在每次插入或删除时更新它(使用触发器)。然后,您可以通过带有 LIMIT 和 ORDER BY 子句的简单查询获得结果。 (显然不到 1 秒)
  • @tadman 我一般同意,但代码不是这里的范围。找到不同的观点来解决问题是。代码如下。
  • @CasimiretHippolyte 就是我在上面的评论中提到的那种“不同的视角”。我想到了类似的东西,但是从中得出计数的出现列表的内容可能会因执行的每个查询而异 - 所以它不仅仅是“计算该表中列中的单词” - 记录可能必须匹配一些要包含在计数中的参数。
  • @tadman 我研究了这些,并且-尽管这种解决方案最终需要在相对较短的时间内集成-两者似乎都非常适合该问题(如一切都扩大了,mysql中的全文-boolean-搜索也开始迅速变薄)从文档中可以看出,对于任何一种解决方案来说,诸如单词(出现)计数之类的东西都太低级了。它可能可以完成,但不是立即的。

标签: php mysql arrays regex


【解决方案1】:
  1. 将列转储到文本文件中。建议SELECT ... INTO OUTFILE 'x.txt'...
  2. (如果在 linux 等上):tr -s '[:blank:]' '\n' <x.txt | sort | uniq -c

要获得前 50 名:

tr -s '[:blank:]' '\n' <x.txt | sort | uniq -c | sort -nbr | head -50

如果您需要调整“单词”的定义,例如处理标点符号,请参阅tr 上的文档。您可能在使用单引号中的缩写词与短语时遇到问题。

一些文本的清理可以(并且应该)在 SQL 中完成:

WHERE col NOT LIKE 'http%'

有些可以(并且应该)在 shell 脚本中完成。

例如,这将去掉 @variables 和电话号码以及 0、1 和 2 个字符的“单词”:

egrep -v '^@|^([-0-9]+$|$|.$|..)$'

这可以在第一个 sort 之前的管道流中。

唯一的限制是磁盘空间。脚本中的每一步都非常快。

测试用例:

表格:176K 行,大部分是文本(包括一些换行符)

$ wc x3.txt
  3,428,398 lines  31,925,449 'words'  225,339,960 bytes

$ time tr -s '[:blank:]' '\n' <x.txt | egrep -v '^@|^([-0-9]+$|$|.$|..)$' |
              sort | uniq -c | sort -nbr | head -50
 658569 the
 306135 and
 194778 live
 175529 rel="nofollow"
 161684 you
 156377 for
 126378 that
 121560 this
 119729 with
...
real    2m16.926s
user    2m23.888s
sys 0m1.380s

够快吗?

我正在通过top 观看它。似乎最慢的部分是第一个sortSELECT 耗时不到 2 秒(但是,该表可能缓存在 RAM 中)。

【讨论】:

  • 我喜欢这个。我唯一的疑虑可能在于所有这一切都发生在一个 API 中,所以它会比我感到舒服的多一些 shell_exec - 但我可以控制使用它的人(这是一个非公共平台),因此在安全性方面可能是可以接受的。
  • @qqg - 这是一个单行脚本(有很多管道)。也就是说,只有一个 shell-exec。还是我不理解你的问题?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-08-09
  • 1970-01-01
  • 2021-11-17
  • 1970-01-01
相关资源
最近更新 更多