Гость
Целевая тема:
Создать новую тему:
Автор:
Форумы / PostgreSQL [игнор отключен] [закрыт для гостей] / Как организовать хранение и извление исторических данных? / 25 сообщений из 53, страница 1 из 3
18.04.2008, 18:14
    #35265616
Dan Black
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Как организовать хранение и извление исторических данных?
Возможно, конечно, вопрос и для форума "Проектирование БД", но для начала отпишусь в "родном" форуме

Ниже описывается прототип схемы хранения истории изменения объекта и проблема, которая скоро съест мой мозг
Хочется услышать мнения умных людей по поводу структуры хранения данных (таблиц и полей), и запросов к этим структурам. А так же варианты реализации задачи :)

Имеется таблица, в которой хранится история изменения значения объекта
Код: plaintext
1.
2.
3.
4.
5.
CREATE TABLE object_history (
  object_id INT4, -- id объекта
  ttime TIMESTAMP, -- время начала транзакции
  ctime TIMESTAMP DEFAULT current_timeclock(), -- реальное время создания записи
  value VARCHAR NOT NULL -- значение объекта на момент времени ttime:ctime
);

Есть функции:
1) get_object_value(int object_id) - получить значение объекта на время транзакции
2) set_object_value(int object_id, varchar value) - записать значение объекта на время транзакции

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

Код: plaintext
1.
2.
3.
4.
5.
begin
get_object_value( 100 ); -- 1
-- много кода
get_object_value( 100 ); -- 2
-- много кода
commit;

Проблема в том, значение объекта 100, полученное в строке "1", может отличаться от значения полученного в строке "2", так как параллельно работают другие транзакции, которые могут изменить значение объекта 100.

Доп. условие задачи: Если внутри нашей транзакции у объекта изменилось значение, то при следующем вызове get_object_value должно возвратиться новое измененное значение. Изменения значения объекта в параллельных транзакциях не должны влиять на возврат функции get_object_value.

Пример.
Код: plaintext
1.
2.
3.
4.
begin;
get_value_object( 100 ); -- возвращяет 'old_value'
set_object_value( 100 , 'new_value');
get_object_value( 100 ); -- возращает 'new_value'
commit;

внутри функции get_object_value делается простой запрос (к примеру)
Код: plaintext
SELECT * FROM object_history WHERE object_id =  100  AND ttime <= now() ORDER BY time DESC, ctime  DESC

внутри функции set_object_value так же делается запрос (к примеру)
Код: plaintext
INSERT INTO object_history(object_id, ttime, value) VALUES( 100 , now(), 'new_value');

Этих двух запросов хватает почти для всех случаев.
Вот один из случаев, для которых запросы не срабатывают
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
x: sql-код -- х - номер транзакции

 1 :begin;
 1 :set_object_value( 100 , 'new_value');
      2 :begin;
      2 :get_object_value( 100 ); -- возвращает old_value
 1 :commit;
      2 :get_object_value( 100 ); -- возвращает new_value
      2 :commit;
Есть огромное желание, чтобы при втором вызове get_object_value возвращалось то же значение, что и при первом вызове или измененное значение, но измененное в той же транзакции.
Код: plaintext
1.
----------------------------
 Verba volent, scripta manent 
...
Рейтинг: 0 / 0
18.04.2008, 18:27
    #35265645
Ёш
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Как организовать хранение и извление исторических данных?
Dan BlackЕсть огромное желание, чтобы при втором вызове get_object_value возвращалось то же значение, что и при первом вызове или измененное значение, но измененное в той же транзакции.имхо по хорошему, что бы другие транзакции не мешались - нужно соответственно усиливать изоляцию транзакций %) но тогда придётся менять логику клиента, что бы он был готов перезапускать транзакцию которая отменилась из-за нарушения изоляции.
...
Рейтинг: 0 / 0
18.04.2008, 18:30
    #35265652
