Oracle - 每日分区方法适用于 table 数百万行

Oracle - is daily partitioning approach good for table with millions of rows

我正在处理一个现有的 table,它在零售交易历史方面拥有数百万行数据。架构如下所示:

create table History(
hid number(19,0),
type varchar2(255 char),
lastupdated timestamp (6) not null enable,
name varchar2(255 char),
primary key (hid))
partition by range (lastupdated) interval (numtodsinterval(1,'day'))
(partition retailhistory values less than (to_timestamp('12/01/2020','DD/MM/YYYY')));

上面table是按日间隔分区的,所以一年有365个分区,百万行。我们正计划创建一个清除作业,但在那之前,在 table 中有这么多分区是否可以?与没有分区的 table 相比,它会导致性能下降吗?

如有任何帮助,我们将不胜感激。谢谢。 :)

根据您对 "millions" 的定义,每日分区可能过于精细,可能会在读取数据时导致性能问题。

每个分区在物理上都像 table 一样存储并且有不同的开销 - 最重要的是段 space 分配的开销。 Oracle 几乎从不分配 exactly 需要多少 space,总会多出一点。如果您创建大量的小分区,"little extra" 可能会比实际数据大。

在我下面的测试案例中,假设每年有 500 万行,并且每列中的值相对较小,每日分区使用的段 space 是每月分区的十倍。这意味着每日分区对于选择一天的数据是最佳的,但对于选择多天的数据将是糟糕的。而且由于完整 table/partition 扫描使用多块读取,并且一次读取数兆字节的数据,无论如何读取整整一个月的数据可能不会比读取一天的数据慢多少。

示例架构

创建三个 table 用于每日、每月和不分区。将 500 万行平均加载到 365 天的每一天,然后检查段大小。

  • 每日分区使用 2,920 兆字节用于 365 个段。
  • 每月分区使用 288 兆字节用于 13 个段。
  • 没有分区使用 280 兆字节用于 1 个段。

代码:

----------------------------------------
--DAY
----------------------------------------

create table History_day(
hid number(19,0),
type varchar2(255 char),
lastupdated timestamp (6) not null enable,
name varchar2(255 char),
primary key (hid))
partition by range (lastupdated) interval (numtodsinterval(1,'day'))
(partition retailhistory values less than (to_timestamp('12/01/2020','DD/MM/YYYY')));

create sequence history_day_seq;

begin
    for i in 1 .. 365 loop
        for j in 1 .. 13698 loop
            insert into history_day values(history_day_seq.nextval, 'some type value', date '2020-12-01' + i, 'some name value');
        end loop;
    end loop;
    commit;
end;
/

select sum(bytes)/1024/1024 mb, count(*) partition_count from dba_segments where segment_name = 'HISTORY_DAY';


----------------------------------------
--MONTH: 288 megabytes for 13 partitions.
----------------------------------------

create table History_month(
hid number(19,0),
type varchar2(255 char),
lastupdated timestamp (6) not null enable,
name varchar2(255 char),
primary key (hid))
partition by range (lastupdated) interval (numtoyminterval(1,'month'))
(partition retailhistory values less than (to_timestamp('12/01/2020','DD/MM/YYYY')));

create sequence history_month_seq;

begin
    for i in 1 .. 365 loop
        for j in 1 .. 13698 loop
            insert into history_month values(history_month_seq.nextval, 'some type value', date '2020-12-01' + i, 'some name value');
        end loop;
    end loop;
    commit;
end;
/

select sum(bytes)/1024/1024 mb, count(*) partition_count from dba_segments where segment_name = 'HISTORY_MONTH';


----------------------------------------
--NO PARTITIONS
----------------------------------------

create table History(
hid number(19,0),
type varchar2(255 char),
lastupdated timestamp (6) not null enable,
name varchar2(255 char),
primary key (hid));

create sequence history_seq;

begin
    for i in 1 .. 365 loop
        for j in 1 .. 13698 loop
            insert into history values(history_seq.nextval, 'some type value', date '2020-12-01' + i, 'some name value');
        end loop;
    end loop;
    commit;
end;
/

select sum(bytes)/1024/1024 mb, count(*) partition_count from dba_segments where segment_name = 'HISTORY';

这些结果是否适用于您?

根据您的数据和系统分配段的方式,您的系统上的结果很可能会有所不同。重要的是你应该自己进行这样的测试。请记住,分区是为 "large" 数据量设计的 - 但 "large" 这个词显然是主观的。

每日分区对于许多大型 table 来说是完美的,但我感觉您的 table 使用月份分区会更好。