优化大型表上的SQL查询

时间:2008-09-22 11:11:49

标签: mysql sql optimization

首先,这个问题关于MySQL 3.23.58,所以请注意。

我有2个表,其定义如下:

Table A: id INT (primary), customer_id INT, offlineid INT

Table B: id INT (primary), name VARCHAR(255)

现在,表A包含65k +记录范围,而表B包含约40个记录。除了2个主键索引外,表A中的 offlineid 字段还有一个索引。每个表中有更多字段,但它们不相关(我看到它,询问是否必要的)这个查询。

我第一次看到以下查询(查询时间:~22秒):

SELECT b.name, COUNT(*) AS orders, COUNT(DISTINCT(a.kundeid)) AS leads
FROM katalogbestilling_katalog a, medie b
WHERE a.offlineid = b.id
GROUP BY b.name

现在,medie中的每个id都与一个不同的名称相关联,这意味着您可以按ID和名称进行分组。来回进行了一些测试(查询时间:~6秒):

SELECT a.name, COUNT(*) AS orders, COUNT(DISTINCT(b.kundeid)) AS leads
FROM medie a
INNER JOIN katalogbestilling_katalog b ON a.id = b.offline
GROUP BY b.offline;

有没有办法把它调到“即时”时间(最差时间最长1秒)?我在offlineid上添加了索引,但除了那个和查询的重新安排之外,我无法做什么。 EXPLAIN查询显示查询正在使用fileshort(原始查询也使用临时表)。欢迎所有建议!

10 个答案:

答案 0 :(得分:1)

我猜你的主要问题是你使用的是旧版本的MySQL。也许MySQL 3不喜欢COUNT(DISTINCT())。

或者,它可能只是系统性能。你有多少记忆?

不过,MySQL 3真的很老了。我至少会组建一个测试系统来查看更新版本是否更快地运行该查询。

答案 1 :(得分:1)

不幸的是,mysql 3不支持子查询。我怀疑旧版本通常会导致性能下降。

答案 2 :(得分:0)

如何定义kundeid?查看两个表的完整模式(由MySQL生成,即带索引)以及EXPLAIN的输出以及上述查询将会很有帮助。

调试此问题并找出问题的最简单方法是从查询中逐个删除字段并测量运行所需的时间(记得在运行每个查询之前运行RESET QUERY CACHE) )。在某些时候,您会看到执行时间显着下降,然后您已经确定了瓶颈。例如:

SELECT b.name, COUNT(*) AS orders, COUNT(DISTINCT(a.kundeid)) AS leads
FROM katalogbestilling_katalog a, medie b
WHERE a.offlineid = b.id
GROUP BY b.name

可能会成为

SELECT b.name, COUNT(DISTINCT(a.kundeid)) AS leads
FROM katalogbestilling_katalog a, medie b
WHERE a.offlineid = b.id
GROUP BY b.name

消除“订单”成为瓶颈的可能性,或

SELECT b.name, COUNT(*) AS orders
FROM katalogbestilling_katalog a, medie b
WHERE a.offlineid = b.id
GROUP BY b.name

消除方差中的“线索”。这将引导您朝着正确的方向前进。

更新 :我不建议从最终查询中删除任何数据。只需删除它们以减少变量数量,同时寻找瓶颈。鉴于你的评论,我理解

SELECT b.name
FROM katalogbestilling_katalog a, medie b
WHERE a.offlineid = b.id
GROUP BY b.name

仍然表现不佳?这显然意味着它是未优化的连接或分组依据(您可以通过删除组来测试 - JOIN将仍然很慢,在这种情况下,您需要修复的问题,或者它不会 - 在这种情况下,它显然是GROUP BY)。你能发布

的输出吗?
EXPLAIN SELECT b.name
FROM katalogbestilling_katalog a, medie b
WHERE a.offlineid = b.id
GROUP BY b.name

以及表模式(以便更容易调试)?

更新#2

还有可能正确地创建了所有的indeces,但是当你遇到最大内存使用时,或者你强制它使用磁盘分类时,你的mysql安装配置错误。

答案 3 :(得分:0)

