【问题标题】:index help for a MySQL query using greater-than operator and ORDER BY使用大于运算符和 ORDER BY 的 MySQL 查询的索引帮助
【发布时间】:2011-01-25 04:42:41
【问题描述】:

我有一个至少有几百万行的表和一个包含所有整数的架构,大致如下:

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

感谢所有回答的人,你们真的帮助我解决了这个问题。

【问题讨论】:

  • 在您的查询中发布 EXPLAIN 的输出。
  • 您可以尝试在查询上执行EXPLAIN 并将其粘贴到索引列表中吗?可能有助于了解 mysql 正在尝试做什么。
  • 等等,another_id 是从哪里来的?从来没有提到过,我很困惑为什么查询分析器会认为它是相关的,因为它甚至没有在 WHERE 中使用。
  • @Malonso,我简化了示例,实际表比我在这里显示的多几列。我根本不打算包含 another_id 索引,除非查询分析器决定使用它,不知道为什么。
  • 哪些列最有选择性?

标签: sql mysql indexing database


【解决方案1】:

你能让你的示例表和 EXPLAIN 结果匹配吗? 因为,显然情况不同,我们不知道您是否仅通过查看提供的 EXPLAIN 结果来抽象您的实际查询时是否犯了错误。 如果您不想显示太多结构,则将其反转并创建带引号的表结构并提供 EXPLAIN 结果(也许您会以这种方式发现问题)。

现在可以确定一件事——排序是使用filesort,这很糟糕。

为了简化(我们将回到它) - 对排序有用的复合索引需要在前面有排序字段。

示例 idx(ID, 开始)

ID      Start
1
        5
        8
        8
        10
        25
2
        3
        9
        10
        40
        41
        42
        42
...

在上面的示例中,如果您没有将 ID 限制为一个值的 where 条件,则索引对排序没有多大帮助。

但是,这个例外很重要,因为您在一个或两个 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) 将字段 stop 添加到索引的末尾 b) 使用 stop 而不是 start 创建两个更相似的索引(索引 intersect 可以在那里使用,更广泛的查询可以从中受益)

但一定要测试所有上述理论。

几个一般性建议

  • 首先以最有选择性的方式写下您的条件
  • 当测试索引首先从单列索引开始,然后扩展为复合索引时(例如,对于开始排序,我将只在开始时添加索引)
  • 太多的索引在 mysql 中不太好,因为查询计划器无法快速运行所有可能的组合并且无法正确估计所有操作的成本(因此它偷工减料,最佳索引组合和计划可能被排除在外)
  • 因此在您的选择中使用USE INDEX (index1) FOR ORDER BY 测试索引以衡量某个索引相对于刨床的好处,请参阅更多here(尤其是 FORCE 选项;另外 - 旨在只留下有用的索引,看看刨床是否能够使用它们,如果没有,仅作为最后的手段,在查询中强制使用对性能至关重要的索引。请记住,这在管理和设计方面是一种不好的做法)。

【讨论】:

    【解决方案2】:

    尽量避免使用范围(例如 >、>=、

    乍一看,我会说至少在 first_user_id、stop、second_user_id 上创建一个索引。然后相应地指定查询:

    select * from tbl_name where first_user_id=N and stop >= M and second_user_id=N

    更新:D'oh,所以我在上面的查询中完全自相矛盾 - 因为在停止“范围”之后的 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)

    【讨论】:

    • 为了给出准确的答案,我需要了解更多信息:表格、数据、每次拉取的可接受延迟、可用硬件、预期增长等。根据经验,您将使用具有 WHERE 语句的查询获得最佳性能,这些语句的字段都是索引(正确)。但是,鉴于您似乎总是需要按“停止”值范围进行过滤,我不确定您是否可以创建一个索引来帮助处理停止范围和 ORDER BY 开始。
    • 我可以完全改变查询的顺序,这应该不是问题。我仍然想知道 (first_user_id,stop,second_user_id) 上的索引是否适用于 ORDER BY,这似乎是真正减慢查询速度的部分。
    • @Jaymon,以我的电话簿为例,Last_Name+Street_Name 上的索引会有帮助吗?对于每个不同的 Last_Name,例如“Jones”,街道会被正确排序,但是您会得到一个 Last_Name 范围,因此无论如何它仍然需要根据 Street_Name 重新排序所有内容。
    • 首先,看看我对我的答案所做的更新 b/c 我在原文中搞砸了一些东西。如果结果集很大,那么对于停止“范围”,您实际上无能为力。出于好奇,请运行这些查询并发布结果:“select count(start) from tbl_name where first_user_id=N and stop >= M;”和“从 tbl_name 中选择 count(start) where first_user_id=N and second_user_id=N and stop >= M;”
    • "select count(start) from tbl_name where first_user_id=N and second_user_id=N and stop >= M;"有 30 行。 select count(start) from tbl_name where first_user_id=N and stop >= M;" 有 4096 行。tbl_name 总共有大约 400 万行。
    【解决方案3】:

    奇怪的是你的查询只返回 238 行 (?)

    既然您说没有ORDER BY 查询会更快,我可以建议您在查询后进行排序吗?
    这可能是解决问题的最快方法。

    另外,之后不要忘记删除未使用的索引 :)


    编辑

    这是一个疯狂的猜测(因为我不确定 mysql 不会将查询分解为当前形式),但您可以尝试执行以下操作:

    SELECT * FROM (
        SELECT * 
          FROM tbl_name 
         WHERE stop >= M 
           AND first_user_id=N 
        ) AS derived
    ORDER BY start ASC
    

    【讨论】:

    • 我考虑过事后对它们进行排序,但是虽然我使用的一组值只有 238 行,但我们不能保证会有这么少,而且将来会最多肯定会更多。这就是为什么我现在想弄清楚。是的,所有这些索引都在我的开发数据库上。当我找到一组有效的索引时,这些将是唯一添加到实时服务器的索引。
    • 但是这些索引的存在不会给优化器带来问题吗?或者这只是我脑海中关于 Oracle 的一些东西,与 MySQL 无关?
    猜你喜欢
    • 2023-03-12
    • 1970-01-01
    • 2017-12-16
    • 1970-01-01
    • 1970-01-01
    • 2019-11-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多