SQL中日志记录表的适当查询和索引

时间:2010-08-16 14:39:53

标签: sql mysql sql-server oracle database-agnostic

假设一个名为'log'的表,其中有大量记录。

应用程序通常通过简单的SQL检索数据:

SELECT * 
FROM log 
WHERE logLevel=2 AND (creationData BETWEEN ? AND ?)

logLevelcreationData具有索引,但记录数量使得检索数据需要更长时间。

我们如何解决这个问题?

8 个答案:

答案 0 :(得分:5)

查看执行计划/“EXPLAIN PLAN”结果 - 如果您要检索大量数据,那么您可以做很少的事情来提高性能 - 您可以尝试将SELECT语句更改为仅包括您感兴趣的列,但它不会改变您正在执行的逻辑读取次数,因此我怀疑它只会对性能产生可忽略的影响。

如果您只检索少量记录,那么LogLevel的索引和CreationDate上的索引应该可以解决问题。

更新: SQL服务器主要用于查询大型数据库的小型子集(例如,从数百万的数据库中返回单个客户记录)。它并没有真正适应返回真正大型的数据集。如果您要返回的数据量真正大,那么您将只能执行一定数量的操作,因此我不得不问:

你实际上试图实现是什么?

  • 如果您正在向用户显示日志消息,那么他们一次只会对一个小的子集感兴趣,因此您可能还需要研究分配SQL数据的有效方法 - 如果您只回来说,一次说500左右的记录,它应该仍然非常快。

  • 如果您尝试进行某种统计分析,那么您可能希望将数据复制到更适合统计分析的数据存储中。 (不知道那是什么,这不是我的专业领域)

答案 1 :(得分:4)

1:永远不要使用Select *
2:确保您的索引正确无误,并且您的统计信息是最新的 3 :(可选)如果你发现你没有在一定时间内查看日志数据(根据我的经验,如果它发生在一周以前,我可能根本不需要它的日志)设置一个将作业存档到某些备份,然后删除未使用的记录。这将使表格大小减少,从而减少搜索表格所需的时间。

答案 2 :(得分:2)

根据您正在使用的SQL数据库类型,您可能会查看Horizaontal Partitioning。通常,这可以完全在数据库方面完成,因此您不需要更改代码。

答案 3 :(得分:1)

你需要所有专栏吗?第一步应该是只选择你真正需要检索的那些。

另一个方面是您在数据到达应用程序后对数据进行处理(填充数据集/按顺序读取/?)。

处理应用程序方面可能有一些改进的可能性。

你应该回答这些问题:

您是否需要立即将所有返回的数据保存在内存中?你在检索端每行分配多少内存?你一次需要多少内存?你能重用一些记忆吗?

答案 4 :(得分:0)

一些事情

您是否需要所有列,人们通常会SELECT *,因为他们懒得列出表格中的15列中的5列。

获得更多内存,更多内存,你拥有更多数据可以存储在缓存中,这比从磁盘读取快1000倍

答案 5 :(得分:0)

对我来说,你可以做两件事,

  1. 根据日期列

  2. 水平分区表格
  3. 使用预聚合的概念。

  4. <强>预聚合 在preagg中,您将拥有“logs”表,“logs_temp”表,“logs_summary”表和“logs_archive”表。 logs和logs_temp表的结构完全相同。应用程序流将以这种方式,所有日志都记录在日志表中,然后每小时运行一个cron作业,执行以下操作:

    一个。将日志表中的数据复制到“logs_temp”表并清空日志表。这可以使用暗影表技巧来完成。

    湾从logs_temp表

    聚合该特定小时的日志

    ℃。将汇总结果保存在汇总表

    d。将记录从logs_temp表复制到logs_archive表,然后清空logs_temp表。

    这种结果在汇总表中预先汇总。

    每当您想要选择结果时,您都可以从摘要表中选择它。

    这种选择非常快,因为记录的数量远远少于每小时预先聚合的数据。您甚至可以将阈值从一小时增加到一天。这一切都取决于您的需求。

    现在插入速度也很快,因为日志表中的数据量不多,因为它仅保留最后一小时的数据,因此与非常大的数据相比,插入时的索引重新生成所需的时间非常短-set因此使插入快速。

    您可以阅读有关Shadow Table技巧here

    的更多信息

    我在基于wordpress的新闻网站上使用了预聚合方法。我不得不为新闻网站开发一个插件,该插件会显示最近流行的(过去3天流行的)新闻项目,每天有100K次点击,这个预聚合的东西确实帮了我们很多。查询时间从2秒以上下降到不到2秒。我打算尽快公布插件。

答案 6 :(得分:0)

根据其他答案,除非你真的需要所有字段,否则不要使用'select *'。

  

logLevel和creationData具有索引

你需要一个包含这两个值的索引,你输入它们的顺序会影响性能,但假设你有少量可能的loglevel值(并且数据没有偏差)你将获得更好的性能将creationData放在首位

请注意,最佳索引会降低查询记录的成本(N),即随着记录数量的增加,它仍然会变慢。

下进行。

答案 7 :(得分:0)

我真的希望creationData代表creationDate

首先,在logLevelcreationData上拥有索引是不够的。如果您有2个单独的索引,Oracle将只能使用1。 您需要的是两个字段上的单个索引:

CREATE INDEX i_log_1 ON log (creationData, logLevel);

请注意,我首先放置了creationData。这样,如果只将该字段放在WHERE子句中,它仍然可以使用索引。 (过滤日期似乎更有可能仅在日志级别上)。

然后,确保该表填充了数据(与您在生产中使用的数据一样多)并刷新表中的统计信息。

如果表很大(至少有几十万行),请使用以下代码刷新统计信息:

DECLARE
  l_ownname          VARCHAR2(255) := 'owner'; -- Owner (schema) of table to analyze
  l_tabname          VARCHAR2(255) := 'log'; -- Table to analyze
  l_estimate_percent NUMBER(3) := 5;  -- Percentage of rows to estimate (NULL means compute)
BEGIN
  dbms_stats.gather_table_stats (
     ownname => l_ownname ,
      tabname => l_tabname,
      estimate_percent => l_estimate_percent,
      method_opt => 'FOR ALL INDEXED COLUMNS',
      cascade => TRUE
  );
END;

否则,如果表格很小,请使用

ANALYZE TABLE log COMPUTE STATISTICS FOR ALL INDEXED COLUMNS;

此外,如果表变大,您应该考虑在creationDate列上按范围对其进行分区。有关详细信息,请参阅以下链接: