我有一个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(*)
是一件坏事,这将扩展到所需字段的完整列表。但是,上面未列出的字段更多是int
和double
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
有帮助吗?
答案 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执行此操作的最有效方式。
但我最大的提示是:
答案 2 :(得分:1)
它们是相同的〜每次560个id?或者在不同的查询运行中它是不同的~500个ID?
你可以将560个UserID插入一个单独的表(甚至临时表)中,在该表上粘贴一个索引,然后将其连接到原始表。
答案 3 :(得分:1)
这是您最重要的查询吗?这是一个交易表吗?
如果是这样,请尝试在user_id上创建聚簇索引。您的查询可能会很慢,因为它仍然必须使随机磁盘读取来检索列(键查找),即使在找到匹配的记录(对user_Id索引的索引搜索)之后也是如此。
如果无法更改聚簇索引,则可能需要考虑ETL过程(最简单的是插入具有最佳索引的另一个表的触发器)。这应该会产生更快的结果。
另请注意,此类大型查询可能需要一些时间来解析,因此,如果可能,请将查询的ID放入临时表中以帮助解决此问题
答案 4 :(得分:0)
您可以尝试在临时表和内部联接两个表中插入需要查询的ID。我不知道这是否有帮助。