简体   繁体   English

帮我优化这个MySql查询

[英]Help me optimize this MySql query

I have a MySql query that take a very long time to run (about 7 seconds). 我有一个MySql查询,需要很长时间才能运行(大约7秒)。 The problem seems to be with the OR in this part of the query: "(tblprivateitem.userid=?userid OR tblprivateitem.userid=1)". 问题似乎出在查询的此部分中的OR:“((tblprivateitem.userid =?userid OR tblprivateitem.userid = 1)”。 If I skip the "OR tblprivateitem.userid=1" part it takes only 0.01 seconds. 如果跳过“ OR tblprivateitem.userid = 1”部分,则只需0.01秒。 As I need that part I need to find a way to optimize this query. 由于需要这一部分,因此我需要找到一种优化此查询的方法。 Any ideas? 有任何想法吗?

QUERY: 查询:

SELECT 
    tbladdeditem.addeditemid,
    tblprivateitem.iitemid,
    tblprivateitem.itemid
FROM tbladdeditem 
INNER JOIN tblprivateitem 
    ON tblprivateitem.itemid=tbladdeditem.itemid 
        AND (tblprivateitem.userid=?userid OR tblprivateitem.userid=1)
WHERE tbladdeditem.userid=?userid

EXPLAIN: 说明:

id    select_type    table            type    possible_keys    key    key_len    ref                    rows    extra
1     SIMPLE         tbladdeditem     ref     userid           userid 4          const                  293     Using where
1     SIMPLE         tblprivateitem   ref     userid,itemid    itemid 4          tbladdeditem.itemid    2       Using where

TABLES: 表:

tbladdeditem contains 1 100 000 rows: tbladdeditem包含1100万行:

CREATE TABLE `tbladdeditem` (
    `addeditemid` int(11) NOT NULL auto_increment,
    `itemid` int(11) default NULL,
    `userid` mediumint(9) default NULL,
    PRIMARY KEY  (`addeditemid`),
    KEY `userid` (`userid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

tblprivateitem contains 2 700 000 rows: tblprivateitem包含2700 000行:

CREATE TABLE `tblprivateitem` (
    `privateitemid` int(11) NOT NULL auto_increment,
    `userid` mediumint(9) default '1',
    `itemid` int(10) NOT NULL,
    `iitemid` mediumint(9) default NULL,
    PRIMARY KEY  (`privateitemid`),
    KEY `userid` (`userid`),
    KEY `itemid` (`itemid`) //Changed this index to only use itemid instead
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

I would try this instead, on your original JOIN you have an OR associated with a parameter, move that to your WHERE clause. 我会尝试这样做,在您原始的JOIN您有一个与参数关联的OR ,将其移至WHERE子句。

SELECT 
    tbladdeditem.addeditemid,
    tblprivateitem.iitemid,
    tblprivateitem.itemid
FROM tbladdeditem 
INNER JOIN tblprivateitem 
    ON tblprivateitem.itemid=tbladdeditem.itemid 
WHERE tbladdeditem.userid=?userid
    AND (tblprivateitem.userid=?userid OR tblprivateitem.userid=1)

Since you have the predicate condition tbladdeditem.userid=?userid in the where clause I don't think you need it in the join condition.. Try removing it from the join condition and (If you are using the Or to handle the case where the parameter is null, then use Coalesce instead of OR ) if not leave it as an Or 由于您在where子句中有谓词条件tbladdeditem.userid=?userid ,因此我不认为您在tbladdeditem.userid=?userid条件中需要它。请尝试从tbladdeditem.userid=?userid条件tbladdeditem.userid=?userid其删除,(如果您使用Or来处理where参数为null,如果不将其保留为Or,则使用Coalesce代替OR

-- If Or is to provide default for when (?userid is null...
      SELECT  a.addeditemid, p.iitemid,  p.itemid 
      FROM tbladdeditem a 
        JOIN tblprivateitem p
          ON p.itemid=a.itemid 
      WHERE a.userid=?userid 
         AND p.userid=Coalesce(?userid, 1)
-- if not then
      SELECT  a.addeditemid, p.iitemid,  p.itemid 
      FROM tbladdeditem a 
        JOIN tblprivateitem p
          ON p.itemid=a.itemid 
      WHERE a.userid=?userid 
         AND (p.userid=?userid Or p.userid = 1)

Second, if there is not an index on the userId column in these two tables, consider adding one. 其次,如果在这两个表的userId列上没有索引,请考虑添加一个。

Finally, if these all fail, try converting to two separate queries and unioning them together: 最后,如果所有这些均失败,请尝试转换为两个单独的查询并将它们合并在一起:

      Select  a.addeditemid, p.iitemid,  p.itemid 
      From tbladdeditem a 
        Join tblprivateitem p
          On p.itemid=a.itemid 
            And p.userId = a.Userid
      Where p.userid=?userid 
      Union
      Select a.addeditemid, p.iitemid,  p.itemid 
      From tbladdeditem a 
        Join tblprivateitem p
          On p.itemid=a.itemid 
            And p.userId = a.Userid
      Where p.userid = 1

UPDATE UPDATE

I made my queries and schema match your original question exactly, multi-column key and all. 我让我的查询和模式完全匹配您的原始问题,多列键以及所有其他内容。 The only possible difference is that I populated each table with two million entries. 唯一可能的区别是,我在每个表中填充了200万个条目。 My query (your query) runs in 0.15 seconds. 我的查询(您的查询)在0.15秒内运行。

delimiter $$
set @userid = 6
$$
SELECT 
    tbladdeditem.addeditemid,    tblprivateitem.iitemid,    tblprivateitem.itemid
FROM tbladdeditem 
INNER JOIN tblprivateitem 
    ON tblprivateitem.itemid=tbladdeditem.itemid 
        AND (tblprivateitem.userid=@userid or tblprivateitem.userid = 1)
WHERE tbladdeditem.userid=@userid

I have the same explain that you do, and with my data, my query return over a thousand matches without any issue at all. 我的解释与您所做的相同,根据我的数据,我的查询返回了1000多个匹配项,而没有任何问题。 Being completely at a loss, as you really shouldn't be having these issues -- is it possible you are running a very limiting version of MySQL? 完全不知所措,因为您实际上不应该遇到这些问题-运行的MySQL版本非常有限吗? Are you running 64-bit? 您正在运行64位吗? Plenty of memory? 有足够的记忆力吗?

I had made the assumption that your query wasn't performing well, and when mine was, assumed I had fixed you problem. 我已经假设您的查询效果不佳,而当我的查询成功时,我已经解决了您的问题。 So now I eat crow. 所以现在我吃乌鸦了。 I'll post some of the avenues I went down. 我会发布一些我走的路。 But I'm telling you, your query the way you posted it originally works just fine. 但我告诉您,您查询的发布方式本来就很好。 I can only imagine your MySQL thrashing to the hard drive or something. 我只能想象您的MySQL撞击硬盘或其他东西。 Sorry I couldn't be more help. 对不起,我无法提供更多帮助。

PREVIOUS RESPONSE (Which is also an update) 上一个响应(这也是更新)

I broke down and recreated your problem in my own database. 我崩溃了,并在自己的数据库中重新创建了您的问题。 After trying independent indexes on userid and on itemid I was unable to get the query below a few seconds, so I set up very specific multi-column keys as directed by the query. 在尝试对useriditemid独立索引后,我无法在几秒钟内得到查询,因此我按照查询的指示设置了非常具体的多列键。 Notice on tbladdeditem the multi-column query begins with itemid while on the tblprivateitem the columns are reversed: 请注意,在tbladdeditem ,多列查询以itemid开头,而在tblprivateitem ,列则相反:

Here is the schema I used: 这是我使用的架构:

CREATE TABLE `tbladdeditem` (
  `addeditemid` int(11) NOT NULL AUTO_INCREMENT,
  `itemid` int(11) NOT NULL,
  `userid` mediumint(9) NOT NULL,
  PRIMARY KEY (`addeditemid`),
  KEY `userid` (`userid`),
  KEY `i_and_u` (`itemid`,`userid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE `tblprivateitem` (
  `privateitemid` int(11) NOT NULL AUTO_INCREMENT,
  `userid` mediumint(9) NOT NULL DEFAULT '1',
  `itemid` int(10) NOT NULL,
  `iitemid` mediumint(9) NOT NULL,
  PRIMARY KEY (`privateitemid`),
  KEY `userid` (`userid`),
  KEY `itemid` (`itemid`),
  KEY `u_and_i` (`userid`,`itemid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

I filled each table with 2 million entries of random data. 我在每个表中填充了200万个随机数据条目。 I made some assumptions: 我做了一些假设:

  • userid varies from 1 to 2000 用户名从1到2000不等
  • itemid varies between 1 and 10000 itemid在1到10000之间变化

This gives each user about a thousand entries in each table. 这使每个用户在每个表中都有大约一千个条目。

Here are two versions of the query (I'm using workbench for my editor): 这是查询的两个版本(我在编辑器中使用工作台):

Version 1 - do all the filtering on the join. 版本1-对联接执行所有过滤。

Result: 0.016 seconds to return 1297 rows 结果:0.016秒返回1297行

delimiter $$
set @userid = 3
$$
SELECT 
    a.addeditemid,
    p.iitemid,
    p.itemid
FROM tblprivateitem as p
INNER JOIN tbladdeditem as a
    ON (p.userid in (1, @userid))
        AND p.itemid = a.itemid 
        AND a.userid = @userid
$$

Here's the explain: 这是解释:

EXPLAIN: 
id select_type table type  key     ref  rows extra
1  SIMPLE      p     range u_and_i      2150 Using where; Using index
1  SIMPLE      a     ref   i_and_u      1    Using where; Using index

Version 2 - filter up front 版本2-预先过滤

Result: 0.015 seconds to return 1297 rows 结果:0.015秒返回1297行

delimiter $$
set @userid = 3
$$
SELECT 
    a.addeditemid,
    p.iitemid,
    p.itemid
from 
  (select userid, itemid, iitemid from tblprivateitem 
      where userid in (1, @userid)) as p
  join tbladdeditem as a on p.userid = a.userid and a.itemid = p.itemid;
where a.userid = @userid
$$

Here's the explain: 这是解释:

id select_type table      type  key     ref               rows extra
1  PRIMARY     <derived2> ALL   null    null              2152
1  PRIMARY     a          ref   i_and_u p.itemid,const    1    Using where; Using index
2  DERIVED     p1         range u_and_i                   2150 Using where

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM