gpt4 book ai didi

mysql - 为什么索引使这个查询变慢?

转载 作者:可可西里 更新时间:2023-11-01 06:54:28 25 4
gpt4 key购买 nike

表有 1 500 000 条记录,其中 1 250 000 条的字段为“z”。
我需要选择随机而不是“z”字段。

$random = mt_rand(1, 250000);  
$query = "SELECT field FROM table WHERE field != 'z' LIMIT $random, 1";

它工作正常。

然后我决定优化它并在表中索引字段

结果很奇怪 - 慢了 ~3 倍。我测试过了。
为什么它更慢?这样的索引不是应该让它更快吗?

我的 ISAM

explain with index:  
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE table range field field 758 NULL 1139287 Using

explain without index:
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE table ALL NULL NULL NULL NULL 1484672 Using where

最佳答案

总结

问题在于,由于 b-trees 的性质,field 不是一个好的索引候选者。

解释

假设您有一个表,其中包含 500,000 次掷硬币的结果,其中掷的结果是 1(正面)或 0(反面):

CREATE TABLE toss (
id int NOT NULL AUTO_INCREMENT,
result int NOT NULL DEFAULT '0',
PRIMARY KEY ( id )
)

select result, count(*) from toss group by result order by result;
+--------+----------+
| result | count(*) |
+--------+----------+
| 0 | 250290 |
| 1 | 249710 |
+--------+----------+
2 rows in set (0.40 sec)

如果您想(随机)选择一次抛掷,其中抛掷是反面,那么您需要搜索您的表格,随机选择一个起始位置。

select * from toss where result != 1 limit 123456, 1;
+--------+--------+
| id | result |
+--------+--------+
| 246700 | 0 |
+--------+--------+
1 row in set (0.06 sec)

explain select * from toss where result != 1 limit 123456, 1;
+----+-------------+-------+------+---------------+------+---------+------+--------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+--------+-------------+
| 1 | SIMPLE | toss | ALL | NULL | NULL | NULL | NULL | 500000 | Using where |
+----+-------------+-------+------+---------------+------+---------+------+--------+-------------+

您会看到,您基本上是在按顺序搜索所有行以找到匹配项。

如果您在 toss 字段上创建索引,那么您的索引将包含两个值,每个值大约有 250,000 个条目。

create index foo on toss ( result );
Query OK, 500000 rows affected (2.48 sec)
Records: 500000 Duplicates: 0 Warnings: 0

select * from toss where result != 1 limit 123456, 1;
+--------+--------+
| id | result |
+--------+--------+
| 246700 | 0 |
+--------+--------+
1 row in set (0.25 sec)

explain select * from toss where result != 1 limit 123456, 1;
+----+-------------+-------+-------+---------------+------+---------+------+--------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------+---------------+------+---------+------+--------+-------------+
| 1 | SIMPLE | toss | range | foo | foo | 4 | NULL | 154565 | Using where |
+----+-------------+-------+-------+---------------+------+---------+------+--------+-------------+

现在您搜索的记录减少了,但搜索时间从 0.06 秒增加到 0.25 秒。为什么?因为顺序扫描索引实际上比顺序扫描表效率低,对于给定键具有大量行的索引。

让我们看看这个表上的索引:

show index from toss;
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment |
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
| toss | 0 | PRIMARY | 1 | id | A | 500000 | NULL | NULL | | BTREE | |
| toss | 1 | foo | 1 | result | A | 2 | NULL | NULL | | BTREE | |
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+

PRIMARY 索引是一个很好的索引:有 500,000 行,有 500,000 个值。排列成BTREE,可以根据id快速识别单行。

foo 索引是一个错误的索引:有 500,000 行,但只有 2 个可能的值。这几乎是 BTREE 最糟糕的情况——搜索索引的所有开销,并且仍然必须搜索结果。

关于mysql - 为什么索引使这个查询变慢?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3375853/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com