Mysql 搜索 1.18 亿行耗时 86 秒

Posted

技术标签:

【中文标题】Mysql 搜索 1.18 亿行耗时 86 秒【英文标题】:Mysql search of 118 million rows taking 86 seconds 【发布时间】:2012-07-06 13:37:14 【问题描述】:

我得到了一个带有 2 个表的 mysql Innodb。一个是 117+ 百万行,有超过 340 列,包括姓名、地址、城市、州和邮政编码。第二个表是 17+ 百万行,其中包含姓名、地址、城市、州和邮政编码以及电子邮件。第一个和第二个表中的数据不会被添加或更新。每个表中的 id 都有一个主键。没有定义其他索引。

我首先从 117+ 百万行表中创建了一个联系人表,其中只有姓名、地​​址、城市、州和邮编,使其显着变小。我编写了一个 php 脚本来使用包含 17+ 百万条记录的较小表中的每一行执行搜索,试图在联系人表中找到匹配项。当找到一个时,我将 id 和 email 插入到一个单独的表中。我取消了它,因为每次搜索大约需要 86 秒。拥有超过 17 万条记录,这将需要很长时间才能完成。

这是我的搜索查询:

q= "从 GB_contacts 中选择 id
WHERE LAST_NAME=\"$LAST\" 和 FIRST_NAME=\"$FIRST\" 和 MI=\"$MIDDLE\"
和 ADDRESS=\"$ADDRESS\" 和 ZIP=\"$ZIP\""。

我的问题是我怎样才能更快地做到这一点?我应该对联系人表中的姓名、地址和邮编进行索引,还是应该索引联系人表中的每一列?有没有更快的方法通过mysql做到这一点?我已经阅读了一大堆不同的资源,但不确定哪个是最好的方法。由于这些桌子很大,我尝试做的任何事情都需要很长时间,所以我希望得到一些专家的建议,避免浪费几天、几周和几个月的时间来解决这个问题。感谢您提供任何有用的建议!

【问题讨论】:

您计划进行 1700 万个单独的查询?即使有索引,这也将永远持续下去。更好的选择是使用基于集合的连接解决方​​案。应该能够只使用一个插入语句,但您需要向我们展示有关表格的更多信息...... @DJ.:我相信他在 1700 万行上运行查询,而不是 1700 万次。这将是非常低效的,并且可能导致系统阻塞/崩溃。 “使用包含 17+ 百万条记录的较小表中的每一行执行搜索” - 对我来说听起来像是 1700 万次 感谢您的快速cmets! DJ - 就搜索而言,您是正确的。疯狂,我知道,因此我的问题。感谢您的建议,我正在考虑加入。据我回忆,连接会创建一个临时表,而我需要一个永久表。这是正确的,我可以从连接创建一个永久表吗?至于表的信息:id INT(10) UNSIGNED PRIMARY KEY, FIRST_NAME VARCHAR(30), LAST_NAME VARCHAR(30), MIDDLE VARCHAR(1), ADDRESS VARCHAR(47) ZIP MEDIUMINT(5) UNSIGNED。跨度> 这也需要非常非常长的时间。我必须通过 SSH 连接到我的服务器,但我的管道不断损坏。 【参考方案1】:

执行此操作的最佳方法是在要匹配的字段上创建聚集索引。在这种情况下,从邮政编码开始可能是个好主意,然后是名字或姓氏 - 姓氏较长,因此需要更长的时间来匹配,但也更加不同,因此会留下更少的行做进一步的匹配(你必须测试哪个表现更好)。这里的策略是告诉 mysql 只查找人的口袋,而不是搜索整个数据库。在这样做的时候,你必须聪明地告诉 MySQL 从哪里开始缩小范围。测试时,不要忘记使用 EXPLAIN 命令。

【讨论】:

这听起来很有希望,但是你能给我一个创建聚集索引的例子吗?从我在那里读到的内容看来,我无法在具有主键的表上创建一个。这是我的表:id INT(10) UNSIGNED PRIMARY KEY, FIRST_NAME VARCHAR(30), LAST_NAME VARCHAR(30), MIDDLE VARCHAR(1), ADDRESS VARCHAR(47) ZIP MEDIUMINT(5) UNSIGNED。 我想我会像这样创建索引:CREATE INDEX idx1 ON tbl1 (ZIP,FIRST_NAME,LAST_NAME)。这样,查询将首先查找具有正确邮政编码的行,然后查找具有正确名字的行,然后是正确的姓氏。我想在任何情况下都不应该让服务器留下这么多行,以至于它需要构建一个临时表来进行额外的排序。请注意,在这么大的数据库上,这个索引会对插入和更新造成影响。关于拥有主键的注意事项:这不应该是一个问题。我假设您正在使用 InnoDB。 ...(续)...在这种情况下,索引保留在表本身中。因此,您可能需要考虑将 A_I 字段添加到索引(在末尾),这样您就可以使索引唯一并可以删除 PK。这样可以保证表中的数据按照我刚才描述的索引进行排序。如果还有什么不清楚的,请告诉我。【参考方案2】:

您是否尝试过典型的连接,如果您的连接键被索引,它应该不会花费太多时间。

如果有一次您可以在连接列上创建索引。

第二步是将返回的记录加载到新的联系人表中。

【讨论】:

感谢大家的所有反馈。通过将所有内容分解为多个表,创建索引然后合并表,我让它非常有效地工作。现在我的搜索需要几秒钟。 :-)

以上是关于Mysql 搜索 1.18 亿行耗时 86 秒的主要内容,如果未能解决你的问题,请参考以下文章

SQL 查询耗时过长

Sphinx 安装与使用

如何使用弹性搜索索引 10 亿行 CSV 文件?

select 的字串 少 速度快 还是 *快?

MySQL 查询耗时超过 6 秒

MySql 查询在 PhpMyAdmin 中耗时 0.16 秒,但在 Php 中耗时 10 分钟