用于用户定义查询的MySQL临时索引

时间:2013-11-26 18:28:43

标签: mysql sql database indexing

我正在构建一个分析平台,用户可以在其中创建针对MySQL数据库的报告。这个数据库中的一些表非常庞大(数十亿行),因此对于所有功能到目前为止,我都建立了索引来加速每个查询。

但是,下一个功能是为用户添加定义自己查询的功能,以便他们能够以我们尚未预定义的方式分析数据。他们对相关数据库具有完全读取权限,因此基本上任何SELECT查询都是他们输入的有效查询。但是,如果定义了一个对我们当前没有编入索引的列进行过滤或连接的查询,则会产生问题 - 有时甚至需要花费一分钟来执行简单的查询 - 这些基本内容如下:

    SELECT tbl1.a, tbl2.b, SUM(tbl3.c)
    FROM
        tbl1
        JOIN tbl2 ON tbl1.id = tbl2.id
        JOIN tbl3 ON tbl1.id = tbl3.id
    WHERE
        tbl1.d > 0
    GROUP BY
        tbl1.a, tbl1.b, tbl3.c, tbl1.d

现在,假设我们只在目前为止未出现在此查询中的列上创建了索引。此外,我们不希望太多的索引减慢插入,更新和删除(否则简单的解决方案是在用户可访问的每个列上构建索引)。

我的问题是,处理这个问题的最佳方法是什么?目前,我认为我们应该扫描查询,为尚未编入索引的WHEREJOIN中出现的任何内容构建索引,执行查询,然后删除已构建的索引然后。但是,我不确定的主要内容是a)对于我不知道的这种用例,是否已经有一些最佳实践?和b)构建这些索引的开销是否足以抵消索引提供的任何性能提升?

如果此策略不起作用,我可以看到的下一个选项是收集用户运行的查询类型的统计信息,并让一些常规作业定期检查哪些常用列缺少索引并创建它们。 / p>

2 个答案:

答案 0 :(得分:1)

如果使用MyISAM,那么对具有大量(数十亿行)的表执行ALTER语句以添加索引将花费相当多的时间,可能远远超过您所说的1分钟对于上面的陈述(之后你需要另一个ALTER来删除索引)。在此期间,表将被锁定,这意味着其他用户无法执行自己的查询。

如果你的表使用InnoDB引擎而你正在运行MySQL 5.1+,那么CREATE / DROP索引语句不应该锁定表,但它仍然可能需要一些时间来执行。

ALTER TABLE [这里] [1]的历史有一个很好的概述。

我还建议用于识别和构建indeces的自动查询分析很难做到正确。例如,foo.a选择但foo.b排序等情况呢?这种查询通常需要覆盖多个列的索引,否则您可能会发现您的服务器在巨大的结果集上尝试文件输出,这可能会导致很大的问题。

为用户提供“解释查询”选项将是一个很好的第一步。如果他们知道足够的SQL来执行自定义查询,那么他们应该能够分析EXPLAIN以便最好地执行他们的查询(或者至少意识到给定的查询需要很长时间)。

答案 1 :(得分:1)

因此,进一步了解我的想法,我建议您将数据划分为明确的视图。您使用了抽象名称,因此我无法重复使用您的业务模型,但我将采用虚拟示例。

假设你有3张桌子:

  • 客户(性别,社会类别,出生日期......)
  • 发票(日期,金额,......)
  • 产品(价格,创建日期......)

您将为特定细分创建一些物化视图。这就像在最底部的数据表示层之上添加业务层一样。

例如,我们可以识别以下细分:

  • 老人至少有2张发票
  • 2013年发票超过1件产品

怎么做?如何有效地?定期查看对您的问题没有帮助,因为他们对随机查询的解释计划很差。我们需要的是这些细分的真实物理表现。我们可以这样做:

CREATE TABLE MV_SENIORS_WITH_2_INVOICES AS 
SELECT ... /* select from the existing tables */
;

/* add indexes: */
ALTER TABLE MV_SENIORS_WITH_2_INVOICES ADD CONSTRAINT...
... etc.

所以现在,你的家伙只需要查询MV_SENIORS_WITH_2_INVOICES而不是原始表。由于记录较少,索引可能较多,因此表现会更好。

我们完成了!哦等等,没有:-) 我们需要刷新这些数据,有点像Oracle中的FAST REFRESH。 MySql没有(不是我知道......有人纠正我吗?)一个类似的系统,所以我们必须为此创建一些触发器。

CREATE TRIGGER ... AFTER INSERT ON `seniors`
... /* insert the datas in MV_SENIORS_WITH_2_INVOICES if it matches the segment */
END;

现在我们已经完成了!