Oracle中的包级缓存导致与真实的数据更改不一致

7z5jn7bk  于 2023-05-16  发布在  Oracle
关注(0)|答案(1)|浏览(149)

我遇到过这样的情况,数据缓存导致报告中出现错误的结果,因为由于在线事务,表中的数据已经更新,但报告显示的是以前缓存的数据。
实现缓存的原因-我们有第三方SQL,它使用一个昂贵的内联函数,并且这个函数在一个SQL中使用不同的参数多次。此外,该函数的设计方式是,它获取每个调用中的所有信息,并仅返回基于输入参数的请求。
因此,为了提高性能,引入了一个包集合来存储第一个内联函数执行本身中获取的所有数据,然后为所有后续内联函数调用从该缓存集合本身返回数据。
示例:

SELECT
    pack_test.fn_costly('Person','Name'),
    pack_test.fn_costly('Person','Age'),
    pack_test.fn_costly('Person', 'Gender'),
    pack_test.fn_costly('Person', 'Address')
FROM DUAL;

在这个例子中,fn_costly被设计为获取与Person相关的所有信息,但它只返回第二个参数中请求的值。因此,添加了一个包级集合来存储与第一次执行“Name”时的Person相关的所有信息,并且从Cache返回了第二、第三、第四次调用。
在这种方法中,我的假设是,当SQL进入时,这个集合将始终为空,但看起来像是,由于启用了连接池,以前的执行数据已经存在于集合中,因此,当前执行最终使用了以前缓存的数据,这是错误的。
有没有什么方法可以确保打包的集合在每次独立的SQL执行之前都是空的?
PS:我只能在包(函数存在的地方)或由第三方触发的SQL中进行更改。
谢谢
附加信息(16/5):为了暂时解决这个问题,我已经将'HH 24 MI'附加到缓存的集合索引中,这意味着如果SQL与缓存的数据在同一分钟内执行,则缓存是有效的,可以使用。否则,重新加载该高速缓存并将当前HH 24 MI附加到索引列,然后使用新数据。我知道这不是一个100%可靠的方法,但现在可以守住堡垒。如果有人有更好的解决方案,请分享。

rsl1atfo

rsl1atfo1#

包变量在会话的生存期内保留在内存中,所以是的,连接池可能会导致这种行为。因为在SELECT子句中没有执行顺序的概念,所以你不能保证Oracle调用这些子句的顺序,所以你必须有一种方法来将函数本身标识为第一个调用。这可能会变得棘手(您可能会使用v$session中的SQL_IDSQL_EXEC_ID的组合,但这会变得很难看)。
也许这种方法是错误的。如果是我,我会从调优底层函数开始,让它在合理的时间内运行,这样就不需要缓存了。如果你够快,你就完了。
如果仍然需要合并,请重写函数,使其具有对象返回类型,以便可以一次返回所有字段。有多种方法可以做到这一点。这里有一个:

CREATE OR REPLACE TYPE myobjectype AS OBJECT (name varchar2(128), age integer, gender char(1), address varchar2(256))
/

CREATE OR REPLACE FUNCTION FN_COSTLY
  RETURN myobjecttype
AS
  myobject myobjecttype;
BEGIN
  -- expensive stuff to get data
  
  myobject := myobjecttype('My name',13,'M','123 Main St');
  dbms_output.put_line('got here');
  RETURN myobject;
END;
/

SELECT TREAT(result AS myobjecttype).name name,
       TREAT(result AS myobjecttype).age age,
       TREAT(result AS myobjecttype).gender gender,
       TREAT(result AS myobjecttype).address address
  FROM (SELECT /*+ NO_MERGE */  FN_COSTLY result FROM dual)

然后确保只得到一条“gotthere”消息(这就是使用NO_MERGE提示的原因,以防止Oracle合并查询块并调用函数四次),然后注解掉或删除dbms_output调用。
结果:

相关问题