如果删除内部联接并使用嵌套的select语句替换它,也可以删除计数(*)并将其替换为PK,那么性能可能会略有提高。

SELECT a.name, COUNT(*) AS orders, COUNT(DISTINCT(b.kundeid)) AS leads FROM medie aINNER JOIN katalogbestilling_katalog b ON a.id = b.offline GROUP BY b.offline;

将是

SELECT a.name, COUNT(a.id) AS orders, (SELECT COUNT(kundeid) FROM katalogbestilling_katalog b WHERE b.offline = a.id) AS Leads FROM medie a;

答案 4 :(得分:0)

如果查询运行得足以保证开销,那么在表A上创建一个包含查询中使用的字段的索引。然后可以从索引读取所有结果,它不必扫描表格。

那就是说,我所有的经验都是基于MSSQL的,所以可能无法正常工作。

答案 5 :(得分:0)

您的第二个查询很好,65k + 40k行不是很大:)

在katalogbestilling_katalog.offline列上添加一个新索引,它会更快地运行。

答案 6 :(得分:0)

您可以尝试确保在每个表上定义覆盖索引。覆盖索引只是一个索引,其中在select中使用的每个列或在连接中使用的列都包含在索引中。这样,引擎只需读取索引条目,并且不必执行相应的行查找以获取索引中未包含的任何请求列。我在Oracle和MS SqlServer中使用了这种技术并取得了巨大的成功。

查看您的查询,您可以尝试:

medie.id的一个索引,medie.name
katalogbestilling_katalog.offlineid的一个索引,katalogbestilling_katalog.kundeid

应在索引的这些订单中定义列。无论索引是否可以使用都会有所不同。

更多信息:

Covering Index Info

答案 7 :(得分:0)

尝试将索引添加到(offlineid,kundeid)

我将180,000个BS行添加到katalog,将30,000个BS行添加到medie中(使用katalog offlineid对应于medie id并使用一些重叠的kundeid来确保消除计数有效)。请注意,这是在mysql 5上,所以如果你没有类似的结果,mysql 3可能是你的罪魁祸首,但从我记得mysql 3应该能够处理这个就好了。

我的桌子:

CREATE TABLE `katalogbestilling_katalog` (
  `id` int(11) NOT NULL auto_increment,
  `offlineid` int(11) NOT NULL,
  `kundeid` int(11) NOT NULL,
  PRIMARY KEY  (`id`),
  KEY `offline_id` (`offlineid`,`kundeid`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=60001 ;

CREATE TABLE `medie` (
  `id` int(11) NOT NULL auto_increment,
  `name` varchar(255) NOT NULL,
  PRIMARY KEY  (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=30001 ;

我的查询:

SELECT b.name, COUNT(*) AS orders, COUNT(DISTINCT(a.kundeid)) AS leads
FROM medie b
INNER JOIN katalogbestilling_katalog a ON b.id = a.offlineid
GROUP BY a.offlineid
LIMIT 0 , 30


"Showing rows 0 - 29 (30,000 total, Query took 0.0018 sec)"

解释:

id:  1
select_type:    SIMPLE
table: a
type: index
possible_keys:  NULL
key:    offline_id
key_len:    8
ref: NULL
rows: 180000
Extra: Using index

id: 1
select_type:    SIMPLE
table: b
type: eq_ref
possible_keys:  PRIMARY
key:    PRIMARY
key_len:    4
ref: test.a.offlineid
rows: 1
Extra:

答案 8 :(得分:0)

尝试优化服务器本身。有关最重要的变量,请参阅this post by Peter Zaitsev。一些是InnoDB特定的,而另一些是MyISAM。你没有提到你正在使用哪种引擎在这种情况下可能是相关的(例如,在MyISAM中,count(*)比在InnoDB中快得多)。 Here is another post from same blog以及MySQL Forge

中的文章

答案 9 :(得分:-1)

这需要多长时间:

SELECT fieldlist FROM A
SELECT fieldlist FROM B

如果你的mysql在连接上的运行速度很慢,那么最好是通过单表扫描获取数据并将数据一起分布在数据库之外。 65k记录确实不是 很多。