powered by simpleCommunicator - 2.0.60     © 2026 Programmizd 02
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Форумы / PostgreSQL [игнор отключен] [закрыт для гостей] / Как организовать хранение и извление исторических данных?
25 сообщений из 53, страница 1 из 3
Как организовать хранение и извление исторических данных?
    #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
Как организовать хранение и извление исторических данных?
    #35265645
Фотография Ёш
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Dan BlackЕсть огромное желание, чтобы при втором вызове get_object_value возвращалось то же значение, что и при первом вызове или измененное значение, но измененное в той же транзакции.имхо по хорошему, что бы другие транзакции не мешались - нужно соответственно усиливать изоляцию транзакций %) но тогда придётся менять логику клиента, что бы он был готов перезапускать транзакцию которая отменилась из-за нарушения изоляции.
...
Рейтинг: 0 / 0
Как организовать хранение и извление исторических данных?
    #35265652
Dan Black
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
ЁшКак и хочется избежать блокировок, оставшись на уровне изоляции Read Commited
...
Рейтинг: 0 / 0
Как организовать хранение и извление исторических данных?
    #35265680
Фотография Ёш
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
по идеи задача сводится к "выбрать из object_history последнюю запись с сортировкой по ctime у которой ttime = now() [текущая транзакция], если такой нет, выбрать просто последнюю запись с сортировкой по ctime" но остаётся проблема, если объект в текущей транзакции не изменялся, а в соседних - изменялся, тогда будет возвращён изменённый в соседней транзакции объект. можно запоминать состояние объекта на начало транзакции (но будет рейс кондишн между begin; и save_current_object_value(id) %) ), но имхо это не что иное как ручное эмулирование TRANSACTION ISOLATION LEVEL SERIALIZABLE =)


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

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


--
„Истина — это вовсе не то, что можно убедительно доказать, это то, что
делает всё проще и понятнее“ — Антуан де Сент-Экзюпери
...
Рейтинг: 0 / 0
Как организовать хранение и извление исторических данных?
    #35266018
Dan Black
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Ёшда, затупил, этож тоже самое что ttime %)
ps: с пятницей :)
С субботой
однако мозг почти съеден, а проблема осталась :(
...
Рейтинг: 0 / 0
Как организовать хранение и извление исторических данных?
    #35266725
SeniorAndre
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Если проблема не решается внутри системы, то на проблему надо посмотреть снаружи... :) Собственно: "А нафига?" Глядя на код у меня например возникает вопрос: "При такой структуре построения кода, зачем вообще второй вызов get_? Запиши в переменную..."
Код: plaintext
1.
2.
3.
4.
5.
begin
get_object_value( 100 ); -- 1
-- много кода
get_object_value( 100 ); -- 2
-- много кода
commit;
...
Рейтинг: 0 / 0
Как организовать хранение и извление исторических данных?
    #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
Как организовать хранение и извление исторических данных?
    #35266944
Vladimir Sitnikov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Dan Blackget_... может вызываться внутри другой хп.А чем это мешает записать в переменную? custom_variable_classes
...
Рейтинг: 0 / 0
Как организовать хранение и извление исторических данных?
    #35267027
Dan Black
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Vladimir Sitnikov Dan Blackget_... может вызываться внутри другой хп.А чем это мешает записать в переменную? custom_variable_classes объектов может быть 1 000 000 000...
+ даже если в переменную можно записать массив, доступ к его элементам будет осуществляться без индексов, что существенно замедлит работу хп
+ к тому же использование custom_variable_classes противоречит моей религии
...
Рейтинг: 0 / 0
Как организовать хранение и извление исторических данных?
    #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
Как организовать хранение и извление исторических данных?
    #35267749
Dan Black
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Vladimir SitnikovИ все они нужны одновременно? Ню ню. Сколько времени вы этот миллиард обрабатывать будете?Ладно, нужно будет только 1000 объектов. Это что-то изменит? ;)))
Vladimir SitnikovАх да, религия, когда всё должно соотвествовать стандартам ansi sql-81, где нельзя менять уровни изоляции и т.п.Религия позволяет менять уровень изоляции транзакций, но в данном случае есть строгое требование, которое нельзя обойти.

