6

我在 MySQL 4.x DB 中有以下 3 个表:

  • 主机:(300.000 条记录)
    • id (UNSIGNED INT) 主键
    • 名称(VARCHAR 100)
  • 路径:(6.000.000 条记录)
    • id (UNSIGNED INT) 主键
    • 名称(VARCHAR 100)
  • 网址:(7.000.000 条记录)
    • host (UNSIGNED INT) PRIMARY KEY <--- 链接到 hosts.id
    • path (UNSIGNED INT) PRIMARY KEY <--- 链接到paths.id

如您所见,模式非常简单,但问题在于这些表中的数据量。

这是我正在运行的查询:

SELECT CONCAT(H.name, P.name)
FROM hosts AS H
INNER JOIN urls as U ON H.id = U.host
INNER JOIN paths AS P ON U.path = P.id;

此查询运行良好,但需要 50 分钟才能运行。有谁知道我如何加快查询速度?

提前致谢。尼古拉斯

4

14 回答 14

6

也许您应该包含一个 WHERE 子句?还是您真的需要所有数据?

于 2009-02-04T13:56:48.143 回答
4

在我看来,这就像过度使用代理键会减慢您的速度的情况。如果表格是:

  • 主机:

    • 名称(VARCHAR 100)主键
  • 路径:

    • 名称(VARCHAR 100)主键
  • 网址:

    • host (VARCHAR 100) PRIMARY KEY <--- 链接到 hosts.name
    • path (VARCHAR 100) PRIMARY KEY <--- 链接到paths.name

那么您的查询将根本不需要连接:

SELECT CONCAT(U.host, U.path) FROM urls U;

诚然,表 URLS 会占用更多磁盘空间——但这有关系吗?

编辑:再想一想,那 PATHS 表的意义何在?不同主机多久共享一次相同的路径?

为什么不:

  • 主机:

    • 名称(VARCHAR 100)主键
  • 网址:

    • host (VARCHAR 100) PRIMARY KEY <--- 链接到 hosts.name
    • path (VARCHAR 100) PRIMARY KEY <--- 没有链接到任何地方

EDIT2:或者如果您真的需要主机的代理键:

  • 主机:

    • id 整数 PRIMARY KEY
    • 名称(VARCHAR 100)
  • 网址:

    • 主机整数 PRIMARY KEY <--- 链接到 hosts.name
    • path (VARCHAR 100) PRIMARY KEY <--- 没有链接到任何地方

    SELECT CONCAT(H.name, U.path) FROM urls U JOIN hosts H ON H.id = U.host;

于 2009-02-04T14:09:41.347 回答
2

一方面,我不会在查询中执行 CONCAT。在外面做。

但实际上,您的查询运行缓慢,因为您要检索数百万行。

于 2009-02-04T13:58:36.813 回答
2

总体而言,最好的建议是跟踪和分析以查看真正占用时间的内容。但这是我对要查看的具体事物的看法。

(1) 我想说您要确保在执行此查询时不使用索引。由于您没有过滤条件,因此对所有表进行全扫描然后通过排序合并或哈希操作将它们连接在一起应该更有效。

(2) 字符串连接肯定需要一些时间,但我不明白为什么人们建议删除它。然后您可能需要在另一段代码中进行连接,这仍然需要大约相同的时间(除非 MySQL 的字符串连接由于某种原因特别慢)。

(3) 从服务器到客户端的数据传输可能花费大量时间,很可能超过服务器获取数据所需的时间。如果您有工具可以追踪这类事情,请使用它们。如果您可以增加客户端中的获取数组大小,请尝试不同的大小(例如,在 JDBC 中使用 Statement.setFetchSize() )。即使客户端和服务器位于同一主机上,这也可能很重要。

于 2009-02-04T14:41:57.333 回答
1

您是否已经在连接属性上声明了一些索引?

PS:有关MySQL 4.x 的索引,请参见此处[断开的链接]

于 2009-02-04T13:57:50.340 回答
1

在运行查询之前尝试优化表:

optimize table hosts, paths, urls;

它可能会为您节省一些时间,尤其是在已从表中删除行的情况下。(有关优化的更多信息,请参见此处)

于 2009-02-04T14:02:02.640 回答
1

我会尝试用你想要的数据创建一个新表。这样做意味着您会丢失一些真实数据,但您会快速获胜。这个想法可能类似于 OLAP 或类似的东西吗?

当然,您必须(每天或其他)更新此表。

于 2009-02-04T14:04:15.600 回答
1

我不是 MySQL 专家,但看起来 MySQL 主键是集群的——你需要确保你的主键是这样的;聚集索引肯定有助于加快速度。

不过有一件事——我不相信任何一张桌子上都可以有两个“主”键。由于这个原因,你的 urls 表在我看来相当可疑。最重要的是,您应该绝对确保 urls 表中的这两列被索引到了刀柄——每一个上都有一个数字索引应该没问题——因为你要加入它们,所以 DBMS 需要知道如何快速找到它们;这可能是你的情况。如果您正在对那么多行进行全表扫描,那么是的,您可能会在服务器尝试查找您要求的所有内容时坐在那里很长一段时间。

