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/