SQL查询:大表之间的内部联接优化

SQL查询:大表之间的内部联接优化,sql,mysql,optimization,inner-join,bigtable,Sql,Mysql,Optimization,Inner Join,Bigtable,我在MySQL 4.x数据库中有以下3个表: 主持人:30万条记录 id无符号整型主键 名称VARCHAR 100 路径:6.000.000条记录 id无符号整型主键 名称VARCHAR 100 URL:7.000.000记录 主机UNSIGNED INT主键也许应该包含WHERE子句?或者你真的需要所有的数据吗?你已经在连接属性上声明了一些索引了吗 PS:关于MySQL 4.x上的索引,请参见[断开的链接],有一件事我不会在查询中使用CONCAT。在外面做 但实际上,您的查询运行缓慢,因为您正

我在MySQL 4.x数据库中有以下3个表:

主持人:30万条记录 id无符号整型主键 名称VARCHAR 100 路径:6.000.000条记录 id无符号整型主键 名称VARCHAR 100 URL:7.000.000记录
主机UNSIGNED INT主键也许应该包含WHERE子句?或者你真的需要所有的数据吗?

你已经在连接属性上声明了一些索引了吗


PS:关于MySQL 4.x上的索引,请参见[断开的链接],有一件事我不会在查询中使用CONCAT。在外面做


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

在运行查询之前,请尝试优化您的表:

optimize table hosts, paths, urls;
这可能会为您节省一些时间,尤其是在已从表中删除行的情况下。
有关优化的更多信息,请参阅。concat肯定会让您减速。我们能看到mysql解释的结果吗


不过,最重要的是尝试只提取所需的数据。如果你能拉更少的记录,那么你的速度会比任何东西都快。但是mysql解释应该可以帮助我们看看是否有索引会有帮助。

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


当然,您必须每天更新此表或其他任何内容。

在我看来,过度使用代理键会让您的速度变慢。如果表格为:

主持人:

名称VARCHAR 100主键 路径:

名称VARCHAR 100主键 网址:


host VARCHAR 100 PRIMARY KEY我知道您需要一个完整的URL列表,即700万条记录。 也许你应该考虑使用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
如果这仍然是缓慢的,我会去检查的时间 从URL中选择count*

然后

然后

只是为了找到减速的源头

有时,对查询重新排序也会有所帮助

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

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

我不是MySQL专家,但看起来MySQL主键是集群的-您需要确保主键的情况也是如此;聚集索引肯定有助于加快速度

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

我还建议从select语句中删除CONCAT函数,看看它如何影响结果。如果这不是一个促成因素,我会感到惊讶。只需检索这两个列,然后处理连接,然后看看如何进行


最后,你找到瓶颈在哪里了吗?仅仅连接三个数百万行的表应该不会花费太多时间,我估计可能需要一秒钟左右的时间,只要仔细查看表和查询,只要表被正确索引。但是,如果您将这些行推过一个缓慢的或已经固定的NIC,推到内存不足的应用程序服务器上,等等,那么这种缓慢可能与您的查询没有任何关系,而是与查询后发生的情况有关。700万行是需要组装和移动的相当多的数据,不管查找这些行需要多长时间。试着只选择一行,而不是全部700万行,然后看看对比结果如何。如果这很快,那么问题不在于查询,而在于结果集。

总的来说,最好的建议是跟踪和分析哪些内容真正占用了时间。但以下是我对具体问题的看法

1我想说的是,您希望确保在执行此查询时不使用索引。由于您没有筛选条件,所以应该更有效地完全扫描所有表,然后通过排序合并或哈希操作将它们连接在一起

2字符串连接确实需要一些时间,但我不明白为什么人们建议删除它。然后,您可能需要在另一段代码中进行连接,除非MySQL的字符串连接是particu,否则它仍然需要大约相同的时间 由于某种原因,速度很慢


3从服务器到客户机的数据传输可能需要大量时间,很可能比服务器获取数据所需的时间还要长。如果你有跟踪这类事情的工具,就使用它们。如果您可以增加客户端中的获取数组大小,请尝试不同的大小,例如在JDBC use Statement.setFetchSize中。即使客户机和服务器在同一台主机上,这一点也很重要。

由于结果集返回所有数据,因此几乎无法进行任何优化。您正在扫描整个表,然后连接其他具有索引的表

主密钥是否群集?这样可以确保数据按索引顺序存储在磁盘上,从而避免在磁盘的不同部分之间来回跳转


此外,您还可以将数据分布在多个磁盘上。如果主路径上有URL,次路径上有路径/主机,那么驱动器的吞吐量会更好。

您需要查看服务器配置。MySQL的默认内存参数将削弱该大小表的性能。如果使用默认值,则至少需要将key\u buffer\u size和join\u buffer\u size提高至少4倍,甚至更多。查阅文件;您还可以调整其他内存参数


MySQL有一个有趣的性能怪癖,如果您的表超过一定的大小,并且查询将返回大部分数据,那么性能就会下降。不幸的是,它无法告诉您何时达到该阈值。不过,在我看来,您似乎已经尝试过了。

因为我不是MySQL的忠实粉丝,我想问您是否尝试过PostgreSQL。在该数据库中,您可能希望确保work_mem设置非常高,但您可以使用set work_mem=64MB为每个数据库连接设置它

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

另一件可能有帮助也可能没有帮助的事情是使用固定长度的文本字段而不是varchar。它曾经使速度不同,但我不确定目前的DB引擎

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

现在有更多的评论 当您选择一小部分行(可能是来自同一域的每个URL)时,这里的数据布局非常有用。如果您的查询经常需要对URL表中存储的其他数据进行顺序扫描,那么它也会有很大帮助,因为扫描可以跳过较大的文本字段,除非这无关紧要,因为您的DB通过指向链接表的指针存储文本


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

是的,如果他不总是需要最新的数据,则建议使用物化视图。实际上,如果他确实希望返回所有行,则索引可能没有帮助。对表中的每个值进行索引查找可能比完全扫描表并将它们散列或合并在一起要慢。我看到了几百兆的数据。如果这一切都在记忆中,你是对的。但是一个合适的DBMS,我想即使是MySQL 4.x,因为它足够合适,它本身也会忽略现有的索引。我只是想补充一个答案,与第二部分的答案相同。好吧,只是出于兴趣,如果这是你的观点,为什么要使用关系数据库呢?你的意图与我给所有客户的建议完全相反。我只能说啊!!!戴姆斯,如果你坚持要求每张桌子上都有代理键,我很同情你的客户。关系数据库与自然键一样工作,有时甚至更好。啊!!!的确MySQL只允许每个表有一个主键,但该键可以由表中的多个列组成。因此,在Nicolas的例子中,URL表有一个由主机+路径组成的主键。当然,这是有意义的-我忽略了询问这些键是否是一个组合键,我认为无论如何都不必弄清楚。不过,我主要想指出,以某种方式显式索引这两列的重要性。
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
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