Dan Black
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Как организовать хранение и извление исторических данных?
ЁшКак и хочется избежать блокировок, оставшись на уровне изоляции Read Commited
...
Рейтинг: 0 / 0
18.04.2008, 18:47
    #35265680
Ёш
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Как организовать хранение и извление исторических данных?
по идеи задача сводится к "выбрать из object_history последнюю запись с сортировкой по ctime у которой ttime = now() [текущая транзакция], если такой нет, выбрать просто последнюю запись с сортировкой по ctime" но остаётся проблема, если объект в текущей транзакции не изменялся, а в соседних - изменялся, тогда будет возвращён изменённый в соседней транзакции объект. можно запоминать состояние объекта на начало транзакции (но будет рейс кондишн между begin; и save_current_object_value(id) %) ), но имхо это не что иное как ручное эмулирование TRANSACTION ISOLATION LEVEL SERIALIZABLE =)


--
„Истина — это вовсе не то, что можно убедительно доказать, это то, что
делает всё проще и понятнее“ — Антуан де Сент-Экзюпери
...
Рейтинг: 0 / 0
18.04.2008, 18:50
    #35265690
Dan Black
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Как организовать хранение и извление исторических данных?
Задача решается, если в записи object_history сохранять время окончания транзакции создавшей эту запись. Но как это сделать?
Код: plaintext
1.
----------------------------
 Verba volent, scripta manent 
...
Рейтинг: 0 / 0
18.04.2008, 19:09
    #35265719
Ёш
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Как организовать хранение и извление исторических данных?
в таблицах есть скрытые служебные поля - xmin, xmax - вот xmin - это номер транзакции (он всегда только возрастает, по идеи :) ). получается нужно выбирать только те записи, у которых xmin <= txid_current()

пример доступа к служебным полям xmin, xmax таблицы:
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
seb=> select xmin,xmax, txid_current(), txid_current_snapshot(), * from t2;
 xmin | xmax | txid_current | txid_current_snapshot | id |   val2
------+------+--------------+-----------------------+----+----------
  6873  |     0  |          6888  |  6888 : 6888 :            |   1  | val1
  6874  |     0  |          6888  |  6888 : 6888 :            |   1  | val2
  6875  |     0  |          6888  |  6888 : 6888 :            |   1  | val3
  6876  |     0  |          6888  |  6888 : 6888 :            |   2  | val1 -  2 
  6877  |     0  |          6888  |  6888 : 6888 :            |   2  | val2 -  2 
  6878  |     0  |          6888  |  6888 : 6888 :            |   2  | val3 -  2 
  6880  |     0  |          6888  |  6888 : 6888 :            |   3  | val3 -  3 
  6881  |     0  |          6888  |  6888 : 6888 :            |   1  | val1 -  1 

--
„Истина — это вовсе не то, что можно убедительно доказать, это то, что
делает всё проще и понятнее“ — Антуан де Сент-Экзюпери
...
Рейтинг: 0 / 0
18.04.2008, 19:23
    #35265734
Dan Black
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Как организовать хранение и извление исторических данных?
если "вражеская" транзакция началась раньше, то xmin будет меньше txid_current() (см. последний случай), этого условия недостаточно :(
Код: plaintext
1.
----------------------------
 Verba volent, scripta manent 
...
Рейтинг: 0 / 0
18.04.2008, 19:36
    #35265751
Ёш
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Как организовать хранение и извление исторических данных?
да, затупил, этож тоже самое что ttime %)

ps: с пятницей :)


--
„Истина — это вовсе не то, что можно убедительно доказать, это то, что
делает всё проще и понятнее“ — Антуан де Сент-Экзюпери
...
Рейтинг: 0 / 0
19.04.2008, 03:15
    #35266018
