У меня есть приложение, которое выполняет дорогой запрос для заполнения пользовательского интерфейса. Через определенные промежутки времени пользовательский интерфейс должен обновлять и отображать новые данные, соответствующие требованиям исходного запроса. Я хотел бы, чтобы второй запрос был максимально эффективным и возвращал только те новые данные, которые соответствуют требованиям. Как я могу это сделать?
Например, мой первый запрос выглядит следующим образом:
выберите * из some_really_big_table
Самая очевидная мысль - добавить новую квалификацию в исходный запрос. Предположим, у меня есть столбец с именем update_timestamp, в котором хранится отметка времени, когда строка была создана или последний раз обновлена. Чтобы упростить этот вопрос, давайте предположим, что существует только один сервер базы данных, и эта временная метка заполняется из времени операционной системы сервера базы данных, когда выполняется оператор вставки / обновления. Также предположим, что часы имеют достаточное разрешение, чтобы никогда не было одинаковых двух временных меток. Добавьте еще одно предположение, что значение часов никогда не уменьшится. Я знаю, что это несколько нереальные предположения.
С помощью этого нового столбца (и индекса для этого столбца) я выполняю обновление по следующему запросу (где X - наибольшее значение update_timestamp, возвращаемое для любого результата в исходном запросе):
select * from some_really_big_table, где update_timestamp> X
У меня все еще есть проблема. Поскольку update_timestamp был назначен при выполнении оператора, возможно, что две транзакции, записывающие записи одновременно, будут выполнять свои операторы вставки / обновления в одном порядке, но фиксировать в противоположном порядке. Таким образом, строка с большим значением update_timestamp будет существовать до строки с меньшим значением update_timestamp. Если запрос обновления приложения выполняется между этими двумя коммитами, он никогда не увидит данные из второго коммита! Это будет пропущено в текущем обновлении, и следующее обновление также не поднимет его. Я не могу принять эту возможность.
Сериализованные записи
Чтобы решить эту проблему, мне кажется, мне нужно сериализовать записи в some_really_big_table путем синхронизации на некоторой блокировке перед выбором update_timestamp. Затем снимите блокировку, как только вставка / обновление будет зафиксировано. При таком подходе я могу гарантировать, что запись никогда не будет записана не по порядку относительно update_timestamp.
Если записи в базу данных были редкими или уже однопоточными, возможно, меня это не беспокоило бы, но давайте предположим, что записи в some_really_big_table происходят часто и параллельно.
Есть ли какое-либо решение, которое не требует от меня сериализации записей?
Альтернативный раствор
Я полагаю, что вместо этого мог бы использовать следующий запрос (где Y - некоторый коэффициент выдумки для того, как далеко друг от друга я думаю, что значения update_timestamp могут быть не в порядке):
select * from some_really_big_table, где update_timestamp> (X - Y)
Я мог бы сделать это, но я не чувствую себя комфортно, рискуя ошибиться с Y, и мое приложение молча пропустило данные из-за этого. Возможно, я бы сделал Y довольно большим, чтобы попытаться сделать это крайне маловероятным, но из-за этого пострадала бы производительность моего приложения, и я все еще мог ошибиться. Например, кто-то может изменить часы на сервере базы данных, и мой коэффициент помадки теперь не работает.
Я мог бы использовать логические часы вместо часов операционной системы, но мне все еще нужно сериализоваться перед выбором следующего значения часов, или я снова столкнулся с той же проблемой, связанной с возможностью выбора значений логических часов и фиксацией базы данных, чтобы быть вне последовательности , Я мог бы использовать фактор выдумки и на логических часах, но все еще есть место, чтобы что-то пошло не так.
Серийные записи с разделами
Я также считаюкрасный разделение данных так, что мне не нужно синхронизировать все записи в some_really_big_table. Скажем, например, у меня есть столбец some_attribute, который является естественным способом разбиения some_really_big_table таким образом, что одновременные записи для данного значения some_attribute относительно редки. В этом случае я гарантирую только порядок вставки / обновления update_timestamp внутри раздела. Тогда мой запрос на обновление становится следующим:
select * from some_really_big_table where
(some_attribute = A1 and update_timestamp > X1)
or (some_attribute = A2 and update_timestamp > X2)
or (some_attribute = A3 and update_timestamp > X3)
...
...
X1, X2, X3 ... это самые высокие значения update_timestamp, которые я видел в последний раз, когда я запрашивал. A1, A2, A3… это значения some_attribute, о которых заботится мой запрос. На самом деле, мой исходный запрос содержал бы эту квалификацию some_attribute, но я исключил ее из этого обсуждения, чтобы вначале все было проще. Так что на самом деле самый первый запрос был бы такой:
select * from some_really_big_table, где some_attribute в (A1, A2, A3, ...)
Кажется, эта методика секционированной сериализации - лучшее, что я могу придумать. Я знаю, что другие должны были решить эту проблему раньше. Кажется, что это может появиться при реализации кэшей для определенных типов данных. Я немного погуглил, но мне сложно выбрать условия поиска, которые приведут меня к обсуждению этой проблемы.
У кого-нибудь есть опыт с подобной проблемой, которой он может поделиться?
Пока я проводил небольшое исследование по этой теме, я наткнулся на функциональность Oracle Flashback Query [1]. Казалось, что может иметь смысл для этого варианта использования с запросом, основанным на SCN, но я не могу рассчитывать на доступ к этой функции, поскольку моя система не обязательно будет работать на Oracle.
[1] http://download.oracle.com/docs/cd/B19306_01/appdev.102/b14251/adfns_flashback.htm