MySQL 未正确选择行(有时)
MySQL is not correctly selecting rows (sometimes)
这是对这个问题的更新,我在其中四处寻找,试图弄清楚到底发生了什么:
我最终在那里接受了一个答案,因为它确实回答了我提出的问题 ("why might this happen"),尽管它没有回答我真正想知道的问题 ("why is this happening to me")。 但是我已经设法缩小了后一个问题的范围,并且我认为我可以明确地说出了一些我不理解也从未见过的错误之前。
这个问题真的很难调试,因为出于我无法理解的原因,登录数据库会自动修复它。但是,今天我在终端中打开 MySQL 会话时设法触发了有问题的状态。以下是该会话中的一些查询和后续响应:
首先,这是我的 table 布局:
mysql> describe forum_posts;
+-----------+------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------+------------+------+-----+---------+----------------+
| post_id | int(11) | NO | PRI | NULL | auto_increment |
| thread_id | int(11) | YES | MUL | NULL | |
| forum_id | int(11) | YES | MUL | NULL | |
| user_id | int(11) | YES | MUL | NULL | |
| moderator | tinyint(1) | NO | | 0 | |
| message | mediumtext | YES | MUL | NULL | |
| date | int(11) | NO | MUL | NULL | |
| edited | int(11) | YES | | NULL | |
| deleted | tinyint(1) | YES | MUL | 0 | |
| bbcode | tinyint(1) | NO | | 1 | |
+-----------+------------+------+-----+---------+----------------+
10 rows in set (0.00 sec)
现在,让我们看看给定的论坛帖子中有多少帖子:
mysql> SELECT count(post_id) as num FROM `forum_posts` where thread_id=5243;
+-----+
| num |
+-----+
| 195 |
+-----+
1 row in set (0.00 sec)
好的,但我只想要没有设置 deleted
标志的论坛帖子:
mysql> SELECT count(post_id) as num FROM `forum_posts` where thread_id=5243 and deleted=0;
+-----+
| num |
+-----+
| 0 |
+-----+
1 row in set (0.06 sec)
mysql> select post_id,deleted from forum_posts where thread_id=5243 and deleted=0;
Empty set (0.06 sec)
好的,让我们再次确保它们实际上并没有全部删除:
mysql> select post_id,deleted from forum_posts where thread_id=5243;
+---------+---------+
| post_id | deleted |
+---------+---------+
| 104081 | 0 |
| 104082 | 0 |
[snip]
| 121162 | 0 |
| 121594 | 0 |
+---------+---------+
195 rows in set (0.00 sec)
table 中的每一行都将 'deleted' 设置为 0,但向查询添加 and deleted=0
不会产生任何结果。直到我通过从终端 window 再次登录到 MySQL 打开一个新会话,之后我可以再次正确 select 行,其中 'deleted' 是 0.
到底是什么?
更新:
@miken32 在下面的评论中建议我尝试 EXPLAIN SELECT ...
,所以:
mysql> explain select post_id,deleted from forum_posts where thread_id='5243' and deleted=0;
+----+-------------+-------------+-------------+-------------------+-------------------+---------+------+------+--------------------------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------------+-------------+-------------------+-------------------+---------+------+------+--------------------------------------------------------------+
| 1 | SIMPLE | forum_posts | index_merge | thread_id,deleted | thread_id,deleted | 5,2 | NULL | 97 | Using intersect(thread_id,deleted); Using where; Using index |
+----+-------------+-------------+-------------+-------------------+-------------------+---------+------+------+--------------------------------------------------------------+
1 row in set (0.00 sec)
根据使用 FORCE KEY 改变查询结果的评论,我们很可能正在处理合并优化器错误。原始查询的 EXPLAIN 显示优化是通过从已删除的键中选择,然后从 post_id 键中选择,然后合并结果来完成的。当我们强制绕过该代码时,问题就消失了。
起点步骤:
- 使用最新的 5.6 版本 MySQL
在相同数据上尝试
- 如果问题重现,请尝试将其隔离到最小的测试用例,访问 http://bugs.mysql.com/ 并报告错误
驱魔除魔!添加此索引以避免任何 "merge" 错误:
INDEX(deleted, thread_id) and DROP the key on just deleted
标志上的索引几乎总是无用的。这次还不如废了
这将比 FORCE INDEX 更便宜、更快、更安全。
这是对这个问题的更新,我在其中四处寻找,试图弄清楚到底发生了什么:
我最终在那里接受了一个答案,因为它确实回答了我提出的问题 ("why might this happen"),尽管它没有回答我真正想知道的问题 ("why is this happening to me")。 但是我已经设法缩小了后一个问题的范围,并且我认为我可以明确地说出了一些我不理解也从未见过的错误之前。
这个问题真的很难调试,因为出于我无法理解的原因,登录数据库会自动修复它。但是,今天我在终端中打开 MySQL 会话时设法触发了有问题的状态。以下是该会话中的一些查询和后续响应:
首先,这是我的 table 布局:
mysql> describe forum_posts;
+-----------+------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------+------------+------+-----+---------+----------------+
| post_id | int(11) | NO | PRI | NULL | auto_increment |
| thread_id | int(11) | YES | MUL | NULL | |
| forum_id | int(11) | YES | MUL | NULL | |
| user_id | int(11) | YES | MUL | NULL | |
| moderator | tinyint(1) | NO | | 0 | |
| message | mediumtext | YES | MUL | NULL | |
| date | int(11) | NO | MUL | NULL | |
| edited | int(11) | YES | | NULL | |
| deleted | tinyint(1) | YES | MUL | 0 | |
| bbcode | tinyint(1) | NO | | 1 | |
+-----------+------------+------+-----+---------+----------------+
10 rows in set (0.00 sec)
现在,让我们看看给定的论坛帖子中有多少帖子:
mysql> SELECT count(post_id) as num FROM `forum_posts` where thread_id=5243;
+-----+
| num |
+-----+
| 195 |
+-----+
1 row in set (0.00 sec)
好的,但我只想要没有设置 deleted
标志的论坛帖子:
mysql> SELECT count(post_id) as num FROM `forum_posts` where thread_id=5243 and deleted=0;
+-----+
| num |
+-----+
| 0 |
+-----+
1 row in set (0.06 sec)
mysql> select post_id,deleted from forum_posts where thread_id=5243 and deleted=0;
Empty set (0.06 sec)
好的,让我们再次确保它们实际上并没有全部删除:
mysql> select post_id,deleted from forum_posts where thread_id=5243;
+---------+---------+
| post_id | deleted |
+---------+---------+
| 104081 | 0 |
| 104082 | 0 |
[snip]
| 121162 | 0 |
| 121594 | 0 |
+---------+---------+
195 rows in set (0.00 sec)
table 中的每一行都将 'deleted' 设置为 0,但向查询添加 and deleted=0
不会产生任何结果。直到我通过从终端 window 再次登录到 MySQL 打开一个新会话,之后我可以再次正确 select 行,其中 'deleted' 是 0.
到底是什么?
更新:
@miken32 在下面的评论中建议我尝试 EXPLAIN SELECT ...
,所以:
mysql> explain select post_id,deleted from forum_posts where thread_id='5243' and deleted=0;
+----+-------------+-------------+-------------+-------------------+-------------------+---------+------+------+--------------------------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------------+-------------+-------------------+-------------------+---------+------+------+--------------------------------------------------------------+
| 1 | SIMPLE | forum_posts | index_merge | thread_id,deleted | thread_id,deleted | 5,2 | NULL | 97 | Using intersect(thread_id,deleted); Using where; Using index |
+----+-------------+-------------+-------------+-------------------+-------------------+---------+------+------+--------------------------------------------------------------+
1 row in set (0.00 sec)
根据使用 FORCE KEY 改变查询结果的评论,我们很可能正在处理合并优化器错误。原始查询的 EXPLAIN 显示优化是通过从已删除的键中选择,然后从 post_id 键中选择,然后合并结果来完成的。当我们强制绕过该代码时,问题就消失了。
起点步骤:
- 使用最新的 5.6 版本 MySQL 在相同数据上尝试
- 如果问题重现,请尝试将其隔离到最小的测试用例,访问 http://bugs.mysql.com/ 并报告错误
驱魔除魔!添加此索引以避免任何 "merge" 错误:
INDEX(deleted, thread_id) and DROP the key on just deleted
标志上的索引几乎总是无用的。这次还不如废了
这将比 FORCE INDEX 更便宜、更快、更安全。