MySQL 具有不支持事务的特殊表类型 MyISAM。甲骨文有这样的东西吗?我想创建需要非常快(将存储大量数据)并且不需要事务的只写数据库(用于日志记录)。
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;
/
日志记录保存在 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;
/
写入日志表使用 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>
嗯,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;
以下是它的时间安排:
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 时发布了他的答案。尽管我们的观点有相似之处,但我认为建议的解决方法的差异值得发布。
“write_each 的时间是什么时候没有自治但最后只有一个提交?我的时间表明这并不重要 - 增加插入量是最大的胜利”
我的时间安排表明有些不同。最后用单个 COMMIT 替换每次写入的 COMMIT 大约会使经过的时间减半。仍然比散装方法慢,但几乎没有那么多。
这里的关键是基准测试。我的概念证明的运行速度比 Jules 的测试快大约六倍(我的表有一个索引)。这可能有各种各样的原因——机器规格、数据库版本(我使用的是 Oracle 11gR1)、表结构等。换句话说,YMMV。
所以教学是:首先决定为您的应用程序做正确的事情,然后为您的环境进行基准测试。如果您的基准测试表明存在严重的性能问题,请仅考虑不同的方法。Knuth 关于过早优化的警告适用。
最接近的可能是创建一个 NOLOGGING 表空间,并使用 NOLOGGING 选项在其中创建表 - 尽管这可能仅适用于批量操作(即 INSERT /*+ APPEND */ 需要提示)。
这将删除 REDO,但如果数据库出现故障,则会以损失完整性和数据为代价。
我不知道它实际上会“更快”,并且您还应该考虑并发性(如果您有许多进程试图写入同一个表,那么最好使用将挂起的更新写入重做日志的事务而不是尝试全部更新“真实”表)。
不过,我还没有真正研究过 NOLOGGING - 我很少遇到应用程序瓶颈一直是 INSERT 速度的点 - 当我遇到时,更新索引的成本而不是问题的表。
我刚刚完成了一个快速测试,并在我的动力不足的开发数据库上(启用了重做)。为每一行使用一个自治事务 - 因此每一行都开始一个新事务并以提交结束,我可以在 1 秒内将超过 1000 行写入/提交到索引日志表,而在不提交的情况下执行 1000 次插入大约需要 0.875 秒。
使用批量操作在单次命中中插入 1000 行只需几分之一秒 - 因此,如果您可以批量处理日志,请执行此操作。
其他一些想法:外部表是否可以完成这项工作 - 即写入一个日志文件,然后当/如果您需要读取它时将其挂载为 Oracle 中的外部表?
我的经验是,最好将日志记录到平面文件中。我的观点是日志通常不是特别重要 - 直到出现问题,此时它们变得至关重要。因此,我不想对我的日志进行事务控制。如果我因为出现问题而需要回滚事务,我真的不希望日志数据回滚,因为我将使用它来帮助确定问题所在。另外,如果日志存储在无法连接的数据库中,如何记录连接数据库出现问题?
分享和享受。
“这需要非常快”
在快速和可恢复之间存在权衡(有时)。
在 Oracle 中,可恢复性是通过重做日志文件实现的。每次提交时,数据库“日志编写器”都会进行同步调用以将未完成的更改写入文件。通过同步,我的意思是它等待文件系统确认写入已成功,然后才说提交已成功。
如果您正在执行大量日志记录(尤其是一次来自大量会话),并且日志文件中的每一行都是独立提交的(ag 自主事务),那么这很可能是一个瓶颈。
如果您不需要那种级别的可恢复性(即,如果发生重大故障,您可以承受从日志中丢失日志数据的最后几行),请查看提交的NOWAIT选项。
如果你不能失去任何东西,那么你最好的选择是真正快速的存储(可能是电池支持的缓存)。
对于类似的情况,我会做的是将日志写入文件(附加到文件可能是存储日志的最快方法),然后让进程定期将这些日志批量插入数据库。除非当然直接插入数据库足够快......但你必须测试......
这似乎是寻找问题的解决方案。
您是否对性能进行了基准测试?Oracle 对您来说足够快吗?事务管理内置于 Oracle 的工作方式中,尝试解决它似乎是在为自己创造工作。
您似乎已经将事务管理确定为一个问题,但并没有真正知道是否存在问题。当您在桌子上有多个作家时,稍后会发生什么?还是读者屏蔽了作者?
PRAGMA AUTONOMOUS_TRANSACTION
这将允许您在不影响周围事务的情况下记录和提交您的日志。日志记录是极少数可接受的自治事务用例之一。它按照它所说的去做,允许您编写一个 pl/sql 函数/过程,该函数/过程可以提交其工作,而不会影响它可能已经参与或可能尚未参与的事务。它是“自治的”。
au·ton·o·mous 1.(国家或地区的)自治的。2. 独立行事或有这样做的自由:“校董会自治委员会”。
AUTONOMOUS_TRANSACTION pragma 改变了子程序在事务中的工作方式。标有此 pragma 的子程序可以执行 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;
如果您需要极高的性能,另一个选择是考虑 Oracle 的 TimesTen In-Memory 数据库:http ://www.oracle.com/technology/products/timesten/index.html