具有多个共享列的MySql索引策略

时间:2017-01-24 17:00:36

标签: mysql sql indexing database-performance query-performance

我们有一个数据库表,用于存储访问者的浏览器数据,按多种不同的子类型进行细分。为简单起见,我们使用下面的表模式。查询基本上将在任何单个id列,度量列,时间戳列(存储为epoch之后的秒)以及设备,浏览器或os列之一。

我们将对星形与雪花模式进行性能测试(其中所有ID都进入单个列,但随后添加了一个额外的列id_type以确定该表的标识符类型) ,但只要星型模式(现在是这样)在雪花性能的80%以内,我们就会保留它,因为它会使我们的加载过程更容易。然而,在我这样做之前,我想确保在星型模式上优化索引。

create table browser_data (
id_1 int,
id_2 int,
id_3 int,
id_4 int,
metric varchar(20),
browser varchar(20),
device varchar(20),
os varchar(20),
timestamp bigint
)

仅在id列上创建单个索引会更好,还是在这些索引中也包含metrictimestamp列?

1 个答案:

答案 0 :(得分:1)

规范化"连续"值,例如DATETIMEFLOATINT执行将值保留在主表中。

当您将值移动到其他表格时,特别是"雪花",它会根据稍慢和很多之间的值进行查询。当您需要筛选不在主表中的多个指标时,尤其会发生这种情况。由于"雪花"它们中的任何一个都表现得很差。或者"过度规范化":

WHERE a.x = 123 AND b.y = 345

ORDER BY a.x, b.y

至于要创建的索引 - 这完全取决于您需要执行的查询。因此,我强烈建议您根据您的暂定SELECTs草拟可能的CREATE TABLEs

INT是4个字节。 TIMESTAMP是5,FLOAT是4,等等。也就是说,规范化这些事情在空间上效率也很低。

更多

在执行JOINs时,优化器几乎总是从一个表开始,然后转到另一个表,等等。(参见"嵌套循环连接"。)

例如(基于上面的'代码'),当2列被规范化,并且您正在测试值时,您手边没有两个ids,您只有这两个值。这使得查询执行效率非常低。对于

SELECT ...
    FROM main
    JOIN a  USING(a_id)
    JOIN b  USING(b_id)
    WHERE a.x = 123 AND b.y = 345

以下很可能是执行计划':

  1. 到达a找到x = 123的行;获取这些行的id(s)。这可能包括许多尚未由b.y过滤的行。 a需要INDEX(x)
  2. 返回main表,查找包含这些ID的行。 main需要INDEX(a_id)。同样,可以拖运更多不必要的行。
  3. 仅限现在,您是否b(使用b_id)检查y=345;扔掉你一直拖着的不必要的行。 b需要INDEX(b_id)
  4. 请注意我关于"围绕"的评论。盲目地使用*(在SELECT *中)会增加问题 - 所有列都在执行步骤时被拖走。

    另一方面......如果xy位于main表中,那么代码就像:

    WHERE main.x = 123
      AND main.y = 345
    

    只需要INDEX(x,y)(按任意顺序)。它可以快速找到所需的行。

    对于ORDER BY a.x, b.y,它不能在任何表上使用任何索引。因此查询必须创建一个tmp表,对其进行排序,然后按所需顺序传递行。

    但如果xy位于同一个表格中,那么INDEX(x,y)(按此顺序)可能ORDER BY x,y和避免使用tmp表和排序。

    使用单个表格时,优化程序可能会使用WHERE的索引,或者可能会使用ORDER BY的索引,具体取决于月亮的相位。在某些情况下,一个索引可以用于两者 - 这是最佳的。

    另一个注意事项:如果您还有LIMIT 10,...如果避免sort,那么只需要查看10行,而不是WHERE的整个集合