更快的方式在MySQL中使用集合

时间:2009-05-25 16:28:21

标签: sql mysql optimization performance set

我有一个MySQL 5.1 InnoDB表(customers),结构如下:

int         record_id (PRIMARY KEY)
int         user_id (ALLOW NULL)
varchar[11] postcode (ALLOW NULL)
varchar[30] region (ALLOW NULL)
..
..
..

表中大约有700万行。目前,该表正在被查询:

SELECT * FROM customers WHERE user_id IN (32343, 45676, 12345, 98765, 66010, ...

在实际查询中,目前超过560 user_id个在IN子句中。表中有数百万条记录,此查询

桌面上有二级索引,第一个索引位于user_id本身,我认为这会有所帮助。

我知道SELECT(*)是一件坏事,这将扩展到所需字段的完整列表。但是,上面未列出的字段更多是intdouble s。还有另外50个被退回,但报告需要

我想有更好的方法来访问user_id的数据,但我想不出怎么做。我最初的反应是删除ALLOW NULL字段上的user_id,因为我了解NULL处理会降低查询速度吗?

如果你能指出一个比使用IN ( )方法更有效的方向,我将非常感激。

修改 冉EXPLAIN,说:

select_type = SIMPLE 
table = customers 
type = range 
possible_keys = userid_idx 
key = userid_idx 
key_len = 5 
ref = (NULL) 
rows = 637640 
Extra = Using where 

有帮助吗?

5 个答案:

答案 0 :(得分:3)

首先,检查USER_ID 上是否有索引并确保已使用

您可以通过运行EXPLAIN来完成此操作。

其次,创建一个临时表并在JOIN

中使用它
CREATE TABLE temptable (user_id INT NOT NULL)

SELECT  *
FROM    temptable t
JOIN    customers c
ON      c.user_id = t.user_id

第三,您的查询可能会返回哪些行?

如果它几乎返回所有行,那么它就会很慢,因为它必须通过连接通道抽取所有这些数百万个。

NULL不会减慢您的查询速度,因为IN条件只满足索引的非NULL值。

<强>更新

使用索引,计划很好,但它返回超过50万行。

您真的需要将所有这些638,000行放入报告吗?

希望不打印:对雨林,全球变暖和其他东西不利。

严肃地说,您似乎需要对查询进行聚合或分页。

答案 1 :(得分:2)

“选择*”并不像有些人想的那么糟糕;基于行的数据库将在获取任何行时获取整行,因此在您不使用覆盖索引的情况下,“SELECT *”基本上不比“SELECT a,b,c”慢(NB:那里有大型BLOB时有时会出现异常,但这是一个边缘情况。)

首先要做的事情 - 您的数据库是否适合RAM?如果没有,请获得更多内存。不,真的。现在,假设您的数据库太大而无法合理地放入ram(Say,> 32Gb),您应该尝试减少随机I / O的数量,因为它们可能正在阻碍。

我将从这里开始假设您正在运行适当的服务器级硬件,RAID1(或RAID10等)中的RAID控制器和至少两个主轴。如果你不是,那就去吧。

你绝对可以考虑使用聚集索引。在MySQL InnoDB中,您只能对主键进行群集,这意味着如果其他内容当前是主键,则您必须对其进行更改。复合主键是可以的,如果你在一个标准(比如user_id)上做了很多查询,那么将它作为主键的第一部分是一个明确的好处(你需要添加其他东西才能使它成为主键)唯一的)。

或者,您可以使查询使用覆盖索引,在这种情况下,您不需要user_id作为主键(事实上,它不能是)。只有当您需要的所有列都在以user_id开头的索引中时才会发生这种情况。

就查询效率而言,WHERE user_id IN(ID的大列表)几乎肯定是从SQL执行此操作的最有效方式。

但我最大的提示是:

  • 记住目标,弄清楚目标是什么,当你达到目标时,停止。
  • 不要接受任何人的话 - 试试看
  • 确保您的性能测试系统与生产相同的硬件规格
  • 确保您的性能测试系统具有与生产相同的数据大小和种类(相同的架构不够好!)。
  • 如果无法使用生产数据,请使用合成数据(复制生产数据可能在逻辑上很困难(请记住您的数据库> 32Gb);它也可能违反安全策略。)
  • 如果您的查询是最佳的(因为它可能已经是),请尝试调整架构,然后调整数据库本身。

答案 2 :(得分:1)

它们是相同的〜每次560个id?或者在不同的查询运行中它是不同的~500个ID?

你可以将560个UserID插入一个单独的表(甚至临时表)中,在该表上粘贴一个索引,然后将其连接到原始表。

答案 3 :(得分:1)

这是您最重要的查询吗?这是一个交易表吗?

如果是这样,请尝试在user_id上创建聚簇索引。您的查询可能会很慢,因为它仍然必须使随机磁盘读取来检索列(键查找),即使在找到匹配的记录(对user_Id索引的索引搜索)之后也是如此。

如果无法更改聚簇索引,则可能需要考虑ETL过程(最简单的是插入具有最佳索引的另一个表的触发器)。这应该会产生更快的结果。

另请注意,此类大型查询可能需要一些时间来解析,因此,如果可能,请将查询的ID放入临时表中以帮助解决此问题

答案 4 :(得分:0)

您可以尝试在临时表和内部联接两个表中插入需要查询的ID。我不知道这是否有帮助。