能够加入 MySQL 的行和表的合理数量是多少?

Posted

技术标签:

【中文标题】能够加入 MySQL 的行和表的合理数量是多少?【英文标题】:What's a reasonable number of rows and tables to be able to join in MySQL? 【发布时间】:2010-04-02 21:25:19 【问题描述】:

我有一张将位置映射到邮政编码的表格。例如,纽约州有大约 2000 个邮政编码。我有另一个表将邮件映射到它发送到的邮政编码,但这个表有大约 500 万行。我想查找所有发往纽约州的邮件,这看起来很简单,但查询速度慢得令人难以置信。我什至无法等待足够长的时间来完成它。有500万行的问题吗?我不禁想,现在 500 万对于一台计算机来说应该不是一个很大的数字……哦,一切都被索引了。 SQL 只是没有设计用于处理如此大的连接吗?

更新:正如人们所问的,我已经用我正在使用的表定义和查询更新了这个问题。

-- Roughly 70,000 rows
CREATE TABLE `mail_zip` (
  `mail_id` int(11) default NULL,
  `zip` int(11) default NULL,
  KEY `index_mail_zip_on_mail_id` (`mail_id`),
  KEY `index_mail_zip_on_zip` (`zip`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1

-- Roughly 5,000,000 rows
CREATE TABLE `geographies` (
  `city_id` int(11) default NULL,
  `postal_code` int(11) default NULL,
  KEY `index_geographies_on_city_id` (`city_id`),
  KEY `index_geographies_on_postal_code` (`postal_code`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1

-- Query
select mz.mail_id from mail_zip mz join geographies g on mz.zip = g.postal_code where g.city_id = 36 limit 10;

更新 2:好吧,我撒谎了。使用适当的索引,上述查询可以正常工作。问题实际上是 order by 子句。请参阅下面两个几乎相同的查询:唯一的区别是“order by m.sent_on desc”,它为查询增加了额外的 4 分 30 秒!此外,使用解释,通过使用文件排序添加订单,这一定是减慢速度的原因。但是,sent_on 是有索引的,那为什么不使用索引呢?我一定没有正确地制作索引。

-- Roughly 350,000 rows
CREATE TABLE `mail` (
  `id` int(11) NOT NULL auto_increment,
  `sent_on` datetime default NULL,
  `title` varchar(255) default NULL,
  PRIMARY KEY  (`id`),
  KEY `index_mail_on_sent_on` (`sent_on`),
) ENGINE=InnoDB DEFAULT CHARSET=latin1

-- Runs in 0.19 seconds
-- Query
select distinct(m.id), m.title from mail m join mail_zip mz on mz.mail_id = m.id join geographies g on g.postal_code = mz.zip where g.city_id = 36 limit 10;

+----+-------------+-------+--------+--------------------------------------------------------+---------+---------+----------------------+---------+-----------------------+
| id | select_type | table | type   | possible_keys                                          | key     | key_len | ref                  | rows    | Extra                 |
+----+-------------+-------+--------+--------------------------------------------------------+---------+---------+----------------------+---------+-----------------------+
|  1 | SIMPLE      | mz    | ALL    | index_mail_zip_on_com_id,index_mail_zip_on_zip         | NULL    | NULL    | NULL                 | 5260053 | Using temporary       | 
|  1 | SIMPLE      | m     | eq_ref | PRIMARY                                                | PRIMARY | 4       |            mz.com_id |       1 |                       | 
|  1 | SIMPLE      | g     | ref    | index_geographies_on_city_id,zip                       | zip     | 5       |            mz.zip    |       1 | Using where; Distinct | 
+----+-------------+-------+--------+--------------------------------------------------------+---------+---------+----------------------+---------+-----------------------+

-- Runs in 4 minutes and 30 seconds
-- Query
select distinct(m.id), m.title from mail m join mail_zip mz on mz.mail_id = m.id join geographies g on g.postal_code = mz.zip where g.city_id = 36 order by m.sent_on desc limit 10;

+----+-------------+-------+--------+--------------------------------------------------------+---------+---------+----------------------+---------+---------------------------------+
| id | select_type | table | type   | possible_keys                                          | key     | key_len | ref                  | rows    | Extra                           |
+----+-------------+-------+--------+--------------------------------------------------------+---------+---------+----------------------+---------+---------------------------------+
|  1 | SIMPLE      | mz    | ALL    | index_mail_zip_on_com_id,index_mail_zip_on_zip         | NULL    | NULL    | NULL                 | 5260053 | Using temporary; Using filesort | 
|  1 | SIMPLE      | m     | eq_ref | PRIMARY                                                | PRIMARY | 4       |            mz.com_id |       1 |                                 | 
|  1 | SIMPLE      | g     | ref    | index_geographies_on_city_id,zip                       | zip     | 5       |            mz.zip    |       1 | Using where; Distinct           | 
+----+-------------+-------+--------+--------------------------------------------------------+---------+---------+----------------------+---------+---------------------------------+

【问题讨论】:

请添加更多关于确切的表和索引定义以及慢查询的详细信息。 注意:要显示准确的表和索引定义,您可以为每个相关表SHOW CREATE TABLE tablename 并在此处发布。 正如人们所问的那样,我已经用我正在使用的表定义和查询更新了这个问题。 好吧,我撒谎了。使用适当的索引,上述查询可以正常工作。问题实际上是 order by 子句。往上看。感谢您解决我的问题,马克! 对 ORDER BY x DESC 子句使用 ASC 索引需要知识渊博的优化器 - 它可能根本不知道如何以相反的顺序遍历索引,或者可能是 InnoDB 索引无法反向遍历命令。然而,这个问题现在大多被认为是优化器的一个弱点。 【参考方案1】:

mysql 完全能够处理涉及 500 万行甚至更多行的连接。

您的问题可能是以下两种情况之一:

您缺少索引。 您正在编写查询,导致优化器无法使用最佳索引,例如,如果您在连接条件的索引列上使用的函数不是 sargable。

既然您声称“所有内容都已编入索引”,我猜它是第二个。发布您的表格信息和查询,我们应该能够帮助您修复它。

您还可以对查询运行 EXPLAIN 以查看它正在使用哪些索引。

【讨论】:

正如人们所问的那样,我已经用我正在使用的表定义和查询更新了这个问题。【参考方案2】:

为了争论起见,您应该能够加入 10 个表,其中最大的表具有数百万甚至更多的行,并且您应该能够快速获得结果。

假设索引策略或查询操作或查询计划有问题。

它与 SQL 本身无关;它可能与 MySQL 或您在 MySQL 中使用的特定存储引擎有关。

您知道 SQL 标准没有定义任何与索引相关的内容吗?您可能会争辩说,与索引相关的任何内容都是非标准的,尽管“标准之外”可能是一种更好的看待它的方式。

【讨论】:

正如人们所问的那样,我已经用我正在使用的表定义和查询更新了这个问题。

以上是关于能够加入 MySQL 的行和表的合理数量是多少?的主要内容,如果未能解决你的问题,请参考以下文章

sql截取字符串,作为表的行和列

从一个表中获取不同的行和*

查询数据库和表的大小

Mysql --库和表的操作

根据谷歌电子表格中的行和列条件计算值

解析 JSON 并绑定到动态创建的 HTML 表的行和列