Dan Black
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Как организовать хранение и извление исторических данных?
Ёшда, затупил, этож тоже самое что ttime %)
ps: с пятницей :)
С субботой
однако мозг почти съеден, а проблема осталась :(
...
Рейтинг: 0 / 0
20.04.2008, 08:35
    #35266725
SeniorAndre
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Как организовать хранение и извление исторических данных?
Если проблема не решается внутри системы, то на проблему надо посмотреть снаружи... :) Собственно: "А нафига?" Глядя на код у меня например возникает вопрос: "При такой структуре построения кода, зачем вообще второй вызов get_? Запиши в переменную..."
Код: plaintext
1.
2.
3.
4.
5.
begin
get_object_value( 100 ); -- 1
-- много кода
get_object_value( 100 ); -- 2
-- много кода
commit;
...
Рейтинг: 0 / 0
20.04.2008, 12:11
    #35266797
Dan Black
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Как организовать хранение и извление исторических данных?
get_... может вызываться внутри другой хп.
Код: plaintext
1.
2.
3.
4.
5.
begin
get_object_value( 100 ); -- 1
-- много кода
PERFORM proc(); -- get_object_value(100) // Вызывается внутри proc()
-- много кода
commit;
Код: plaintext
1.
----------------------------
 Verba volent, scripta manent 
...
Рейтинг: 0 / 0
20.04.2008, 14:44
    #35266944
Vladimir Sitnikov
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Как организовать хранение и извление исторических данных?
Dan Blackget_... может вызываться внутри другой хп.А чем это мешает записать в переменную? custom_variable_classes
...
Рейтинг: 0 / 0
20.04.2008, 16:05
    #35267027
Dan Black
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Как организовать хранение и извление исторических данных?
Vladimir Sitnikov Dan Blackget_... может вызываться внутри другой хп.А чем это мешает записать в переменную? custom_variable_classes объектов может быть 1 000 000 000...
+ даже если в переменную можно записать массив, доступ к его элементам будет осуществляться без индексов, что существенно замедлит работу хп
+ к тому же использование custom_variable_classes противоречит моей религии
...
Рейтинг: 0 / 0
21.04.2008, 08:39
    #35267529
Vladimir Sitnikov
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Как организовать хранение и извление исторических данных?
Dan Black Vladimir Sitnikov Dan Blackget_... может вызываться внутри другой хп.А чем это мешает записать в переменную? custom_variable_classes объектов может быть 1 000 000 000...И все они нужны одновременно? Ню ню. Сколько времени вы этот миллиард обрабатывать будете?

Dan Black+ к тому же использование custom_variable_classes противоречит моей религии
Ах да, религия, когда всё должно соотвествовать стандартам ansi sql-81, где нельзя менять уровни изоляции и т.п.


Добавьте поддержку "select * from table as of timestamp(...)" -- глядишь не только вам пригодится.

Если религия позволяет триггеры, то timetravel вам в руки
...
Рейтинг: 0 / 0
21.04.2008, 10:38
    #35267749
Dan Black
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Как организовать хранение и извление исторических данных?
Vladimir SitnikovИ все они нужны одновременно? Ню ню. Сколько времени вы этот миллиард обрабатывать будете?Ладно, нужно будет только 1000 объектов. Это что-то изменит? ;)))
Vladimir SitnikovАх да, религия, когда всё должно соотвествовать стандартам ansi sql-81, где нельзя менять уровни изоляции и т.п.Религия позволяет менять уровень изоляции транзакций, но в данном случае есть строгое требование, которое нельзя обойти.

Vladimir SitnikovЕсли религия позволяет триггеры, то timetravel вам в рукиПри уровне изоляции Read Commited там возникает такая же проблема, что я описал в конце своего первого топика.
...
Рейтинг: 0 / 0
21.04.2008, 11:30
    #35267902
LeXa NalBat
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Как организовать хранение и извление исторических данных?
Dan Black
Код: plaintext
1.
2.
3.
4.
5.
begin
get_object_value( 100 ); -- 1
-- много кода
get_object_value( 100 ); -- 2
-- много кода
commit;

