在具有索引结构的大型表上改进DELETE和INSERT时间

时间:2012-02-27 12:26:53

标签: oracle database-performance database-partitioning

我们的应用程序管理一个包含每个用户行集的表 计算密集型查询的结果。将此结果存储在表格中 似乎是加快进一步计算的好方法。

该表的结构基本如下:

CREATE TABLE per_user_result_set
           ( user_login         VARCHAR2(N)
           , result_set_item_id VARCHAR2(M)
           , CONSTRAINT result_set_pk PRIMARY KEY(user_login, result_set_item_id)
           )
           ;

我们应用程序的典型用户将此结果集计算为30次a 一天,结果集包含1个单项和500,000个项目。 典型的客户将向生产数据库中声明大约500个用户。 因此,该表通常包含500万行。

我们用于更新此表的典型查询是:

BEGIN
    DELETE FROM per_user_result_set WHERE user_login = :x;
    INSERT INTO per_user_result_set(...) SELECT :x, ... FROM ...;
END;
/

遇到性能问题后(DELETE部分需要很长时间) 我们决定有一个GLOBAL TEMPORARY TABLE(在提交删除行上)来保存 要从表中删除的行的“delta”和要插入的行:

BEGIN
    INSERT INTO _tmp
    SELECT ... FROM ...
     MINUS SELECT result_set_item_id
             FROM per_user_result_set
            WHERE user_login = :x;

    DELETE FROM per_user_result_set
          WHERE user_login = :x
            AND result_set_item_id NOT IN (SELECT result_set_item_id
                                             FROM _tmp
                                          );
    INSERT INTO per_user_result_set
    SELECT :x, result_set_item_id
      FROM _tmp;

    COMMIT;
END;
/

这有点改善了性能,但仍然不能令人满意。所以 我们正在探索加速这一过程的方法,以及这些问题 我们经历:

  • 我们本来希望使用表分区(通过user_login进行分区)。 但是分区并不总是可用的(在我们测试的数据库上) ORA-00439)。我们的客户无法全都负担Oracle Enterprise Edition 支付额外功能。
  • 我们可以制作per_user_result_set表GLOBAL TEMPORARY,以便它 是孤立的,我们可以TRUNCATE例如...但我们的应用程序 由于网络问题,有时会失去与Oracle的连接 自动重新连接。到那时我们失去了我们的内容 计算。
  • 我们可以将该表拆分为一定数量的桶,进行查看 UNIONs所有这些桶,并触发INSTEAD OF UPDATE和DELETE 该视图,并根据ORA_HASH(user_login) % num_buckets重新分配行。 但我们担心这会使SELECT操作慢得多。 这将导致表的数量恒定,索引更小 在DELETE或INSERT操作中受影响。简而言之,“分配表为 差”。
  • 我们试过ALTER TABLE per_user_result_set NOLOGGING。事实并非如此 改善事情。
  • 我们试过CREATE TABLE ... ORGANIZATION INDEX COMPRESS 1。这个速度 比例为1:5。
  • 我们尝试过每个user_login有一个表。这正是我们所能做到的 通过使用等于数量的多个分区进行分区 不同的user_logins和精心选择的哈希函数。表现因素是 1:10。但我真的想避免这种解决方案:必须维持一个 基于每个用户的大量索引,表,视图。这将是 为用户带来了有趣的性能提升,但对于我们的维护者来说并非如此 系统。
  • 由于用户同时工作,我们无法创建新的 表并将其与旧表交换。

对于这些方法,你有什么可以建议吗?

请注意。我们的客户将Oracle数据库从9i运行到11g,将XE版本运行到 企业版。这是我们需要的各种版本 兼容。

感谢。

2 个答案:

答案 0 :(得分:1)

  

我们试图为每个user_login创建一个表。这正是我们的意思   可以通过使用等于的分区数来进行分区   不同user_logins的数量和精心选择的散列函数。   性能系数为1:10。但我真的想避免这种情况   解决方案:必须维护大量的索引,表,视图   每个用户的基础。这将是一个有趣的性能增益   用户,但不是我们系统的维护者。

然后,您可以创建一个存储过程来基于每个用户生成这些表吗?或者,更好的是,根据所支持的Oracle许可证,这个存储过程是否最合适呢?

If Partitioning option 
  then create or truncate user-specific list partition
Else 
  drop user-specific result table
  Create user-specific result table 
      as Select from template result table
  create indexes
  create constraints
  perform grants
end if
Perform insert

答案 1 :(得分:1)

如果您的所有用户都使用的是11g企业版,我建议您使用Oracle's built-in result-set caching,而不是尝试使用自己的用户。但事实并非如此,所以让我们继续前进。

另一个有吸引力的选择可能是使用PL / SQL集合而不是表。在内存中,这些检索更快,并且需要更少的维护。您需要的所有版本都支持它们。但是,它们是会话变量,因此如果您有大量具有大结果集的用户会给您的PGA分配带来压力。当网络连接中断时,它们的数据也会丢失。所以这可能不是您正在寻找的解决方案。

问题的核心是这句话:

DELETE FROM per_user_result_set WHERE user_login = :x;

这本身并不是问题,但是数据分布存在极大的变化。直截了当地,删除单行将具有与删除50万行非常不同的性能配置文件。而且由于您的用户不断刷新他们的数据,除了为您的用户提供他们自己的表外,您无法处理这些数据。

你说你不希望每个用户都有一张桌子,因为

  

“[它]对用户来说是一个有趣的性能提升,但不是   对于我们系统的维护者,“

系统的存在是为了我们的用户的利益。只要它能帮助我们为他们提供更好的服务,对我们来说便利是很好的。但他们对良好工作经验的需求胜过我们:他们支付账单。

但我怀疑为每个用户设置单独的表是否真的增加了工作量。我假设每个用户都有自己的帐户,因此架构。

我建议你坚持使用索引组织表。您只需要主键中的列并保持单独的索引是不必要的开销(插入和删除)。每个用户拥有一个表的最大优点是可以在刷新过程中使用TRUNCATE TABLE,这比删除要快得多。

因此,您的刷新过程将如下所示:

BEGIN
    TRUNCATE TABLE per_user_result_set REUSE STORAGE;
    INSERT INTO per_user_result_set(...) 
          SELECT ...  FROM  ...;
    DBMS_STATS.GATHER_TABLE_STATS(user
          , 'PER_USER_RESULT_SET'
          , estimate_percent=>10);
    COMMIT;
END;
/

请注意,您不再需要包含USER列,因此您的表只有result_set_item_id的单列(IOT适用性的另一个指示。

收集表统计信息不是强制性的,但建议您这样做。结果集的大小变化很大,并且当表只有一行时,您不希望使用为500000行设计的执行计划,反之亦然。

唯一的开销是需要在用户的架构中创建表。但据推测,您已经为新用户设置了一些设置 - 创建帐户,授予权限等 - 所以这不应该是一个很大的困难。