我还建议从 select 语句中删除该 CONCAT 函数,并查看它如何影响您的结果。如果这不是某种促成因素,我会感到惊讶。只需检索两列并在之后处理连接,然后看看情况如何。

最后,你有没有弄清楚瓶颈在哪里?只要表被正确索引,只需加入三个数百万行的表根本不需要太多时间(我预计可能需要一秒钟左右,只是盯着你的表和查询)。但是,如果您将这些行推送到速度较慢或已经固定的 NIC 上,推送到内存不足的应用服务器等,那么缓慢可能与您的查询完全无关,而是与查询后发生的情况有关。七百万行是相当多的数据,需要组装和移动,无论找到这些行需要多长时间。尝试只选择一行,而不是全部七百万,然后看看对比效果如何。如果速度很快,那么问题不在于查询,而在于结果集。

于 2009-02-04T14:33:21.033 回答
1

由于您的结果集返回所有数据,因此根本无法进行优化。您正在扫描整个表,然后加入具有索引的其他表。

PrimaryKeys 是集群的吗?这样可以确保数据按索引顺序存储在磁盘上,从而避免在磁盘的不同部分反弹。

此外,您可以将数据分布在多个磁盘上。如果您在 PRIMARY 上有 URL,在 SECONDARY 上有 PATHS/HOSTS,那么您将从驱动器获得更好的吞吐量。

于 2009-02-04T15:15:55.753 回答
1

您需要查看您的服务器配置。MySQL 的默认内存参数会削弱这种大小的表的性能。key_buffer_size如果您使用默认值,则需要至少提高join_buffer_size4 倍,甚至更多。查看文档;您还可以调整其他内存参数。

MySQL 有一个有趣的性能怪癖,如果您的表超过一定大小,查询将返回大部分数据,性能就会进入厕所。不幸的是,它无法告诉您何时达到该阈值。不过,在我看来,就像你一样。

于 2009-02-06T06:28:02.413 回答
0

concat 肯定会减慢您的速度。我们可以看到一个mysql解释的结果吗?文档链接

最重要的事情是尝试只提取您需要的数据。如果您可以提取更少的记录,那么这将尽可能加快您的速度。但是 mysql 解释应该可以帮助我们查看是否有任何索引会有所帮助。

于 2009-02-04T14:02:40.470 回答
0

我了解您需要完整的 url 列表 - 包含 700 万条记录。也许正如 Mitch所建议的那样,您应该考虑使用 WHERE 子句来过滤您的结果。也许时间主要与显示记录的延迟有关

检查此查询的时间

select count(*)
FROM hosts AS H
INNER JOIN urls as U ON H.id = U.host
INNER JOIN paths AS P ON U.path = P.id

如果这仍然很慢,我会去检查从 urls 中选择 count(*) 的时间

然后

select count(*) 
from urls u 
inner join hosts h on u.host = h.id

然后

select count(*) 
from urls u 
inner join hosts h on u.host = h.id
inner join paths p on u.path = p.id

只是为了找到减速的根源

有时重新排序您的查询也会有所帮助

SELECT CONCAT(u.host, u.path)
from urls u 
inner join hosts h on u.host = h.id
inner join paths p on u.path = p.id
于 2009-02-04T14:26:55.587 回答
0

我不能肯定地说 mySQL,但我知道在 SQL Server 中主键会自动创建索引,但外键不会。确保检查您的外键字段是否有索引。

于 2009-02-04T14:28:20.903 回答
0

因为我不是 MySQL 的忠实粉丝,所以我想问一下你是否尝试过 PostgreSQL。在该数据库中,您需要确保您的 work_mem 设置相当高,但您可以使用 SET work_mem = 64MB 为每个数据库连接设置它。

另一个建议是研究使用重复的路径条目。有许多共享路径URL。

另一件可能有用也可能没有帮助的事情是使用固定长度的文本字段而不是 varchars。它曾经会产生速度差异,但我不确定当前的数据库引擎。

如果您确实使用 PostgreSQL,它将允许您使用 JOIN USING 但即使在 MySQL 上我更喜欢它:在每个表中将您的 id 字段命名为相同。而不是主机中的 id 和 url 中的主机,将其命名为 host_id 两个地方。

现在再发表一些评论。:) 当您选择一小组行时,您在此处拥有的这种数据布局非常有用,可能是来自同一域的每个 URL。如果您的查询经常需要对 urls 表中存储的其他数据进行顺序扫描,它也会有很大帮助,因为扫描可以跳过大文本字段(除非这无关紧要,因为您的数据库通过指针存储文本无论如何都是一个链接表)。

但是,如果您几乎总是选择所有域和路径数据,那么将其存储在一个表中会更有意义。

于 2009-02-06T07:00:11.053 回答