使用MySQL进行奇怪的慢查询

时间:2014-03-09 12:34:12

标签: php mysql sql performance geoip

我的表 A 包含IP范围(列startIpNum,endIpNum,locId)和表 A_location (列locaId和其他非重要列)。有以下索引 - A上的startIpNum和endIpNum,以及A_location上的locId。

问题是有时查询执行得非常慢。下面是一个mysql-slow日志文件,它包含两个查询,其中没有返回任何内容。

# Time: 140001 21:18:45    
# User@Host: root[root] @ localhost [127.0.0.1]
# Query_time: 0.023001  Lock_time: 0.000000 Rows_sent: 0  Rows_examined: 0
SET timestamp=1394367480;
SELECT * FROM A_location AS location, (SELECT * FROM A WHERE (3998482191 BETWEEN startIpNum AND endIpNum) ORDER BY startIpNum DESC LIMIT 1) AS blocks WHERE location.locId = blocks.locId;

# Time: 140309 21:18:45
# User@Host: root[root] @ localhost [127.0.0.1]
# Query_time: 54.893140  Lock_time: 0.000000 Rows_sent: 0  Rows_examined: 0
SET timestamp=1394367525;
SELECT * FROM A_location AS location, (SELECT * FROM A WHERE (2463400155 BETWEEN startIpNum AND endIpNum) ORDER BY startIpNum DESC LIMIT 1) AS blocks WHERE location.locId = blocks.locId;

这种行为可能是什么原因?

EXPLAIN结果: Result for 3998482191 Result for 2463400155

更新: 问题解决了,最终查询

ALTER TABLE A ORDER BY startIpNum ASC;
SELECT A_location.* FROM A_location AS location,
(SELECT A.* FROM A as blocks,
(SELECT * FROM A WHERE startIpNum < 24465138 ORDER BY startIpNum DESC LIMIT 1) AS startipnumquery
WHERE blocks.startIpNum = startipnumquery.startIpNum AND blocks.endIpNum > 24465138
ORDER BY blocks.endIpNum ASC LIMIT 1) as subresult
WHERE location.locId = subresult.locId;

5 个答案:

答案 0 :(得分:3)

您当前的查询我已更新如下,请运行以下查询并检查响应时间,我99.99%肯定会给您最好的结果

Step 1: ALTER TABLE A ORDER BY startIpNum DESC;


Step 2: SET timestamp=1394367480;
SELECT location.*, (SELECT * FROM A WHERE (3998482191 BETWEEN startIpNum AND endIpNum) LIMIT 1) AS blocks FROM A_location AS location, WHERE location.locId = blocks.locId;

Step 1: ALTER TABLE A ORDER BY startIpNum DESC;

Step 2: SET timestamp=1394367525;
SELECT location.*, (SELECT * FROM A WHERE (2463400155 BETWEEN startIpNum AND endIpNum) LIMIT 1) AS blocks FROM A_location AS location WHERE location.locId = blocks.locId;

我刚从您的查询和表格中删除 ORDER BY字段DESC ,并使用 ALTER TABLE A ORDER BY startIpNum DESC;

答案 1 :(得分:0)

打开简介并像Sherlock Holmes一样:

mysql> set profiling = 1;

mysql> select * from A;

mysql> show profiles;

找到您的查询(可能是id为1)并执行:

mysql> show profile for query 1;

答案 2 :(得分:0)

我不太确定您是否正在尝试获取具有最高startipnum的位置,或者所有位置以及每个位置的最高startipnum。

您当前的查询似乎是通过大量记录进行无索引搜索,这可能需要很长时间

第一次尝试这个

SELECT location.*, A.*
FROM
(
    SELECT MAX(startIpNum) AS max_startIpNum
    FROM A 
    WHERE (2463400155 BETWEEN startIpNum AND endIpNum
) blocks
INNER JOIN A
ON A.startIpNum = blocks.max_startIpNum
INNER JOIN A_location AS location
ON A.locId = location.locId

这将依赖于表A上的startIpNum索引和表A_location上的locid索引。

第二次试试这个: -

SELECT location.*, A.*
FROM A_location AS location
INNER JOIN
( 
    SELECT locId, MAX(startIpNum) AS max_startIpNum
    FROM A 
    WHERE (2463400155 BETWEEN startIpNum AND endIpNum
    GROUP BY locId
) blocks
ON location.locId = blocks.locId
INNER JOIN A
ON A.locId = blocks.locId
AND A.startIpNum = blocks.max_startIpNum

这将依赖于表A上覆盖locid和startIpNum的索引,并且表A_location上的locid索引可能很有帮助

答案 3 :(得分:0)

试试这个:

SELECT location.*, blocks.*
FROM A_location AS location 
join  A as blocks on (location.locId = blocks.locId)
WHERE startIpNum<=3998482191 AND endIpNum>=3998482191
group by block.locId
ORDER BY startIpNum DESC;

答案 4 :(得分:0)

A上应该只有一个综合索引:A(startIpNum, endIpNum)。似乎第二个查询对使用的最佳索引感到困惑。

以下内容将重写查询以使用显式join。这不应该对绩效产生影响:

SELECT *
FROM (SELECT *
      FROM A
      WHERE 3998482191 BETWEEN startIpNum AND endIpNum
      ORDER BY startIpNum DESC
      LIMIT 1
     ) blocks join
     A_location location
     on location.locId = blocks.locId;