database - 如何在没有事务的情况下使用 Oracle?

标签 database performance oracle transactions myisam

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

最佳答案

事务是 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;
/

日志记录保存在一个 PL/SQL 集合中,这是一个具有 session 作用域的内存结构。 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;
/

写入日志表使用 AUTONOMOUS_TRANSACTION pragma,因此 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>

嗯,3456 条记录在 0.12 秒内,这还不算太差。这种方法的主要问题是需要刷新缓冲区以收集松散的记录;这很痛苦,例如在 session 结束时。如果某事导致服务器崩溃,未刷新的记录将丢失。在内存中执行操作的另一个问题是它会消耗内存 (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;

这是它的时间安排:

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>

众所周知,挂钟计时不可靠,但批处理方法比单记录方法快 2-3 倍。即便如此,我还是可以在不到半秒的时间内在一台(远非顶级)笔记本电脑上执行三千多个离散事务。所以,问题是:日志记录有多少瓶颈?


为避免任何误解:

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


"What's the timing for write_each without the autonomous but a single commit at the end? My timings suggest it is not significant - that bulking the insert is the big win"

我的时间安排略有不同。在最后用单个 COMMIT 替换每次写入的 COMMIT 大致减半了耗时。仍然比批量方法慢,但几乎没有那么多。

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

因此,教学内容是:首先决定为您的应用程序做正确的事情,然后为您的环境进行基准测试。只有当您的基准测试表明存在严重的性能问题时,才考虑采用不同的方法。 Knuth 关于 premature optimization 的警告适用。

关于database - 如何在没有事务的情况下使用 Oracle?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3244439/

相关文章:

c - 使用c语言连接oracle数据库

sql - 为什么存储过程名称有最大长度?

java - RUL-05717 : The identifier "Header.Teachers.Courses" is not valid here

php - 如何将选定值从选择框传递到研究

java - 无法登录sqldeveloper

mysql - 面向文档的数据库作为主数据库和 RDBMS 数据库作为辅助数据库?

mysql - 使用php查询计算字段总计

performance - CUDA内核: performance drops by 10x when increased loop count by 10%

c - 如何最好地迭代 C 数组?使用指针还是索引?

php - 将一个文件拆分成多个文件,对网站性能有什么影响?