Vladimir SitnikovЕсли религия позволяет триггеры, то timetravel вам в рукиПри уровне изоляции Read Commited там возникает такая же проблема, что я описал в конце своего первого топика.
...
Рейтинг: 0 / 0
Как организовать хранение и извление исторических данных?
    #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
Как организовать хранение и извление исторических данных?
    #35267937
Dan Black
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
LeXa NalBatнаверное должен помочь SELECT ... FOR { UPDATE | SHARE }Чтение не должно блокировать другие транзакции, изменение значения объекта в параллельных транзакциях не должно блокировать чтение в текущей транзакции
...
Рейтинг: 0 / 0
Как организовать хранение и извление исторических данных?
    #35268093
LeXa NalBat
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
в самом начале транзакции узнать минимальный из xid-ов работающих сейчас транзакций, запомнить в переменную. в get_object_value делать where xmin<$OTHER_RUNNING_MIN_XID or xmin=txid_current(). при этом не будут учитываться изменения не только других работавших на момент старта транзакций, но и транзакций, ктоторые уже закончились, но начались позже какой-то из работавших на момент старта.
...
Рейтинг: 0 / 0
Как организовать хранение и извление исторических данных?
    #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
Как организовать хранение и извление исторических данных?
    #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
Как организовать хранение и извление исторических данных?
    #35268156
Dan Black
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
4321если действительно нужна исторя изменения величины, то "дополнительное условие задачи" этому просто противоречит.
ну допустим, что нужно нечто, таки похожее на историю изменения, но ведущее согласно доп условию.
Тогда, в истории, если верить предыдущим комментаторам, всегда можно найти данные, записанные _этой_ транзакцией (или их отсутствие). (как получить текущую транзакцию xmin - вопрос наверное не ко мне, (хотя я могу придумать). Т.е. в get_ вы сначала лезете в историю по номеру текущей транзакции, и только при их отсутствии - ищете другие. Где-то так. Нет?Где-то так.
Ищу данные записанные в этой транзакции, если не нахожу, то смотрю данные записанные транзакциями завершившимися до начала текущей транзакции. Всё это решается установкой уровня транзакции Serializable, но такой возможности нет в силу других требований. Поэтому и ломаю голову, можно ли с использованием Read Committed, решить вопрос видимости данных (через запросы и структуры данных) аналогично действию уровня Serializable.
...
Рейтинг: 0 / 0
Как организовать хранение и извление исторических данных?
    #35268228
4321
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Dan BlackГде-то так.
Ищу данные записанные в этой транзакции, если не нахожу, то смотрю данные записанные транзакциями завершившимися до начала текущей транзакции. Всё это решается установкой уровня транзакции Serializable, но такой возможности нет в силу других требований. Поэтому и ломаю голову, можно ли с использованием Read Committed, решить вопрос видимости данных (через запросы и структуры данных) аналогично действию уровня Serializable.вы наверное меня не поняли. Я рассказал, как получить искомое при Read Committed.
...
Рейтинг: 0 / 0
Как организовать хранение и извление исторических данных?
    #35268302
Dan Black
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
4321вы наверное меня не поняли. Я рассказал, как получить искомое при Read Committed.Не понял :) объясните подробнее, пожалуйста
...
Рейтинг: 0 / 0
Как организовать хранение и извление исторических данных?
    #35268348
Фотография Ёш
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
4321... при их отсутствии - ищете другие ...вот в этот момент, если как в примере транзакция один завершилась в середине транзакции два - в выборку в транзакции два попадёт изменённое в транзакции один значение (так как номер транзакции один (её дата начала) меньше номера транзакции два) - невыходит :)
...
Рейтинг: 0 / 0
Как организовать хранение и извление исторических данных?
    #35268652
4321
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Ах да, я кажется понял . Вы о повторном чтении после только чтения , а не записи. Признаю невнимательность. Ну так сделайте ф-ю get пишущей . (можно даже не туда). (Вам же надо зафиксировать сам факт чтения).
...
Рейтинг: 0 / 0
25 сообщений из 53, страница 1 из 3
Форумы / PostgreSQL [игнор отключен] [закрыт для гостей] / Как организовать хранение и извление исторических данных?
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


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