Как использовать Oracle без транзакций? - PullRequest
4 голосов
/ 14 июля 2010

MySQL имеет специальный тип таблицы MyISAM, который не поддерживает транзакции. Есть ли у Oracle что-то подобное? Я хотел бы создать базу данных только для записи (для ведения журнала), которая должна быть очень быстрой (будет хранить много данных) и не требует транзакций.

Ответы [ 8 ]

8 голосов
/ 14 июля 2010

Транзакции являются ключевыми для операций базы данных 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, поэтому происходит 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 секунды, это не так уж плохо.Основная проблема с этим подходом заключается в необходимости очищать буфер для округления свободных записей;это боль, например, в конце сеанса.Если что-то приводит к сбою сервера, непроверенные записи теряются.Другая проблема, связанная с работой в памяти, заключается в том, что она потребляет память (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 раза быстрее, чем за одну запись appraoch.Несмотря на это, я мог бы выполнить более трех тысяч дискретных транзакций менее чем за полсекунды на ноутбуке (далеко не лучшем).Итак, вопрос: сколько узких мест заносится в журнал?


Чтобы избежать недоразумений:

@ JulesLt опубликовал свой ответ, когда я работал над своим PoC.Хотя в наших взглядах есть сходства, я думаю, что различия в предлагаемых обходных решениях заслуживают публикации.


"Каково время для write_each без автономной, но единственной фиксации в конце?это не важно, что увеличение объема - это большой выигрыш "

Мои тайминги предполагают что-то немного другое.Замена COMMIT на запись с одним COMMIT в конце примерно вдвое сокращает прошедшее время.Все еще медленнее, чем громоздкий подход, но не так сильно.

Ключевым моментом здесь является бенчмаркинг .Мое доказательство концепции работает примерно в шесть раз быстрее, чем тест Жюля (у моей таблицы есть один индекс).Причин тому может быть множество - спецификация машины, версия базы данных (я использую Oracle 11gR1), структура таблиц и т. Д. Другими словами, YMMV.

Итак, учение таково: сначала решите, чтото, что нужно сделать для своего приложения, а затем сравнить его для своей среды.Только рассмотрите другой подход, если ваш тест указывает на серьезную проблему производительности.Предупреждение Кнута о преждевременной оптимизации применяется.

4 голосов
/ 14 июля 2010

Ближайшим может быть создание табличного пространства NOLOGGING и использование опции NOLOGGING для создания таблицы внутри него - хотя это может применяться только для массовых операций (т. Е. INSERT / * + APPEND * / требуется подсказка).

Это удаляет REDO за счет потери целостности и данных в случае сбоя БД.

Я не знаю, что на самом деле это будет «быстрее», и вам также следует учитывать параллелизм (если у вас много процессов, пытающихся записать в одну и ту же таблицу, вам может быть лучше использовать транзакции, которые записывают ожидающие обновления в повтор логи, чем пытаться все обновить "реальную" таблицу).

Хотя я на самом деле не исследовал NOLOGGING - я редко сталкивался с тем, что узким местом приложения была скорость INSERT - когда я это делал, проблема заключалась в стоимости обновления индексов, а не в таблице.

Я только что провел быстрое тестирование на моей довольно слабой базе данных для разработки (с включенным REDO). Используя автономную транзакцию для каждой строки - поэтому каждая строка начинает новую транзакцию и заканчивается фиксацией, я могу записать / зафиксировать более 1000 строк в индексированную таблицу журнала за 1 секунду против примерно 0,875 секунды, выполнив 1000 вставок без фиксации.

Выполнение вставки из 1000 строк за один клик с использованием операции объемной обработки занимает небольшую долю секунды, поэтому, если вы можете выполнить групповую загрузку журналов, сделайте это.

Некоторые другие мысли: Будет ли выполнять эту работу внешняя таблица, то есть записывать в файл журнала, который вы затем монтируете как внешнюю таблицу в Oracle, когда / если вам нужно читать из нее?

2 голосов
/ 14 июля 2010

По моему опыту, ведение журнала лучше всего выполнять в виде плоского файла.Я считаю, что журналы, как правило, не особенно важны - до тех пор, пока что-то пойдет не так, когда они станут критическими.Из-за этого я не хочу транзакционного контроля моей регистрации.Если мне нужно откатить транзакцию, потому что есть проблема, я действительно не хочу откатывать данные журналирования, потому что это то, что я собираюсь использовать, чтобы помочь определить, в чем была проблема.Кроме того, как вы регистрируете, что существует проблема с подключением к базе данных, если журнал хранится в базе данных, к которой вы не можете подключиться?

Поделиться и наслаждаться.

1 голос
/ 15 июля 2010

"это должно быть очень быстро"

Существует компромисс (иногда) между быстрым и восстанавливаемым.

В Oracle восстанавливаемость достигается с помощью файла журнала повторов. Каждый раз, когда вы фиксируете, база данных «средство записи журнала» выполняет синхронный вызов для записи ожидающих изменений в файл. Под синхронным я имею в виду, что файловая система ожидает подтверждения успешной записи, прежде чем сказать, что фиксация прошла успешно.

Если вы ведете много журналов (особенно из нескольких сеансов одновременно), когда каждая строка в файле журнала фиксируется независимо (например, автономная транзакция), тогда это может быть узким местом.

Если вам не нужен этот уровень восстановления (т.е. вы можете позволить себе потерять последние несколько строк ваших данных журнала из ваших журналов в случае серьезного сбоя), посмотрите на NOWAIT опция фиксации.

Если вы не можете позволить себе что-либо потерять, то лучше всего ДЕЙСТВИТЕЛЬНО быстро хранить данные (это может быть кэш с резервным питанием от батареи).

0 голосов
/ 22 июля 2010

Другой вариант, если вам нужна чрезвычайно высокая производительность, - рассмотреть базу данных Oracle TimesTen In-Memory: http://www.oracle.com/technology/products/timesten/index.html

0 голосов
/ 14 июля 2010

PRAGMA AUTONOMOUS_TRANSACTION

Это позволит вам регистрировать и фиксировать свой журнал, не влияя на окружающие транзакции.Ведение журнала является одним из немногих приемлемых вариантов использования для автономных транзакций.Он делает то, что говорит, позволяет вам написать функцию / процедуру pl / sql, которая может зафиксировать свою работу, не влияя на транзакцию, в которой она может или не может уже участвовать. Она «автономна».

au · ton · o · mous 1. (страны или региона) Наличие самоуправления.2. Действовать самостоятельно или иметь право делать это: «автономный комитет школьного совета».

Документы Oracle :

Прагма AUTONOMOUS_TRANSACTION изменяет способ работы подпрограммы в транзакции.Подпрограмма, помеченная этой прагмой, может выполнять операции 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;
0 голосов
/ 14 июля 2010

Это похоже на решение в поисках проблемы.

Вы тестировали производительность?Oracle достаточно быстр для вас, как есть?Управление транзакциями встроено в способ работы Oracle, и, пытаясь обойти его, кажется, что вы создаете работу для себя.

Кажется, вы определили управление транзакциями как проблему, не зная, существует липроблема.Что происходит позже, когда у вас на столе несколько писателей?Или читатели блокируют писателей?

0 голосов
/ 14 июля 2010

Что бы я сделал в аналогичном случае, это записать журналы в файл (добавление к файлу, вероятно, самый быстрый способ сохранить ваши журналы), а затем сделать так, чтобы пакет процессов регулярно вставлял эти журналы в БД интервалы. Если, конечно, вставка непосредственно в БД не является достаточно быстрой ... но вам придется проверить ...

...