Как эффективно обновить результаты дорогого запроса? - PullRequest
2 голосов
/ 23 декабря 2010

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

Например, мой первый запрос выглядит следующим образом:

выберите * из 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

Ответы [ 3 ]

0 голосов
/ 03 декабря 2011

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

Вы можете использовать отдельную облегченную таблицу, чтобы отслеживать номера ревизий и присоединяться к этой таблице при чтении. Это позволяет вам управлять номерами ревизий для группы строк (если вы этого хотите), связывая N с 1 из большой таблицы в таблицу ревизий.

Процесс написания:

Инициализировать номер глобальной ревизии X = 1.

За каждое обновление:

  1. Инкремент X.
  2. Начать транзакцию (вероятно, неявную).
  3. Обновите все строки, которые вы хотите обновить, и обновите их номера версий до X.
  4. Commit.

Процесс чтения:

Инициализировать последний известный номер редакции Y = 0. Выбрать новые данные следующим образом:

За каждое обновление:

  1. SELECT * FROM some_really_big_table WHERE редакция> Y
  2. Обновите Y, указав самый большой из известных номеров ревизий в вашем новом наборе.
0 голосов
/ 03 декабря 2011

Вы можете использовать псевдостолбец ora_rowscn для отслеживания изменений

0 голосов
/ 03 января 2011

Хотите знать, почему no1 ответил до сих пор, но в любом случае, возможно, это вариант для вас: Вместо обновления всех ваших записей создайте вторичную таблицу под названием «update_groups», затем свяжите ваши записи с этой группой (группами) и установите временную метку для группы вместо отдельных записей. Вы также можете использовать логическое значение вместо временной метки IsUpdated или чего-то еще. Но это работает только в том случае, если записи можно поместить в группы, если у вас есть большой толстый список записей, тогда вы можете просто создать таблицу с 1 или более записями под названием «UpdateList» и просто обновить эту таблицу с помощью идентификаторов изменил записи. Вы можете просто разделить их запятыми и точно знать, что делать дальше. Это очень легкий. Единственное, о чем вам следует знать, это как обновить эту запись. Я думаю, что всякий раз, когда обновляется запись, вы можете просто добавить идентификатор к текущему, затем, когда вы обновляете свой пользовательский интерфейс, вы можете очистить все значения и начать заполнять их снова. Вот в принципе, как это работает. Удачи!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...