Есть ли способ обновить самую последнюю версию строки через SQL? - PullRequest
1 голос
/ 01 ноября 2010

Я читал, что Oracle поддерживает версии строк для работы с параллелизмом. Я хочу выполнить запрос на обновление очень большой базы данных в реальном времени, но это задание обновления должно изменить самую последнюю версию строки.

Возможно ли это через PL / SQL или просто SQL?

Отредактировано ниже **

Позвольте мне прояснить сценарий, реальную проблему, с которой мы столкнулись в очень большой базе данных. Наш клиент - известный поставщик услуг сотовой связи.

В нашей базе данных есть таблица, которая управляет записями текущего баланса, оставшегося на счете мобильного телефона клиента. Среди других столбцов таблицы в одном столбце хранится сумма пополнения баланса, а в другом - текущее активное сальдо.

У нас есть два независимых скрипта PL / SQL. Один сценарий запускается автоматически, когда клиент перезаряжает свой телефон и обновляет свой баланс.

Второй сценарий касается удержания определенных платежей со счета клиентов. Это пакетная работа, поскольку она распространяется на всех клиентов. Этот сценарий планируется запускать через определенные интервалы дня. Когда этот скрипт выполняется, он загружает 50 000 записей в память, обновляет определенные столбцы и выполняет массовое обновление обратно в таблицу.

Проблема произошла так:

Клиент, чей номер 101, связался со своим местным магазином, чтобы зарядить свой телефон. Он платит сумму. Но до того момента, когда его телефон должен был подзарядиться, запланированное время второго сценария включило второй сценарий. Второй скрипт загружал записи 50 000 клиентов в память. В этих записях в памяти одна из записей об этом клиенте тоже.

Пока не завершится пакетное обновление второго скрипта, первый скрипт успешно пополнит счет клиента.

Теперь получилось, что это фактическая таблица, столбец: «CurrentAccountBalance» обновляется до 150, но записи в памяти, над которыми работал второй скрипт, имели старый баланс клиента, т. Е. 100.

Второй скрипт должен был вычесть 10 из столбца: «CurrentAccountBalance». Когда, в соответствии с фактической работой, значение «CurrentAccountBalance» клиента должно составлять 140, из-за этой проблемы его баланс составил 90.

Теперь, как решить эту проблему.

Ответы [ 2 ]

4 голосов
/ 01 ноября 2010

Я думаю, что вы хотите, это то, что в любом случае происходит, если вы UPDATE.

Это правда, что Oracle некоторое время хранит старые данные, но только для обеспечения согласованного чтения. То есть операции чтения, которые видят только состояние, которое было в начале транзакции, даже если данные были перезаписаны за это время. Он называется Multi Version Concurrency Control и может управляться с помощью Уровень изоляции транзакции .

Вы можете явно запросить самый последний, выбрав `FOR UPDATE; это добавляет блокировку для записи, так что никто другой не может обновить ее в это время (пока ваша транзакция не закончится).

Однако, если вам нужно что-то написать (например, UPDATE), Oracle работает всегда в самой последней версии.

1 голос
/ 02 ноября 2010

Как подсказал @Markus, у вас есть состояние гонки.Если вы загружаете записи в память и работаете с ними перед обновлением строк в таблице, и что-то еще может попытаться обновить их тем временем, тогда вам нужно заблокировать их, пока вы над ними работаете.(Я предполагаю, что все, что вы делаете, слишком сложно, чтобы сделать простое одношаговое обновление).Примерно так будет работать:

DECLARE
    CURSOR c is SELECT * FROM current_balance_table FOR UPDATE;
BEGIN
    FOR r IN c LOOP
        /* Do whatever calculations you need */
        new_value := r.CurrantAccountBalance - 10;
        UPDATE current_balance_table SET CurrentAccountBalance = new_value
        WHERE CURRENT OF c;
    END LOOP:
END;

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

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

...