没有交易如何使用Oracle?

IAd*_*ter 4 database oracle performance myisam transactions

MySQL具有不支持事务的特殊表类型MyISAM.Oracle有类似的东西吗?我想创建只写数据库(用于日志记录),需要非常快(将存储大量数据)并且不需要事务.

APC*_*APC 8

事务是SQL数据库操作的关键.它们肯定是Oracle的基础.如果不发出提交,就无法永久写入Oracle表,而且!有交易.

Oracle允许我们将表指定为NOLOGGING,它不会生成重做日志.这仅用于批量加载(使用INSERT /*+ APPEND */提示),建议切换到LOGGING并尽快退回.因为未记录的数据无法恢复.如果你不想恢复它,为什么还要先写它呢?

另一种方法是批量写入内存中的写入,然后使用批量插入来编写它们.这很快.

这是一个简单的日志表和概念证明包:

create table log_table
(ts timestamp(6)
 , short_text varchar(128)
 , long_text varchar2(4000)
 )
 /

create or replace package fast_log is
     procedure init;
     procedure flush;
     procedure write (p_short log_table.short_text%type
                      , p_long log_table.long_text%type);
end fast_log;
/
Run Code Online (Sandbox Code Playgroud)

日志记录保存在PL/SQL集合中,该集合是具有会话范围的内存中结构.INIT()过程初始化缓冲区.FLUSH()过程将缓冲区的内容写入LOG_TABLE.WRITE()过程将一个条目插入缓冲区,如果缓冲区具有必需的条目数,则调用FLUSH().

create or replace package body fast_log is

    type log_buffer is table of log_table%rowtype;
    session_log log_buffer;

    write_limit constant pls_integer := 1000;
    write_count pls_integer;

     procedure init
     is
     begin
        session_log := log_buffer();
        session_log.extend(write_limit);
        write_count := 0;
     end init;

     procedure flush
     is
     begin
        dbms_output.put_line('FLUSH::'||to_char(systimestamp,'HH24:MI:SS.FF6')||'::'||to_char(write_count));
        forall i in 1..write_count
            insert into log_table
                values session_log(i);
        init;
     end flush;

     procedure write (p_short log_table.short_text%type
                      , p_long log_table.long_text%type)

     is
        pragma autonomous_transaction;
     begin
        write_count := write_count+1;
        session_log(write_count).ts := systimestamp;
        session_log(write_count).short_text := p_short;
        session_log(write_count).long_text := p_long;

        if write_count = write_limit
        then
            flush;
        end if;

        commit;

     end write;

begin
    init;
end fast_log;
/
Run Code Online (Sandbox Code Playgroud)

写入日志表使用AUTONOMOUS_TRANSACTION编译指示,因此发生COMMIT而不影响触发刷新的周围事务.

对DBMS_OUTPUT.PUT_LINE()的调用可以使监视进度变得容易.所以,让我们看看它有多快......

SQL> begin
  2      fast_log.flush;
  3      for r in 1..3456 loop
  4          fast_log.write('SOME TEXT', 'blah blah blah '||to_char(r));
  5      end loop;
  6      fast_log.flush;
  7  end;
  8  /
FLUSH::12:32:22.640000::0
FLUSH::12:32:22.671000::1000
FLUSH::12:32:22.718000::1000
FLUSH::12:32:22.749000::1000
FLUSH::12:32:22.781000::456

PL/SQL procedure successfully completed.

SQL>
Run Code Online (Sandbox Code Playgroud)

嗯,在0.12秒内有3456个记录,这不算太破旧.这种方法的主要问题是需要刷新缓冲区来舍入松散的记录; 这是一个痛苦,例如在会话结束时.如果某些内容导致服务器崩溃,则会丢失未刷新的记录.在内存中执行操作的另一个问题是它消耗内存(durrrr),因此我们无法使缓存太大.

为了便于比较,我在程序包中添加了一个过程,每次调用时都会将一条记录直接插入到LOG_TABLE中,再次使用自治事务:

 procedure write_each (p_short log_table.short_text%type
                  , p_long log_table.long_text%type)

 is
    pragma autonomous_transaction;
 begin
    insert into log_table values ( systimestamp, p_short, p_long );

    commit;

 end write_each;
Run Code Online (Sandbox Code Playgroud)

这是它的时间:

SQL> begin
  2      fast_log.flush;
  3      for r in 1..3456 loop
  4          fast_log.write_each('SOME TEXT', 'blah blah blah '||to_char(r));
  5      end loop;
  6      fast_log.flush;
  7  end;
  8  /
FLUSH::12:32:44.157000::0
FLUSH::12:32:44.610000::0

PL/SQL procedure successfully completed.

SQL>
Run Code Online (Sandbox Code Playgroud)

众所周知,挂钟定时不可靠,但批量处理方法比单个记录appraoch快2-3倍.即便如此,我还是可以在不到半秒的时间内(远离顶级的)笔记本电脑执行超过三千次离散交易.所以,问题是:记录了多少瓶颈?


为了避免任何误解:

@JulesLt在我处理PoC时发布了他的答案.虽然我们的观点有相似之处,但我认为建议的解决方法的差异值得发布.


"没有自主但最终只有一次提交的write_each的时机是什么时候?我的时间表明它并不重要 - 填充插入是一个巨大的胜利"

我的时间表明略有不同.每次写入时用一个COMMIT替换COMMIT大致将经过的时间减半.仍然比批量方法慢,但不是那么多.

这里的关键是基准测试.我的概念证明运行速度比Jules的测试速度快六倍(我的表有一个索引).这可能有多种原因 - 机器规格,数据库版本(我使用的是Oracle 11gR1),表结构等等.换句话说,YMMV.

因此,教学是:首先确定为您的应用程序做正确的事情,然后根据您的环境进行基准测试.如果您的基准测试表明存在严重的性能问题,则只考虑不同的方 Knuth关于过早优化的警告适用.