代码之家  ›  专栏  ›  技术社区  ›  IAdapter

如何在不进行事务的情况下使用Oracle?

  •  4
  • IAdapter  · 技术社区  · 14 年前

    mysql有不支持事务的特殊表类型myisam。甲骨文有这样的东西吗?我想创建只写数据库(用于日志记录),它需要非常快(将存储大量数据)并且不需要事务。

    8 回复  |  直到 14 年前
        1
  •  8
  •   APC    14 年前

    事务是SQL数据库操作的关键。它们当然是甲骨文的基础。如果不发出commit,就无法永久地写入Oracle表,而且lo!有交易记录。

    Oracle允许我们指定要取消登录的表,这不会生成重做日志。这仅适用于批量装载(使用 INSERT /*+ APPEND */ 提示),建议切换到日志记录并尽快返回。因为没有记录的数据是不可恢复的。如果你不想找回它,为什么要先写呢?

    另一种方法是在内存中成批地进行写操作,然后使用大容量插入来写操作。这很快。

    下面是一个简单的日志表和一个概念验证包:

    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;
    /
    

    日志记录保存在PL/SQL集合中,该集合是内存结构 具有会话范围 . init()过程初始化缓冲区。flush()过程将缓冲区的内容写入log_表。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;
    /
    

    写入日志表使用自治事务pragma,因此提交不会影响触发刷新的周围事务。

    调用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>
    

    嗯,0.12秒内有3456张唱片,还不算太差。这种方法的主要问题是需要刷新缓冲区来聚集松散的记录;这是一种痛苦,例如在会话结束时。如果有什么导致服务器崩溃,未刷新的记录将丢失。在内存中做事情的另一个问题是它会消耗内存(durrrr),因此我们不能使缓存太大。

    为了进行比较,我在包中添加了一个过程,它在每次调用日志表时直接将一条记录插入日志表中,同样使用自治事务:

     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;
    

    以下是时间安排:

    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>
    

    墙上时钟计时是出了名的不可靠,但批量方法比单记录Appraoch快2-3倍。即使如此,我也可以在不到半秒钟的时间内,在一台笔记本电脑上(远远超出范围)执行超过三千个离散事务。所以,问题是:日志记录有多大的瓶颈?


    为避免误解:

    @在我做POC的时候,Juleslt发布了他的答案。尽管我们的观点有相似之处,但我认为在发布这篇文章时,建议的变通方法的优点有所不同。


    “每个写作时间是什么时候? 没有自主,只有一个 最后承诺?我的时间安排建议 这并不重要-膨胀 插入是大胜利”

    我的时间安排有点不同。在结束时用一个提交来替换每次写入的提交,大约将所用时间减半。仍然比膨胀的方法慢,但不是差不多。

    关键是 标杆管理 . 我的概念验证比朱尔斯的测试快六倍(我的表有一个索引)。这可能有各种各样的原因——机器规格、数据库版本(我使用的是Oracle11gr1)、表结构等,换句话说,就是ymmv。

    所以教学方法是:首先决定为应用程序做什么是正确的,然后为您的环境做基准测试。只有当基准测试表明存在严重的性能问题时,才考虑使用不同的方法。Knuth关于 premature optimization 应用。

        2
  •  4
  •   JulesLt    14 年前

    最接近的可能是创建一个nologing表空间,并使用nologing选项在其中创建表-尽管这可能只适用于批量操作(即,需要insert/*+append*/hint)。

    这将删除重做,如果数据库崩溃,则会损失完整性和数据。

    我不知道它实际上会“更快”,您还应该考虑并发性(如果您有许多进程试图写入同一个表,那么您最好使用将挂起的更新写入重做日志的事务,而不是尝试全部更新“real”表)。

    不过,我并没有真正调查过不登录——我很少遇到应用程序瓶颈一直是插入速度的问题——当我这样做的时候,更新索引的成本而不是表的成本才是问题所在。

    我刚刚做了一个快速测试,并在我的开发数据库中(启用了重做)。对每一行使用一个自治事务-所以每一行都启动一个新事务并以一个提交结束,我可以在1秒内将1000多行写入/提交到一个索引日志表,而在不提交的情况下执行1000个插入大约需要.875秒。

    使用批量操作在一次命中中插入1000行只是一秒钟的一小部分-因此,如果您可以批量处理日志,请执行该操作。

    其他一些想法: 当/如果需要从外部表中读取日志文件时,外部表是否会执行该任务,即写入日志文件,然后将其作为外部表装入Oracle?

        3
  •  2
  •   Bob Jarvis - Слава Україні    14 年前

    我的经验是最好将日志记录到平面文件中。我的观点是,日志通常并不特别重要——除非出现问题,这时它们才变得至关重要。因此,我不希望对日志进行事务控制。如果我需要回滚事务,因为存在问题,我真的不希望回滚日志数据,因为这正是我用来帮助确定问题的原因。此外,如果日志存储在无法连接的数据库中,如何记录连接到数据库时出现问题?

    分享和享受。

        4
  •  1
  •   Gary Myers    14 年前

    “那需要很快”

    在快速和可回收之间有一个权衡(有时)。

    在Oracle中,可恢复性是通过重做日志文件实现的。每次提交时,数据库“日志编写器”都会执行同步调用,以将未完成的更改写入文件。通过同步,我的意思是它等待文件系统确认写入成功,然后再声明提交成功。

    如果您在日志文件中的每一行独立提交(AG自治事务)的情况下执行大量日志记录(尤其是同时执行大量会话),那么这很可能是一个瓶颈。

    如果您不需要这种级别的可恢复性(即在发生严重故障时,您可以承受从日志中丢失最后几行日志数据的风险),请查看 NOWAIT 提交选项。

    如果你不能承受损失,那么你最好的选择就是快速存储(可能是一个电池备份的缓存)。

        5
  •  0
  •   Guillaume    14 年前

    对于类似的情况,我要做的是将日志写入一个文件(附加到一个文件可能是存储日志的最快方法),然后让一个进程批处理定期将这些日志插入数据库。当然,除非直接插入数据库足够快…但你必须测试…

        6
  •  0
  •   Mike Meyers    14 年前

    这似乎是寻找问题的解决方案。

    你的表演有基准吗?甲骨文对你来说足够快吗?事务管理内置于Oracle的工作方式中,并且试图解决问题,似乎您正在为自己创建工作。

    您似乎已经将事务管理确定为一个问题而不知道是否存在问题。当您在表上有多个写入程序时,稍后会发生什么?还是读者阻止作家?

        7
  •  0
  •   Brian    14 年前

    pragma自治事务

    这将允许您在不影响周围事务的情况下记录和提交日志。对于自治事务,日志记录是极少数可接受的用例之一。它执行它所说的操作,允许您编写一个pl/sql函数/过程,该函数/过程可以在不影响它可能或可能尚未参与的事务的情况下提交其工作。它是“自主的”。

    Au_·ton_·O_·MoU 1。(指一个国家或地区)有自治权。 2。独立行动或有这样做的自由:“自主的 学校董事会委员会”。

    Oracle docs :

    自治事务pragma 更改子程序的工作方式 在事务中。子程序 用此pragma标记的can do sql 操作和提交或回滚 那些行动,没有承诺 或者在主目录中回滚数据 交易。

    CREATE OR REPLACE FUNCTION FNC_LOG(p_log_text varchar2(4000))
     RETURN NUMBER
     IS
    PRAGMA AUTONOMOUS_TRANSACTION;
    BEGIN
      -- Your brief code goes here (don't abuse the evil feature that is autonomous transactions).
    END;
    
        8
  •  0
  •   Jeffrey Kemp    14 年前

    如果您需要极高的性能,另一个选择是考虑Oracle的Timesten内存数据库: http://www.oracle.com/technology/products/timesten/index.html