统计/聚合查询:完全扫描或索引或分区?

时间:2014-04-08 11:25:03

标签: sql database oracle indexing

假设我们有类似(Oracle语法但不重要)的数据:

create table EVENT (
    UUID raw(16) default sys_guid(),  -- no significant for question
    TYPE number(2,0) not null,
    DATEX date not null,
    AMOUNT number(18,2) -- we use op: SUM, COUNT, AVG, STDDEV_POP, MEDIAN, etc
);

distinct TYPE仅限于人类可管理的计数(比如20),DATEX是过去10年,AMOUNT是统计分析的字段(比如给定{{获取直方图)在选定的EVENT期间内按AMOUNT个月{}}}。

数字或行约为2e + 6.

由于所有查询都使用DATEXTYPE = n限制,我决定为此字段制作索引:

DATEX between DATE 'yyyy-mm-dd' and DATE 'yyyy-mm-dd'

全扫描查询的性能优于大约x2-x5次。

另一种策略是事件create index INDEX_EVENT_MAIN on EVENT (TYPE ASC, DATEX ASC); 在不同的表中分割数据,如EVENT1,EVENT2,......我使用这些表而没有索引。在这种情况下,查询EVENTn表中的性能x2-x10比TYPE的{​​{1}}表(两次全扫描)更好。

我还在EVENT表上进行分区:

EVENT

并且TYPE = n上的查询效果与单独的alter table EVENT add partition event_default values (DEFAULT); alter table DATA_XX split partition event_default values(2) into ( partition event2, partition event_default); 表格相同。

我不是DBA的专家,而是那个制作Web 2.0企业网站的人。所以我可以进行实验和猜测,但不能理解黑盒子,也无法解释强关系/算法理论的结果。

所以有相关的问题:

  • 索引不适用于统计查询(处理各种行)和完全扫描更好吗?
  • 索引仅用于点(非范围,通过ID获取)查询(非宽范围)?
  • 表分割或分区是提升统计/聚合查询查询性能的唯一方法吗?

2 个答案:

答案 0 :(得分:3)

我会回答你的问题,但首先需要一些背景来理解答案:

执行完整扫描所需的时间很大程度上取决于数据所在的硬盘的吞吐量。如果您的磁盘可以提供200 mb / s的速度,则需要大约1秒的时间来执行具有200 mb数据的表的全表扫描,无论行数是否为n。

映像一个200 mb的表,没有任何索引,但列ID在数据中是唯一的。在这种情况下,以下两个查询都将花费相同的时间,因为大部分时间都花在等待硬盘驱动器将数据传递给Oracle进程上。

第一个查询将花费很长时间,因为Oracle必须通过所有数据才能找到满足id = 1的行。第二个查询将花费很长时间,因为Oracle必须通过所有数据来汇总one_columnanother_column的所有值。

select id, one_column, another_column
  from two_hundred_mb_table
 where id = 1

select sum(one_column) / sum(another_column) 
  from two_hundred_mb_table

如果要向列ID添加索引,则所有内容都会更改。第一个查询现在只需要访问ID = 1的索引,然后选择" rowid"这是数据文件中行的物理地址,请求"块"在磁盘上,然后选择行。现在,第一个查询的速度要快得多,因为不需要涉及的所有数据

这里的关键点是,即使您已为列编制索引,您仍然无法直接从磁盘中选择行。您仍然需要从磁盘中获取整个块(通常约为8kb)。平均行长度为100字节,表示该块保持82行。所以你读了82行才能找到你的一行。

这就是为什么你通常不能通过索引读取很多行,然后它变得比表扫描慢。原因是你可能会一遍又一遍地重读相同的块。当然,通过全表扫描读取数据变得比通过索引更快时,存在一个断点(在每种情况下都不同)。

现在,问你的问题:

<强> 1。索引不适用于统计查询(处理各种行)和完全扫描更好吗? 对此的答案在于上述文本。它与sum / count或索引无关,它与表中的数据量有关,并且如果有一个有效的访问路径进入感兴趣的子集。

<强> 2。索引仅用于点(非范围,通过ID获取)查询(非宽范围)? 同样在这里,答案在于上面的文字。您可以对索引使用范围查询,但如果感兴趣的子集很大,则使用全表扫描会更好。

第3。表拆分或分区只是提高统计/聚合查询的查询性能的方法吗?

如果表是2,000 MB,并且您的磁盘可以返回200 mb / s,则执行全表扫描需要10秒。假设type上的统一数据分布并且您有10个不同的值,则可以按type对表进行列表分区。在这种情况下,每个分区都是200 MB,因此type=n上的任何查询都需要1秒而不是10秒。但是,没有type=n的所有查询仍需要10秒钟。

您还可以在datex列上创建范围分区,例如,每月制作一个分区。再假设表格为2000 mb且数据分布均匀,您最终会得到每个分区中1/12的数据。

你也可以组合这些,并按LIST(事件)和RANGE(datex)进行分区。

如果仍然无法满足性能要求,可以考虑创建聚合表(或物化视图)。例如,如果您发现自己在较大的时间跨度上进行了大量分析,则可以按月聚合数据,并对聚合数据执行更高级别的查询。一旦找到了需要的月份,就可以深入了解#34;进入你可以使用事件表再次使用几乎击中一个分区的谓词。

答案 1 :(得分:1)

全表扫描更适合读取“大量”数据,索引更适合读取“少量”数据。找到合适的大小主要取决于索引聚类因子和单块IO与多块IO。

索引聚类因子

正如Ronnis所说,在I / O上花费的时间取决于从磁盘读取的块数。但是一次从同一个块读取多行通常非常便宜 - 一旦块在内存中,扫描行就很快。

真正的问题是,根据数据在磁盘上的排序方式,读取一小部分行可能需要读取大部分数据。由于表数据的创建方式,某些索引效率低下。

index clustering factor衡量数据的排序方式。这个号码是 估计“通过索引读取整个表所需的I / O数”。该号码可在DBA_INDEXES.CLUSTERING_FACTOR

中找到

您通常可以通过使用按特定方式排序的行重建表来优化索引。但这只适用于一个指数。

单块与多块I / O

Oracle可以一次读取一个块,也可以一次读取多个块。

为了从单行读取单个值,显然最快的方法是尽可能少地读取数据。索引范围扫描等访问方法总是使用 单块I / O,全表扫描和快速全索引扫描等访问方法总是使用多块I / O.

对于大量数据,以大块读取数据比一次读取数据更有效。我不能给你一个很好的解释磁盘磁头如何寻找,从扇区读取数据等。细节是不重要的,这里有关于数据库性能的一般教训 - 总是分批处理。

您甚至可以找出系统上的单块与多块时间。如果您收集了系统统计信息,并且它们是准确的,则可以使用  这两个查询用于计算一次一个地读取块的时间与批处理:

--Blocks read per multi-block read, if you set the value yourself.
select value from v$parameter where name = 'db_file_multiblock_read_count';

--Time to read single and multiple blocks, in milliseconds.
--And average blocks per multi-block read.
select * from sys.aux_stats$ where pname in ('SREADTIM', 'MREADTIM', 'MBRC');
相关问题