Проблема в том, значение объекта 100, полученное в строке "1", может отличаться от значения полученного в строке "2", так как параллельно работают другие транзакции, которые могут изменить значение объекта 100.

Доп. условие задачи: Если внутри нашей транзакции у объекта изменилось значение, то при следующем вызове get_object_value должно возвратиться новое измененное значение. Изменения значения объекта в параллельных транзакциях не должны влиять на возврат функции get_object_value. наверное должен помочь SELECT ... FOR { UPDATE | SHARE }
...
Рейтинг: 0 / 0
21.04.2008, 11:38
    #35267937
Dan Black
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Как организовать хранение и извление исторических данных?
LeXa NalBatнаверное должен помочь SELECT ... FOR { UPDATE | SHARE }Чтение не должно блокировать другие транзакции, изменение значения объекта в параллельных транзакциях не должно блокировать чтение в текущей транзакции
...
Рейтинг: 0 / 0
21.04.2008, 12:23
    #35268093
LeXa NalBat
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Как организовать хранение и извление исторических данных?
в самом начале транзакции узнать минимальный из xid-ов работающих сейчас транзакций, запомнить в переменную. в get_object_value делать where xmin<$OTHER_RUNNING_MIN_XID or xmin=txid_current(). при этом не будут учитываться изменения не только других работавших на момент старта транзакций, но и транзакций, ктоторые уже закончились, но начались позже какой-то из работавших на момент старта.
...
Рейтинг: 0 / 0
21.04.2008, 12:30
    #35268116
4321
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Как организовать хранение и извление исторических данных?
Dan Black
Ниже описывается прототип схемы хранения истории изменения объекта и проблема, которая скоро съест мой мозг
Хочется услышать мнения умных людей по поводу структуры хранения данных (таблиц и полей), и запросов к этим структурам. А так же варианты реализации задачи :)

...
Доп. условие задачи: Если внутри нашей транзакции у объекта изменилось значение, то при следующем вызове get_object_value должно???? возвратиться новое измененное значение. Изменения значения объекта в параллельных транзакциях не должны влиять на возврат функции get_object_value.

Пример.
Код: plaintext
1.
2.
3.
4.
begin;
get_value_object( 100 ); -- возвращяет 'old_value'
set_object_value( 100 , 'new_value');
get_object_value( 100 ); -- возращает 'new_value'
commit;

внутри функции get_object_value делается простой запрос (к примеру)
Код: plaintext
SELECT * FROM object_history WHERE object_id =  100  AND ttime <= now() ORDER BY time DESC, ctime  DESC

внутри функции set_object_value так же делается запрос (к примеру)
Код: plaintext
INSERT INTO object_history(object_id, ttime, value) VALUES( 100 , now(), 'new_value');

Этих двух запросов хватает почти для всех случаев.
Вот один из случаев, для которых запросы не срабатывают
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
x: sql-код -- х - номер транзакции

 1 :begin;
 1 :set_object_value( 100 , 'new_value');
      2 :begin;
      2 :get_object_value( 100 ); -- возвращает old_value
 1 :commit;
      2 :get_object_value( 100 ); -- возвращает new_value
      2 :commit;
Есть огромное желание, чтобы при втором вызове get_object_value возвращалось то же значение, что и при первом вызове или измененное значение, но измененное в той же транзакции.
Код: plaintext
1.
----------------------------
 Verba volent, scripta manent 
если действительно нужна исторя изменения величины, то "дополнительное условие задачи" этому просто противоречит.

