代码之家  ›  专栏  ›  技术社区  ›  naivists

加速联接查询需要复合索引吗?

  •  6
  • naivists  · 技术社区  · 14 年前

    一位同事问我如何解释索引(indexs?)提高表现;我试着这样做,但我自己也弄糊涂了。
    我使用下面的模型进行解释(错误/诊断日志记录数据库)。它由三个表组成:

    • 业务系统列表,包含其名称的“系统”表
    • 不同类型的跟踪列表,表“tracetypes”,定义可以记录哪些类型的错误消息
    • 实际跟踪消息,具有来自的外键 System TraceTypes 桌子

    我在演示中使用了MySQL,但是我不记得我使用的表类型。我想是InnoDB。

     System                                TraceTypes
    -----------------------------         ------------------------------------------
    | ID          | Name        |         | ID    | Code   | Description           |
    -----------------------------         ------------------------------------------
    | 1           | billing     |         | 1     | Info   | Informational mesage  |
    | 2           | hr          |         | 2     | Warning| Warning only          |
    -----------------------------         | 3     | Error  | Failure               |
               |                          ------------------------------------------
               |                ------------|
     Traces    |                |            
     --------------------------------------------------
     | ID | System_ID | TraceTypes_ID | Message       |
     --------------------------------------------------
     | 1  |  1        |  1            | Job starting  |
     | 2  |  1        |  3            | System.nullr..|
     --------------------------------------------------
    

    首先,我向所有表添加了一些记录,并演示了下面的查询在0.005秒内执行:

    select count(*) from Traces 
      inner join System on Traces.System_ID = System.ID
      inner join TraceTypes on Traces.TraceTypes_ID = TraceTypes.ID
    where 
      System.Name='billing' and TraceTypes.Code = 'Info'
    

    然后我生成了更多的数据(还没有索引)

    • “系统”包含大约100个条目
    • “tracetypes”包含大约50个条目
    • “痕迹”包含约1000万条记录。

    现在,上一个查询耗时8-10秒。

    我在上创建了索引 Traces.System_ID 列和 Traces.TraceTypes_ID 列。现在,此查询以毫秒为单位执行:

    select count(*) from Traces where System_id=1 and TraceTypes_ID=1;
    

    这也很快:

    select count(*) from Traces 
      inner join System on Traces.System_ID = System.ID
    where System.Name='billing' and TraceTypes_ID=1;
    

    但是前一个连接所有三个表的查询仍然需要8-10秒才能完成。

    只有当我创建了一个复合索引(索引中包括System_ID和TraceTypes_ID列)时,速度降到毫秒。

    我之前教过的基本语句是“所有用于连接的列都必须编入索引”。
    但是,在我的场景中,我在这两个方面都有索引 System_ID TraceTypes_ID 但是MySQL没有使用它们。问题是-为什么?我的赌注是-项目计数比率100:10000000:50使单列索引太大,无法使用。但这是真的吗?

    3 回复  |  直到 14 年前
        1
  •  2
  •   bryantsai    14 年前

    首先,分析慢SQL语句的正确且最简单的方法是进行解释。找出优化器是如何选择计划的,并思考为什么以及如何改进计划。我建议只使用两个单独的索引来研究解释结果,以了解MySQL如何执行您的语句。

    我对MySQL不是很熟悉,但是MySQL4似乎有一个限制,即查询中每个表只能使用一个索引。自MySQL5以来,这方面似乎有所改进。( index merge 但我不确定这是否适用于你的案件。再说一遍,解释应该告诉你真相。

    即使每个表允许使用2个索引(MySQL5),使用2个单独的索引通常比复合索引慢。与使用复合索引的单一过程相比,使用两个单独的索引需要索引合并步骤。

    Multi Column indexes vs Index Merge 可能会有所帮助,使用MySQL5.4.2。

        2
  •  1
  •   Mitch Wheat    14 年前

    决定优化器是否使用索引的不是索引的大小,而是选择性。

        3
  •  0
  •   Nitin Midha    14 年前

    我的猜测是,它将使用索引,然后它可能使用传统的查找移动到另一个索引,然后过滤掉。请检查执行计划。所以简而言之,您可能在嵌套循环中循环访问两个索引。根据我的理解。我们应该尝试对筛选或联接中的列创建一个复合索引,然后对选择中的列使用include子句。我从未在MySQL中工作过,所以我的理解基于SQL Server 2005。