估计数据库大小

Gon*_*o.- 4 oracle database-size

我必须做一些项目并且需要估计数据库大小。我将在项目中使用 Oracle,因为我们将有大量事务和数据。但是在“演示”中,我需要指定 5 年后我希望数据库增长多少。

所以,我有一个 PDF 计算行大小和其他东西的方式,但对于 SQL-Server。我想为 Oracle 做这件事。

pdf 是一个估计公式 - 没有真正的数据库,所以我不能查询数据库来检查实际大小(这是我在其他帖子中看到的,当我用谷歌搜索时)。

SQL-Server 行的公式是

Row_Size = Fixed_Data_Size +Variable_Data_Size +Null_Bitmap + 4

这个公式也适用于 Oracle 吗?另外,“4”是一行标题的大小(不知道英文怎么翻译)

因此,我必须在 Oracle 中执行此操作。Oracle 中的值 4 是多少?我认为我认为我已经解决了其他事情。

Jus*_*ave 14

理想情况下,您将创建数据库、加载一些示例数据、测量大小并进行推断。也就是说,到目前为止,估计 5 年内数据库大小的更准确方法。

如果您确实想计算数据库大小,通常首先要确定单个块中可以容纳多少行。为简单起见,我们假设行永远不会被删除,并且更新永远不会改变行的大小。我们还将假设没有使用压缩。否则,事情会变得有点(更)复杂。

计算行中数据的大小。对于固定大小的数据类型(即DATE, CHAR),这只是类型的大小。对于可变大小的数据类型(即NUMBER, VARCHAR2),这是列中数据的平均大小。有几个字节的额外开销,但您可以非常安全地忽略它——在估计实际数据的大小和随后估计每个块的行数时,它们将被错误淹没。

如果您期望每行将有 x 字节的数据,则每个块的行数将为

<<rows per block>> = 
  floor( <<database block size>> * 
          (1 - <<pctfree of table>>/100) / 
          <<size of row>> ) + 1
Run Code Online (Sandbox Code Playgroud)

假设一行小于<<database block size>> * <<pctfree of table>>/100. 如果该行较大,则不要将floor.

一旦知道每个块的行数,表的估计大小将是

<<size of table>> = 
  ceil( <<number of rows in table>> / <<rows per block>> ) *
    <<database block size>>
Run Code Online (Sandbox Code Playgroud)

浏览一个例子

我的数据库块大小是 8k,我们假设我使用的是默认值PCTFREE10(这意味着 10% 的块保留用于将来增加行大小的更新)。我将创建一个简单的两列表

SQL> create table foo(
  2    foo_id number,
  3    foo_str varchar2(100)
  4  );

Table created.
Run Code Online (Sandbox Code Playgroud)

如果foo_id将成为值为 1 到 100 万的主键,则每个foo_id将消耗 1 到 7 个字节的空间。但我也从测试中了解到,平均而言,它需要大约 6 个字节(实际上是 5.89 个字节)。当然,foo_id值越大,平均每个foo_id需要的空间就越大。Oracle 平均每个元素需要 1.1 字节来存储数字 1-10,1.92 字节来存储 1-100,2.89 字节来存储 1-1,000,3.89 字节来存储 1-10,000,4.89 字节来存储 1-100,000,以及5.89 字节存储 1-1,000,000。因此,让我们估计我们的示例foo_id需要 6 个字节,并且foo_str需要 50 个字节,因为平均值foo_str大约为 50 个字节。因此,我们将估计 56 字节的行大小。

每个块的行数

<<rows per block>> = 
  floor( 8192 * 
          (1 - 10/100) / 
          56 ) + 1
Run Code Online (Sandbox Code Playgroud)

每块 132 行。如果我们要估算一个 100 万行的表的大小,

<<size of table>> = 
  ceil( 1000000 / 132 ) *
    8192
Run Code Online (Sandbox Code Playgroud)

结果为 59.19 MB。

现在,让我们测试一下我们的估计

我们将插入 100 万行,foo_id从 1 到 1,000,000,foo_str是一个随机长度在 1 到 100 之间的字符串。

SQL> ed
Wrote file afiedt.buf

  1  insert into foo
  2    select level, dbms_random.string( 'p', dbms_random.value(1,100))
  3      from dual
  4*  connect by level <= 1000000
SQL> /

1000000 rows created.
Run Code Online (Sandbox Code Playgroud)

我们对平均行长度的估计是正确的(请注意,实际上,您不会如此接近——您对可变列大小的估计不会那么准确)

SQL> exec dbms_stats.gather_table_stats( 'SCOTT', 'FOO' );

PL/SQL procedure successfully completed.
SQL> ed
Wrote file afiedt.buf

  1  select avg_row_len, num_rows
  2    from user_tables
  3*  where table_name = 'FOO'
SQL> /

AVG_ROW_LEN   NUM_ROWS
----------- ----------
         56    1000000
Run Code Online (Sandbox Code Playgroud)

但实际的桌子有多大?最常用的方法是查看 72 MB 的段大小。

SQL> select sum(bytes)/1024/1024 mb
  2    from user_segments
  3   where segment_name = 'FOO';

        MB
----------
        72
Run Code Online (Sandbox Code Playgroud)

