2010-08-16 44 views
1

假设名为'log'的表,其中有大量记录。适用于SQL中日志记录表的查询和索引

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

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

logLevelcreationData有指标,但记录的数量使得它需要更长的时间来检索数据。

我们如何解决这个问题?

+0

什么是 '解释计划' 告诉你关于你的查询? – 2010-08-16 14:41:14

回答

5

看看你的执行计划/“EXPLAIN PLAN”结果 - 如果你正在检索大量数据,那么你可以做很少的事情来提高性能 - 你可以尝试改变你的SELECT声明,只包括你的列但是它不会改变你正在做的逻辑读取的次数,所以我怀疑它只会对性能产生不可忽略的影响。

如果您只是检索少量的记录,那么LogLevel的索引和CreationDate上的索引应该有效。

更新: SQL服务器主要用于查询海量数据库的小型子集(例如,将单个客户记录从数据库返回数百万个数据库)。它并没有真正为回归真正的大数据集做好准备。如果你正在返回的数据量是真的大,那么只有一定的数量,你将能够做,所以我不得不问:

什么是你实际上是试图达到

  • 如果要显示日志消息给用户,那么他们只打算有兴趣在时间的一小部分,所以你可能也想看看分页SQL数据的有效方法 - 如果你一次只能回复500条左右的记录,但它应该仍然很快。

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

+0

+1问这个问题背后的问题是什么”你究竟在努力实现什么“ – 2010-08-16 16:00:42

0

几件事情

你需要的所有列,人们通常SELECT *,因为他们都懒得列出15,该表有5列。

得到更多的内存,themore RAM你有更多的数据可以住在缓存是快1000倍从磁盘

阅读
+0

更多内存?你的意思是我应该写缓存算法吗? – 2010-08-16 14:45:49

+0

机器没有更多的RAM,因此随后的请求命中RAM而不是从磁盘获取数据 – SQLMenace 2010-08-16 14:49:25

+1

更多RAM通常不是开发人员可以影响的选择,更不用说了。 – AllenG 2010-08-16 14:51:06

4

1:不要使用Select *
2:确保你的指标是正确的,你的统计信息是最新的
3 :(可选)如果您发现您没有查看日志数据超过特定时间(以我的经验,如果发生超过一周以前,我可能不会需要它的日志)建立一个工作,将其归档到某些备份中,然后删除未使用的记录。这将减小表格大小,减少搜索表格所花费的时间。

+0

+1为索引建议。伟大的一点。 – Robb 2010-08-16 14:43:44

+0

我可以提供一个轻微的重新说明 - “确保你有正确的索引,并且你的*统计数据是最新的”,你的索引已经过时了! :-) – Justin 2010-08-16 14:46:48

+0

@AllenG:愚蠢的问题:它可能发生的索引是不是最新的?我一直认为它是“自动的”。 – 2010-08-16 14:47:37

1

您是否需要所有列?第一步应该只选择你实际需要检索的那些。

另一方面是数据到达您的应用程序(填充数据集/按顺序读取/?)后对数据执行的操作。

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

你应该回答自己这些问题:

你需要在一次保存所有在内存中返回的数据?您在检索方每行分配多少内存?你一次需要多少内存?你可以重用一些内存吗?

2

根据您使用的是哪种SQL数据库,您可能会查看Horizaontal Partitioning。通常情况下,这可以完全在数据库的一面完成,因此您不需要更改代码。

+0

'shard'可以很好! – 2010-08-17 06:53:28

0

对我来说,有两件事情可以做,

  1. 分区表水平基础上的日期列

  2. 使用预聚合的基本概念。

预聚合: 在preagg你将有一个 “记录” 表, “logs_temp” 表中, “logs_summary” 表和 “logs_archive” 表。日志和logs_temp表的结构是相同的。应用程序的流程就是这样,所有日志都记录在日志表中,然后每个小时执行一个cron作业,执行以下操作:

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

b。 c。从logs_temp表中汇总该特定小时的日志。将汇总结果保存在汇总表中

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

这种方式的结果是在汇总表中预先汇总的。

无论何时您希望选择结果,都可以从汇总表中选择它。

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

现在插入也会很快,因为日志表中的数据量并不多,因为它仅保留最后一个小时的数据,因此插入时的索引重新生成花费的时间与非常大数据集,从而使插入更快。

您可以阅读更多有关影子表招here

我采用的预聚合方法建立在WordPress的新闻网站。我不得不为新闻网站开发一个插件,它会显示最近流行的(最近3天流行的)新闻项目,并且每天有100K次点击,而这个预先聚集的事情对我们有很大的帮助。查询时间从2秒以上下降到不到1秒。我打算尽快将该插件公开发布。

0

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

日志级别和creationData有指标

你需要与这两个值一个指标,你把他们什么样的顺序会影响性能,但是假设你有一个小的数目可能记录等级值(和数据不会倾斜),您将获得更好的性能,首先将creationData。

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

C.

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列的范围对它进行分区。看到这些链接的详细信息: