我们的应用程序管理一个包含每个用户行集的表 计算密集型查询的结果。将此结果存储在表格中 似乎是加快进一步计算的好方法。
该表的结构基本如下:
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;
/
这有点改善了性能,但仍然不能令人满意。所以 我们正在探索加速这一过程的方法,以及这些问题 我们经历:
per_user_result_set
表GLOBAL TEMPORARY,以便它
是孤立的,我们可以TRUNCATE
例如...但我们的应用程序
由于网络问题,有时会失去与Oracle的连接
自动重新连接。到那时我们失去了我们的内容
计算。ORA_HASH(user_login) % num_buckets
重新分配行。
但我们担心这会使SELECT
操作慢得多。
这将导致表的数量恒定,索引更小
在DELETE或INSERT操作中受影响。简而言之,“分配表为
差”。ALTER TABLE per_user_result_set NOLOGGING
。事实并非如此
改善事情。CREATE TABLE ... ORGANIZATION INDEX COMPRESS 1
。这个速度
比例为1:5。对于这些方法,你有什么可以建议吗?
请注意。我们的客户将Oracle数据库从9i运行到11g,将XE版本运行到 企业版。这是我们需要的各种版本 兼容。
感谢。
答案 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行设计的执行计划,反之亦然。
唯一的开销是需要在用户的架构中创建表。但据推测,您已经为新用户设置了一些设置 - 创建帐户,授予权限等 - 所以这不应该是一个很大的困难。