MySQL 服务器在执行简单查询后挂起
MySQL server hangs after executing a simple query
我在 ARM 机器上有一个 MySQL 服务器 运行ning,我有一个查询 运行 in phpmyadmin
通过 Web 界面(或 php 在网站上)
select * from some_table where id IN (select id from other_table)
这个简单的查询将导致 MySQL 服务挂起,直到重新启动。
这是什么原因?
换句话说:
如果我手动输入 ID(无论是 1 还是 100)都不会失败:
select * from some_table where id IN (1,2,3,4,5,6,7,8,9....120)
另一方面 - 如果 ID 是从内部查询生成的,则服务将挂起:
select * from some_table where id IN (select id from other_table)
我可以完全访问服务器,并且可以根据需要重新配置它。我该如何解决这个问题?
数据库服务器
Server: Localhost via UNIX socket
Server type: MySQL
Server version: 5.5.35-1ubuntu1 - (Ubuntu)
Protocol version: 10
User: root@localhost
Server charset: UTF-8 Unicode (utf8)
网络服务器
nginx/1.4.6
Database client version: libmysql - 5.5.35
PHP extension: mysqli
phpMyAdmin
Version information: 4.0.10deb1
P.S。我所说的挂起是指 mysql
变得没有反应。命令 top
将显示 MySQL 服务正在耗尽所有核心。通过命令 sudo /etc/init.d/mysql restart
.
重新启动
P.P.S。挂起的完整查询。 fiddle: http://sqlfiddle.com/#!9/84fe8/1
select
goo.t1_score,
goo.t2_score,
gr.t1_score,
gr.t2_score,
gr.unique_key
from games_ongoing goo, game_results gr
where
goo.id in (
select max(id)
from games_ongoing
group by unique_key
having count(id) >= 3 and MIN(t1_score) = MIN(t2_score) and MIN(t1_score) = 0
)
and gr.unique_key = goo.unique_key
所以,回顾一下...这是一个中间结果
SELECT o.id o_id
, o.t1_score o_t1_score
, o.t2_score o_t2_score
, r.t1_score r_t1_score
, r.t2_score r_t2_score
, r.unique_key
FROM games_ongoing o
JOIN game_results r
ON r.unique_key = o.unique_key;
+------+------------+------------+------------+------------+------------+
| o_id | o_t1_score | o_t2_score | r_t1_score | r_t2_score | unique_key |
+------+------------+------------+------------+------------+------------+
| 1 | 0 | 0 | 1 | 3 | 111 |
| 2 | 0 | 1 | 1 | 3 | 111 |
| 3 | 0 | 2 | 1 | 3 | 111 |
| 4 | 1 | 2 | 1 | 3 | 111 |
| 5 | 1 | 2 | 1 | 3 | 111 |
| 6 | 0 | 0 | 5 | 4 | 222 |
| 7 | 1 | 1 | 5 | 4 | 222 |
| 8 | 2 | 1 | 5 | 4 | 222 |
+------+------------+------------+------------+------------+------------+
为了获得具有最高(持续)id 的唯一键,我们可以使用不相关的子查询,如下所示:
SELECT x.*
, r.*
FROM games_ongoing x
JOIN
( SELECT unique_key, MAX(id) max_id FROM games_ongoing GROUP BY unique_key ) y
ON y.unique_key = x.unique_key
AND y.max_id = x.id
JOIN game_results r
ON r.unique_key = x.unique_key;
+----+---------+---------+------------+----------+----------+----+----------+----------+------------+
| id | t1_name | t2_name | unique_key | t1_score | t2_score | id | t1_score | t2_score | unique_key |
+----+---------+---------+------------+----------+----------+----+----------+----------+------------+
| 5 | team1 | team2 | 111 | 1 | 2 | 1 | 1 | 3 | 111 |
| 8 | team3 | team4 | 222 | 2 | 1 | 2 | 5 | 4 | 222 |
+----+---------+---------+------------+----------+----------+----+----------+----------+------------+
这应该比上面的查询执行得更好,但是为了进一步帮助优化它,我们需要查看 EXPLAIN。
回答你原来的问题。在 MySQL 中,此查询:
select * from some_table where id IN (select id from other_table)
将执行以下查询:
SELECT id
FROM other_table
WHERE other_table.id = some_table.id
对于 some_table
中的每条记录,而这条:
select * from some_table where id IN (1,2,3,4,5,6,7,8,9....120)
将从 some_table
中的列表中寻找每个值。如果 some_table
上有很多记录,但 other_table
上的记录很少,你应该像这样重写查询:
SELECT st.*
FROM (
SELECT DISTINCT
id
FROM other_table
) ot
JOIN some_table st
ON st.id = ot.id
这将在 some_table.id
的索引中搜索每个不同的 other_table.id
。
现在,假设没有负分,你应该运行这个查询:
SELECT *
FROM game_results gr
JOIN games_ongoing goo
ON goo.id =
(
SELECT id
FROM games_ongoing goi
WHERE goi.unique_key = gr.unique_key
AND EXISTS
(
SELECT NULL
FROM games_ongoing goz
WHERE goz.unique_key = goi.unique_key
AND (goz.t1_score, goz.t2_score) = (0, 0)
)
AND EXISTS
(
SELECT NULL
FROM games_ongoing goc
WHERE goc.unique_key = goi.unique_key
LIMIT 2, 1
)
ORDER BY
goi.unique_key DESC, goi.id DESC
LIMIT 1
)
为查询快速运行创建以下索引:
CREATE INDEX ix_game_results_unique_key ON game_results (unique_key);
CREATE INDEX ix_games_ongoing_unique_key ON games_ongoing (unique_key);
CREATE INDEX ix_games_ongoing_unique_key_t2_score_t2_score ON games_ongoing (unique_key, t1_score, t2_score);
参见fiddle:http://sqlfiddle.com/#!9/72444/1
我在 ARM 机器上有一个 MySQL 服务器 运行ning,我有一个查询 运行 in phpmyadmin
通过 Web 界面(或 php 在网站上)
select * from some_table where id IN (select id from other_table)
这个简单的查询将导致 MySQL 服务挂起,直到重新启动。 这是什么原因?
换句话说:
如果我手动输入 ID(无论是 1 还是 100)都不会失败:
select * from some_table where id IN (1,2,3,4,5,6,7,8,9....120)
另一方面 - 如果 ID 是从内部查询生成的,则服务将挂起:
select * from some_table where id IN (select id from other_table)
我可以完全访问服务器,并且可以根据需要重新配置它。我该如何解决这个问题?
数据库服务器
Server: Localhost via UNIX socket
Server type: MySQL
Server version: 5.5.35-1ubuntu1 - (Ubuntu)
Protocol version: 10
User: root@localhost
Server charset: UTF-8 Unicode (utf8)
网络服务器
nginx/1.4.6
Database client version: libmysql - 5.5.35
PHP extension: mysqli
phpMyAdmin
Version information: 4.0.10deb1
P.S。我所说的挂起是指 mysql
变得没有反应。命令 top
将显示 MySQL 服务正在耗尽所有核心。通过命令 sudo /etc/init.d/mysql restart
.
P.P.S。挂起的完整查询。 fiddle: http://sqlfiddle.com/#!9/84fe8/1
select
goo.t1_score,
goo.t2_score,
gr.t1_score,
gr.t2_score,
gr.unique_key
from games_ongoing goo, game_results gr
where
goo.id in (
select max(id)
from games_ongoing
group by unique_key
having count(id) >= 3 and MIN(t1_score) = MIN(t2_score) and MIN(t1_score) = 0
)
and gr.unique_key = goo.unique_key
所以,回顾一下...这是一个中间结果
SELECT o.id o_id
, o.t1_score o_t1_score
, o.t2_score o_t2_score
, r.t1_score r_t1_score
, r.t2_score r_t2_score
, r.unique_key
FROM games_ongoing o
JOIN game_results r
ON r.unique_key = o.unique_key;
+------+------------+------------+------------+------------+------------+
| o_id | o_t1_score | o_t2_score | r_t1_score | r_t2_score | unique_key |
+------+------------+------------+------------+------------+------------+
| 1 | 0 | 0 | 1 | 3 | 111 |
| 2 | 0 | 1 | 1 | 3 | 111 |
| 3 | 0 | 2 | 1 | 3 | 111 |
| 4 | 1 | 2 | 1 | 3 | 111 |
| 5 | 1 | 2 | 1 | 3 | 111 |
| 6 | 0 | 0 | 5 | 4 | 222 |
| 7 | 1 | 1 | 5 | 4 | 222 |
| 8 | 2 | 1 | 5 | 4 | 222 |
+------+------------+------------+------------+------------+------------+
为了获得具有最高(持续)id 的唯一键,我们可以使用不相关的子查询,如下所示:
SELECT x.*
, r.*
FROM games_ongoing x
JOIN
( SELECT unique_key, MAX(id) max_id FROM games_ongoing GROUP BY unique_key ) y
ON y.unique_key = x.unique_key
AND y.max_id = x.id
JOIN game_results r
ON r.unique_key = x.unique_key;
+----+---------+---------+------------+----------+----------+----+----------+----------+------------+
| id | t1_name | t2_name | unique_key | t1_score | t2_score | id | t1_score | t2_score | unique_key |
+----+---------+---------+------------+----------+----------+----+----------+----------+------------+
| 5 | team1 | team2 | 111 | 1 | 2 | 1 | 1 | 3 | 111 |
| 8 | team3 | team4 | 222 | 2 | 1 | 2 | 5 | 4 | 222 |
+----+---------+---------+------------+----------+----------+----+----------+----------+------------+
这应该比上面的查询执行得更好,但是为了进一步帮助优化它,我们需要查看 EXPLAIN。
回答你原来的问题。在 MySQL 中,此查询:
select * from some_table where id IN (select id from other_table)
将执行以下查询:
SELECT id
FROM other_table
WHERE other_table.id = some_table.id
对于 some_table
中的每条记录,而这条:
select * from some_table where id IN (1,2,3,4,5,6,7,8,9....120)
将从 some_table
中的列表中寻找每个值。如果 some_table
上有很多记录,但 other_table
上的记录很少,你应该像这样重写查询:
SELECT st.*
FROM (
SELECT DISTINCT
id
FROM other_table
) ot
JOIN some_table st
ON st.id = ot.id
这将在 some_table.id
的索引中搜索每个不同的 other_table.id
。
现在,假设没有负分,你应该运行这个查询:
SELECT *
FROM game_results gr
JOIN games_ongoing goo
ON goo.id =
(
SELECT id
FROM games_ongoing goi
WHERE goi.unique_key = gr.unique_key
AND EXISTS
(
SELECT NULL
FROM games_ongoing goz
WHERE goz.unique_key = goi.unique_key
AND (goz.t1_score, goz.t2_score) = (0, 0)
)
AND EXISTS
(
SELECT NULL
FROM games_ongoing goc
WHERE goc.unique_key = goi.unique_key
LIMIT 2, 1
)
ORDER BY
goi.unique_key DESC, goi.id DESC
LIMIT 1
)
为查询快速运行创建以下索引:
CREATE INDEX ix_game_results_unique_key ON game_results (unique_key);
CREATE INDEX ix_games_ongoing_unique_key ON games_ongoing (unique_key);
CREATE INDEX ix_games_ongoing_unique_key_t2_score_t2_score ON games_ongoing (unique_key, t1_score, t2_score);
参见fiddle:http://sqlfiddle.com/#!9/72444/1