2

我有一张有 4,000,000 条记录的表。创建表: (user_id int, partner_id int, PRIMARY_KEY ( user_id )) engine=InnoDB; 我想测试select100 条记录的性能。然后,我测试了以下内容:

mysql> explain select user_id from MY_TABLE use index (PRIMARY)  where user_id IN ( 1 );
+----+-------------+----------+-------+---------------+---------+---------+-------+------+-------------+
| id | select_type | table    | type  | possible_keys | key     | key_len | ref   | rows | Extra       |
+----+-------------+----------+-------+---------------+---------+---------+-------+------+-------------+
|  1 | PRIMARY     | MY_TABLE | const | PRIMARY       | PRIMARY | 4       | const |    1 | Using index |
+----+-------------+----------+-------+---------------+---------+---------+-------+------+-------------+
1 row in set, 1 warning (0.00 sec)

还行吧。但是,这个查询是由 mysql 缓冲的。所以,这个测试在第一次测试之后没有。

然后,我想到了一个通过随机值选择的sql。我测试了以下内容:

mysql> explain select user_id from MY_TABLE use index (PRIMARY)  where user_id IN ( select ceil( rand() ) );
+----+-------------+----------+-------+---------------+---------+---------+------+---------+--------------------------+
| id | select_type | table    | type  | possible_keys | key     | key_len | ref  | rows    | Extra                    |
+----+-------------+----------+-------+---------------+---------+---------+------+---------+--------------------------+
|  1 | PRIMARY     | MY_TABLE | index | NULL          | PRIMARY | 4       | NULL | 3998727 | Using where; Using index |
+----+-------------+----------+-------+---------------+---------+---------+------+---------+--------------------------+

但是,很糟糕。 Explain表明 possible_keys 为 NULL。因此,计划了全索引扫描,实际上,它比之前的速度太慢了。

然后,我想请你教我如何用索引查找写随机值。

谢谢

4

3 回答 3

1

在 SQL 中使用rand()通常是使查询变慢的可靠方法。这里的一个共同主题是人们使用它ORDER BY来获得随机序列。它很慢,因为它不仅会丢弃索引,而且还会读取整个表。

但是,在您的情况下,函数调用在子查询中的事实应该允许外部查询仍然使用其索引。事实上,这似乎并不奇怪(所以我给了这个问题+1 票)。

我的理论是,也许 MySQL 的优化器弄错了——它看到了内部查询中的函数,并错误地决定它不能使用索引。

我唯一可以建议解决的方法是使用force index推动 MySQL 使用您想要的索引。

于 2013-03-17T14:03:09.170 回答
0

请参阅rand()的定义。

如果我理解正确,您正在尝试从数据库中获取随机记录。如果是这种情况,再次从 rand() 定义:

ORDER BY RAND() 结合 LIMIT 对于从一组行中选择随机样本很有用:

SELECT * FROM table1, table2 WHERE a=b AND c<d -> ORDER BY RAND() LIMIT 1000;

于 2013-03-17T14:42:35.770 回答
0

这是 MySQL 优化器的一个限制,它不能告诉子查询只返回一个值,它必须假设子查询返回具有不可预测值的多行,甚至可能是 user_id 的所有值。因此它决定只进行索引扫描。

这是一个解决方法:

mysql> explain select user_id from MY_TABLE use index (PRIMARY)  
where user_id = ( select ceil( rand() ) );

请注意,MySQL 的 RAND() 函数返回 range 中的值0 <= v < 1.0。如果你 CEIL() 它,你可能会得到值 1。因此你几乎总是会得到 user_id=1 的行。如果您的表中没有这样的行,您将得到一个空集结果。您当然不会在所有用户中随机选择一个用户。

要解决这个问题,您必须将 rand() 乘以不同的 user_id 值的数量。这带来了您可能存在差距的问题,因此随机选择的值不会与任何现有的 user_id 匹配。


回复您的评论:

当您进行索引扫描时,您总是会看到可能的键为 NULL(即“类型”是“索引”)。

我在一个类似的表上尝试了你的解释查询,看来优化器无法确定子查询是一个常量表达式。您可以通过计算应用程序代码中的随机数,然后在查询中将结果用作常量值来解决此限制:

select user_id from MY_TABLE use index (PRIMARY)  
where user_id = $random;
于 2013-03-17T14:54:49.333 回答