ну допустим, что нужно нечто, таки похожее на историю изменения, но ведущее согласно доп условию.
Тогда, в истории, если верить предыдущим комментаторам, всегда можно найти данные, записанные _этой_ транзакцией (или их отсутствие). (как получить текущую транзакцию xmin - вопрос наверное не ко мне, (хотя я могу придумать). Т.е. в get_ вы сначала лезете в историю по номеру текущей транзакции, и только при их отсутствии - ищете другие. Где-то так. Нет?
...
Рейтинг: 0 / 0
21.04.2008, 12:34
    #35268135
Dan Black
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Как организовать хранение и извление исторических данных?
LeXa NalBatв самом начале транзакции узнать минимальный из xid-ов работающих сейчас транзакций, запомнить в переменную. в get_object_value делать where xmin<$OTHER_RUNNING_MIN_XID or xmin=txid_current(). при этом не будут учитываться изменения не только других работавших на момент старта транзакций, но и транзакций, ктоторые уже закончились, но начались позже какой-то из работавших на момент старта.Переход на 8.3 планируется только через полгода, поэтому использовать txid_current() и прочих подобных функций нет возможности.
+ хочется решить задачу структурно (с помощью особой организации данных) (вырожденным случаем является использование sql-81 (с) Vladimir Sitnikov )
...
Рейтинг: 0 / 0
21.04.2008, 12:43
    #35268156
Dan Black
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Как организовать хранение и извление исторических данных?
4321если действительно нужна исторя изменения величины, то "дополнительное условие задачи" этому просто противоречит.
ну допустим, что нужно нечто, таки похожее на историю изменения, но ведущее согласно доп условию.
Тогда, в истории, если верить предыдущим комментаторам, всегда можно найти данные, записанные _этой_ транзакцией (или их отсутствие). (как получить текущую транзакцию xmin - вопрос наверное не ко мне, (хотя я могу придумать). Т.е. в get_ вы сначала лезете в историю по номеру текущей транзакции, и только при их отсутствии - ищете другие. Где-то так. Нет?Где-то так.
Ищу данные записанные в этой транзакции, если не нахожу, то смотрю данные записанные транзакциями завершившимися до начала текущей транзакции. Всё это решается установкой уровня транзакции Serializable, но такой возможности нет в силу других требований. Поэтому и ломаю голову, можно ли с использованием Read Committed, решить вопрос видимости данных (через запросы и структуры данных) аналогично действию уровня Serializable.
...
Рейтинг: 0 / 0
21.04.2008, 13:04
    #35268228
4321
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Как организовать хранение и извление исторических данных?
Dan BlackГде-то так.
Ищу данные записанные в этой транзакции, если не нахожу, то смотрю данные записанные транзакциями завершившимися до начала текущей транзакции. Всё это решается установкой уровня транзакции Serializable, но такой возможности нет в силу других требований. Поэтому и ломаю голову, можно ли с использованием Read Committed, решить вопрос видимости данных (через запросы и структуры данных) аналогично действию уровня Serializable.вы наверное меня не поняли. Я рассказал, как получить искомое при Read Committed.
...
Рейтинг: 0 / 0
21.04.2008, 13:26
    #35268302
Dan Black
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Как организовать хранение и извление исторических данных?
4321вы наверное меня не поняли. Я рассказал, как получить искомое при Read Committed.Не понял :) объясните подробнее, пожалуйста
...
Рейтинг: 0 / 0
21.04.2008, 13:50
    #35268348
Ёш
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Как организовать хранение и извление исторических данных?
4321... при их отсутствии - ищете другие ...вот в этот момент, если как в примере транзакция один завершилась в середине транзакции два - в выборку в транзакции два попадёт изменённое в транзакции один значение (так как номер транзакции один (её дата начала) меньше номера транзакции два) - невыходит :)
...
Рейтинг: 0 / 0
21.04.2008, 15:05
    #35268652
4321
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Как организовать хранение и извление исторических данных?
Ах да, я кажется понял . Вы о повторном чтении после только чтения , а не записи. Признаю невнимательность. Ну так сделайте ф-ю get пишущей . (можно даже не туда). (Вам же надо зафиксировать сам факт чтения).
...
Рейтинг: 0 / 0
Форумы / PostgreSQL [игнор отключен] [закрыт для гостей] / Как организовать хранение и извление исторических данных? / 25 сообщений из 53, страница 1 из 3
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]