MySql需要很长时间来优化无连接查询

MySql需要很长时间来优化无连接查询,sql,mysql,optimization,join,query-optimization,Sql,Mysql,Optimization,Join,Query Optimization,我们有一个简单的查询,如下所示: SELECT a,b,c,d FROM table WHERE a=1 and b IN ('aaa', 'bbb', 'ccc', ...) 完全没有连接,in子句中有5000个连续值 现在,这个查询需要1-20秒才能在非常强大(16核)的服务器上运行。该表在(a,b)上有一个索引,我们还尝试将索引反转为(b,a)。服务器有大量内存,没有人向这个表中写入数据——只有5个进程在运行我上面描述的SELECT 我们做了一些分析,发现一些查询在“JOIN::opti

我们有一个简单的查询,如下所示:

SELECT a,b,c,d FROM table WHERE a=1 and b IN ('aaa', 'bbb', 'ccc', ...)
完全没有连接,in子句中有5000个连续值

现在,这个查询需要1-20秒才能在非常强大(16核)的服务器上运行。该表在(a,b)上有一个索引,我们还尝试将索引反转为(b,a)。服务器有大量内存,没有人向这个表中写入数据——只有5个进程在运行我上面描述的SELECT

我们做了一些分析,发现一些查询在“JOIN::optimize”(.\sql\u select.cc 977)中花费了3.5秒。我提醒您,查询根本不使用连接

在无联接表上优化联接所花费的时间如此之长,原因可能是什么

以下是解释选择的结果:

id select_type table type   possible_keys key    key_len ref rows   Extra
1  SIMPLE     table range    IX_A_B       IX_A_B 65      \N  5000   Using where

在字段a上有索引吗,尤其是在字段b上

如果您需要SQL优化方面的帮助,请附上

EXPLAIN SELECT a,b,c,d FROM table WHERE a=1 and b IN ('aaa', 'bbb', 'ccc', ...)

同样,没有它,人们只能猜测。

尝试在临时表中输入5000个值:

declare @t table (b varchar(10))
insert into b select 'aaa'
union all select 'bbb'
union all select 'c'
....

select table.*
from table
join @t t on table.b = t.b
where table.a = 1
(x,y,…)中的b被转换为:(b=x或b=y或b=…)


这意味着您需要对表中的每个值进行5000次if检查。

使用类似于in子句的in子句也可能是一个join,因此它不是完全的join-less

在(a,b)上有一个索引是相当好的,但是你必须想知道它如何得到c和d的值。。。最后,它可能会忽略索引,而只是扫描整个表


尝试在(a、b、c、d)上建立索引,以便索引包含所需的所有数据。在SQL Server中,您可以使用包含的列来完成此操作,但我认为在mysql中,您也需要包括其他列。这意味着您的查询可以直接进入A= 1记录,并开始查找与列表匹配的B的记录,然后它拥有它需要的所有信息。

< P>发现这是一个.

< p>您的答案是考虑以下两个答案中的建议:

此外,你提到b是高度选择性的;因此:


我建议您将索引中列的顺序更改为(b,a)。如果Optimer可以更快地缩小您的结果范围,那么使用索引将更为迫切。(通常,将最有选择性的列放在索引的前面是一个很好的经验法则;如果你想偏离这一原则,那就很少了。)

in值来自哪里?它们是像上面那样写的字符串吗?基本上是的,它们是常量。我使用的是jdbc,in值实际上是问号(使用PreparedStatement),但这应该是一样的?“a=1”的选择性有多高?我运行了“分析表”和“显示索引”。如何确定选择性?那么“a=1”返回的百分比是多少?为什么它要扫描整个表?这个表有2500万条记录——通过索引找到正确的记录并获取它们更便宜。事实上,解释显示扫描了5000条记录。我选择的是整个记录(相当于*),因此无法构建包含所有查询字段的索引。你认为使用WHERE a=1和(b='aaa'或b='bbb'或b='ccc')会更好吗?它是扫描,扫描会返回5000条记录,在查看了所有记录之后。大多数数据库系统不能很好地处理或。a,b上的索引意味着如果它使用该索引,它可以找到5000条记录(寻找a=1,然后在其中找到记录),但是它需要从表本身获取c,d,这也需要成本。系统可能会觉得这个额外的查找太多,决定改为扫描。它扫描是因为所需的磁盘查找被认为是非常昂贵的。DB可以快速找到a、b;但是,对于每个a,b,它需要如下“书签查找”c,d:1)读取聚集位置键2)读取从根开始的聚集索引页(磁盘搜索)3)确定下一个索引页4)如果不是在数据页,转到2。与按顺序将所有数据加载到内存中并执行线性搜索相比,磁盘搜索被认为是极其昂贵的。也许服务器会意识到这一点,转而构建一个包含“aaa”、“bbb”等的临时表,并与之进行连接?两个原因可能会更好;绝对值得一试。1) 每当b有不同数量的选项时,DBMS可能会认为它有足够多的不同,可以重新编译(即使它是一个参数化查询)。这很有可能,因为OP描述问题的方式听起来很可能是查询重新编译造成的开销。2)通过将数据放入一个临时表(特别是如果列被索引),你可以给优化器一个选项来考虑各种连接…合并联接可能非常有效。正如我在问题中所写的,我尝试颠倒索引顺序,但没有任何帮助。@ripper:正如我在回答中所写的,您有多个问题要处理。此外,将最具选择性的列放在索引的第一位的指导原则仍然是一个很好的一般原则;即使您没有注意到改进,因为只要in子句中的元素数量发生变化,90%的时间都花在重新编译查询上;但是考虑一下:你实际上是要求OpimISER评估一个5000或条件查询!在您提供的链接中:[2007年8月17日20:43]Igor Babaev说:“这不是一个bug:对于某些查询,任何DBMS都可能被迫花费在优化器阶段,而不是执行阶段。”此外:“任何需要长期优化的查询都可以重写以避免这个问题(例如:中的谓词a可以替换为中的+0)。”我再次向您推荐以下答案:查询很简单-in列表中有5000个常量值。优化不会花费很长时间。我咨询了上述DBA,他对该答案没有多大意义。它可能会起作用,但在此之前,我将尝试更小的ba