我有一个至少有几百万行的表和一个看起来大致如此的所有整数的模式:
start
stop
first_user_id
second_user_id
使用以下查询拉取行:
SELECT *
FROM tbl_name
WHERE stop >= M
AND first_user_id=N
AND second_user_id=N
ORDER BY start ASC
SELECT *
FROM tbl_name
WHERE stop >= M
AND first_user_id=N
ORDER BY start ASC
我无法找出加速这些查询的最佳索引。问题似乎是ORDER BY,因为当我把它拿出来时,查询很快。
我使用标准索引格式尝试了所有不同类型的索引:
ALTER TABLE tbl_name ADD INDEX index_name (index_col_1,index_col_2,...)
他们似乎都没有加快查询速度。有谁知道什么索引可以工作?另外,我应该尝试不同类型的索引吗?我不能保证每一行的唯一性,所以我避免使用UNIQUE索引。
任何指导/帮助将不胜感激。谢谢!
更新:这里有一个索引列表,我最初没有包括这个,因为我采用了霰弹枪的方法,并添加了大量的索引,寻找一个有效的索引:
start_index: [start, first_user_id, second_user_id]
stop_index: [stop, first_user_id, second_user_id]
F1_index: [first_user_id]
F2_index: [second_user_id]
F3_index: [another_id]
test_1_index: [first_user_id,stop,start]
test_2_index: [first_user_id,start,stop]
test_3_index: [start,stop,first_user_id,second_user_id]
test_4_index: [stop,first_user_id,second_user_id,start]
test_5_index: [stop,start]
这是EXPLAIN输出。
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: listing
type: index_merge
possible_keys: stop_index,F1_index,F3_index,test_1_index,test_2_index,test_4_index,test_5_index
key: F1_index,F3_index
key_len: 5,5
ref: NULL
rows: 238
Extra: Using intersect(F1_index,F3_index); Using where; Using filesort
后人更新
我们最终完全重新评估了我们如何查询表并选择了这些索引:
index_select_1: [first_user_id,start,stop]
index_select_2: [first_user_id,second_user_id,start,stop]
然后我们在桌面上选择这样的查询:
SELECT *
FROM tbl_name
WHERE first_user_id=N
AND start >= M
ORDER BY start ASC
SELECT *
FROM tbl_name
WHERE first_user_id=N
AND second_user_id=N
AND start >= M
ORDER BY start ASC
感谢所有回答的人,你真的帮我解决了这个问题。
答案 0 :(得分:1)
你能让你的样本表和EXPLAIN结果匹配吗? 因为,显然它不是同一种情况,我们不知道你是否只是通过查看提供的EXPLAIN结果来抽象你的真实查询时犯了错误。 如果你不想显示太多的结构然后反转它并创建引用的表结构并提供EXPLAIN结果(也许你会以这种方式解决问题)。
现在有一件事是肯定的 - 排序正在使用filesort,这很糟糕。
为了简化(我们将回到它) - 用于排序的复合索引需要在前面放置排序字段。
实施例 idx(ID,开始)
ID Start
1
5
8
8
10
25
2
3
9
10
40
41
42
42
...
在上面的示例中,如果您没有ID仅限于一个值的条件,则索引对排序没有多大帮助。
但是,这个例外非常重要,因为您在一个或两个id字段上都有单行选择。
因此,从索引中开始的唯一索引是
start_index: [start, first_user_id, second_user_id]
test_3_index: [start,stop,first_user_id,second_user_id]
Mysql忽略索引
start_index: [start, first_user_id, second_user_id]
因为它在选择性方面有更好的选择 - 它需要使用此索引进行索引扫描,并且它具有允许索引交叉直接跳转到(未排序)结果的索引。它期望交叉的选择性更好,并且选择性驱动刨床。
一旦获得结果,mysql应该意识到它可以使用另一个索引来对结果进行排序,但似乎它看不出它会有多便宜。
因此,为了帮助刨床,您可以创建一个索引,利用索引来利用您的单值选择性,例如:
two_ids_with_sort: [first_user_id, second_user_id, start]
我认为上面的第二个查询可以很好地工作,你可以在两个id上使用条件来访问预先排序的开始记录指针。以下查询应对第一个查询执行相同的操作:
one_id_with_sort: [first_user_id, start]
只有在结果集中有大量记录时,我才会考虑进一步索引。
那里有两条路 a)将字段停止添加到索引的末尾 b)使用stop而不是start创建两个更相似的索引(索引相交可以在那里使用,更广泛的查询可以从中受益)
但要测试所有上述理论。
一般性建议
USE INDEX (index1) FOR ORDER BY
测试索引,以评估特定索引对平面的好处,请参阅更多here(特别是FORCE选项;另请 - 只留下有用的索引并查看是否planer将能够利用它们,如果不是,只作为最后的手段,强制查询中的索引哪些性能至关重要。请记住,这在管理和设计方面是不好的做法。)答案 1 :(得分:0)
尽量避免使用范围(例如>,> =,<,< =)作为WHERE子句的最左边部分。 MySQL无法对范围右侧的WHERE子句中的任何字段使用索引。
乍一看,我会说至少在first_user_id,stop,second_user_id上创建一个索引。然后相应地指定查询:
从tbl_name中选择*,其中first_user_id = N并且停止> = M和second_user_id = N
更新:D'哦,所以我在上面的查询中完全自相矛盾 - 因为在停止“范围”之后在WHERE中指定它时,将second_user_id合并到索引中是没用的,所以让我们再试一次。
ALTER TABLE tbl_name ADD INDEX index_1(first_user_id,stop) ALTER TABLE tbl_name ADD INDEX index_2(first_user_id,second_user_id,stop)
答案 2 :(得分:0)
奇怪的是,您的查询只返回238行(?)
由于您声明查询在没有ORDER BY
的情况下更快,我建议您在查询后进行排序吗?
这可能是解决问题的最快方法。
此外,不要忘记之后删除未使用的索引:)
修改的
这是一个疯狂的猜测(因为我不确定mysql不会将查询分解为当前形式),但您可以尝试执行以下操作:
SELECT * FROM (
SELECT *
FROM tbl_name
WHERE stop >= M
AND first_user_id=N
) AS derived
ORDER BY start ASC