我们的猜测偏离了大约 20%,那时我们对行大小的估计是完美的。这是因为 Oracle 将空间分配给我们忽略的称为范围的块中的表。对此有不同的算法,这取决于表空间的设置。假设所有表都在本地管理的表空间中的最新 Oracle 版本,您将在统一范围分配和自动范围分配之间进行选择。在我的示例中,我使用的表空间使用自动范围分配。反过来,确切的算法可能取决于您使用的 Oracle 版本。但是,在我的情况下,前 16 个范围是 64 kb,接下来的 63 个范围是 1 MB,最后一个范围是 8 MB

SQL> ed
Wrote file afiedt.buf

  1  select bytes, count(*)
  2    from user_extents
  3   where segment_name = 'FOO'
  4   group by bytes
  5*  order by bytes
SQL> /

     BYTES   COUNT(*)
---------- ----------
     65536         16
   1048576         63
   8388608          1
Run Code Online (Sandbox Code Playgroud)

这意味着我可能有点不走运,我的数据比总共 64 MB 的 79 个盘区多一点,所以我不得不分配大小为 8 MB 的第 80 个盘区,总共 72 MB。我们可以使用dbms_space 包来获取有关正在使用多少空间的更多详细信息。当我们这样做时,我们看到我们实际上只使用了已分配的 72 MB 中的 66.22 MB。所以我们的实际估计误差真的只有~10%

SQL> ed
Wrote file afiedt.buf

  1  DECLARE
  2   l_space_used NUMBER;
  3   l_space_allocated NUMBER;
  4   l_chained_pct NUMBER;
  5  BEGIN
  6    dbms_space.object_space_usage(
  7      'SCOTT',
  8      'FOO',
  9      'TABLE',
 10      NULL,
 11      l_space_used,
 12      l_space_allocated,
 13      l_chained_pct);
 14    dbms_output.put_line('Space Used: ' || TO_CHAR(round(l_space_used/1024/1024,2)) || ' MB');
 15    dbms_output.put_line('Space Allocated: ' || TO_CHAR(l_space_allocated/1024/1024) || ' MB');
 16    dbms_output.put_line('Chained Percentage: ' || TO_CHAR(l_chained_pct));
 17* END;
SQL> /
Space Used: 66.22 MB
Space Allocated: 72 MB
Chained Percentage: 0

PL/SQL procedure successfully completed.
Run Code Online (Sandbox Code Playgroud)

有捷径吗?

如果您使用的是最新版本的 Oracle,则可以使用该dbms_space.create_table_cost过程来估计表的大小。有几种方法可以做到这一点。第一个选项是传入行的大小。使用我们的 56 字节估计,产生 64 MB 的表大小估计,使用 63.52 MB,非常接近

SQL> ed
Wrote file afiedt.buf

  1  DECLARE
  2   l_used_bytes NUMBER;
  3   l_allocated_bytes NUMBER;
  4  BEGIN
  5    dbms_space.create_table_cost('USERS',
  6                                 56,
  7                                 1000000,
  8                                 10,
  9                                 l_used_bytes,
 10                                 l_allocated_bytes);
 11    dbms_output.put_line('Used Bytes: ' || TO_CHAR(round(l_used_bytes/1024/1024,2)) || ' MB');
 12    dbms_output.put_line('Alloc Bytes: ' || TO_CHAR(l_allocated_bytes/1024/1024) || ' MB');
 13* END;
SQL> /
Used Bytes: 63.52 MB
Alloc Bytes: 64 MB

PL/SQL procedure successfully completed.
Run Code Online (Sandbox Code Playgroud)

您还可以指定将在您的表中的列的数据类型。但是,由于无法指定平均大小,因此这往往不如自己做准确。但是,在我们的例子中,这非常好,因为我们的VARCHAR2列碰巧填充了平均为列最大大小一半的字符串。但是,在我们的例子中,它正确估计了 72 MB 的分配大小,使用了 67.94 MB。

SQL> ed
Wrote file afiedt.buf

  1  DECLARE
  2   l_used_bytes NUMBER;
  3   l_allocated_bytes NUMBER;
  4   l_cols sys.create_table_cost_columns;
  5  BEGIN
  6    l_cols := sys.create_table_cost_columns(
  7            sys.create_table_cost_colinfo('NUMBER',10),
  8            sys.create_table_cost_colinfo('VARCHAR2',100));
  9    dbms_space.create_table_cost('USERS',
 10                                 l_cols,
 11                                 1000000,
 12                                 10,
 13                                 l_used_bytes,
 14                                 l_allocated_bytes);
 15    dbms_output.put_line('Used Bytes: ' || TO_CHAR(round(l_used_bytes/1024/1024,2)) || ' MB');
 16    dbms_output.put_line('Alloc Bytes: ' || TO_CHAR(l_allocated_bytes/1024/1024) || ' MB');
 17* END;
SQL> /
Used Bytes: 67.94 MB
Alloc Bytes: 72 MB

PL/SQL procedure successfully completed.
Run Code Online (Sandbox Code Playgroud)

  • 我通常在分析数据样本后使用`DBA_TABLES.avg_row_len`,然后添加 10% 作为开销。 (2认同)