Представления (VIEW)

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

Представления обычно используются для:

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

Важно

В настоящий момент представления в YDB невозможно использовать для предоставления частичного доступа к таблице другим пользователям. Запрос, хранимый в представлении, выполняется от имени пользователя представления, поэтому для чтения из представления пользователю также нужны и права на чтение из подлежащих таблиц. Больше информации по этой теме можно найти на странице CREATE VIEW в описании опции security_invoker.

Инвалидация представления

Если удалить одну из таблиц, на которые ссылается представление, то оно станет невалидным. Запросы на чтения из него будут возвращать ошибку точно так же как если бы вы попытались выполнить записанный в представление запрос непосредственно. Зависимости между представлениями и таблицами на данный момент не отслеживаются. Чтобы вернуть такое представление обратно в валидное состояние, достаточно создать (или восстановить) таблицу с тем же именем, которое было у той таблицы, на которое ссылается представление в своём запросе. В этом смысле чтение из представления аналогично чтению из подзапроса. Единственное различие между чтением из подзапроса и чтением из представления заключается в том, что контекст исполнения запроса, сохранённого в представлении, отличается от контекста объемлющего запроса, читающего из него.

Время выполнения запросов, читающих из представления

Выполнение запроса можно разбить на 2 стадии:

  1. компиляция,
  2. исполнение скомпилированного кода.

При компиляции запросов с участием представлений все упоминания представлений разворачиваются в текст запросов, хранящихся в них. Это означает, что время исполнения скомпилированного кода (стадия №2 выполнения запроса) для запросов с участием представлений и прямых запросов должно всегда совпадать, потому что исполняемый код в обоих случаях генерируется одинаковый.

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

SELECT * FROM a_view;

проходит те же преобразования, что и аналогичный запрос, читающий из подзапроса:

SELECT * FROM (SELECT * FROM underlying_table);

но с дополнительным расходом времени за загрузку содержимого a_view из базы данных и подстановку запроса на место представления.

Важно отметить, что результаты компиляции в YDB кэшируются и поэтому при исполнении одного и того же запроса несколько раз подряд в рамках одной сессии:

-- выполняется несколько раз в течение одной сессии
SELECT * FROM hot_view;

вы не заметите разницы в общем времени выполнения запроса, записанного с участием представлений, по сравнению с аналогичным запросом, записанным непосредственно.

Транзакционность переопределения представления

Важно

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

Кэш результатов компиляции

YDB кэширует результаты компиляции запросов на стороне сервера для большей скорости их выполнения. Для лёгких запросов вида SELECT 1; компиляция может занимать значительное время по сравнению со временем выполнения запроса.

Запись в кэше ищется по ключу, основа которого - текст запроса (но в нём есть и другие поля, например, ID пользователя).

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

Описание проблемы

Представьте себе следующую ситуацию.

Алёна постоянно выполняет следующий запрос:

-- сессия Алёны
SELECT * FROM some_view_which_is_going_to_be_redefined;

В то же время Борис переопределяет представление, из которого читает Алёна, следующими командами:

-- сессия Бориса
DROP VIEW some_view_which_is_going_to_be_redefined;
CREATE VIEW some_view_which_is_going_to_be_redefined ...;

Текст запроса Алёны не меняется, а значит его компиляция будет выполнена один раз, а все последующие запросы будут брать готовый результат компиляции из кэша. Борис переопределяет представление, поменяв запрос хранящийся в нём. Теоретически, запись с результатами компиляции запроса Алёны должна быть удалена из кэша тут же, в одной транзакции с изменениями Бориса. Но на практике это не так. Запрос Алёны будет перекомпилирован после того, как информация об изменившемся определении представления дойдёт до тех нод, на которых закэширован результат компиляции запроса Алёны. Это означает, что в течении некоторого времени, запросы Алёны могут возвращать результат, не соответствующий актуальному определению представления. Подобное поведение YDB нежелательно и будет исправлено в следующих релизах.

См. также

Предыдущая
Следующая