English version Russian version




Основной задачей OLEDB провайдера для InterBase, как и любой другой компоненты доступа к базам данных, является управление информацией выбранной из базы данных. Поскольку IBProvider позиционируется как движок общего назначения, в нем реализованы решения, предназначенные как для использования в тривиальных задачах, так и в задачах оперирующих большими объемами данных.

Эффективность обеспечивается за счет специализированных механизмов управления множеством строк. На текущий момент вы можете использовать:

  • Только на чтение, только однонаправленное движение
  • Только на чтение, произвольный доступ
  • Чтение-запись, произвольный доступ
  • Чтение-запись в режиме отложенных изменений, произвольный доступ

Наборы рядов только на чтение

Наборы не позволяющие модифицировать выбранные данные являются самым высокопроизводительным способом обработки информации.

Основные характеристики

  • Оптимизированный менеджер управления рядами.
  • Представление множества в виде массива, позволяет осуществлять эффективное скроллирование и обращение к рядам по абсолютному номеру.
  • Отслеживание и удержание в оперативной памяти наиболее часто используемых рядов.
  • Вытеснение наименее используемых рядов во временный файл.
  • 64-битный доступ к временному файлу.
  • Максимальное число рядов - 232.

Автоматическое создание и использование временного файла является одной из важнейших характеристик IBProvider. Устанавливая разные значения свойства " Memory Usage", вы можете самостоятельно регулировать порог включения механизма выгрузки данных во временный файл.

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

Почему не используется своп-файл и поддержка со стороны операционной системы

  1. Своп является системным ресурсом и разделяется всеми приложениями, работающими на компьютере.
  2. InterBase поддерживает 232 рядов, поэтому использование оперативной памяти с 32-битной адресацией просто физически не позволяет загружать и обрабатывать такие объемы.
  3. Наборы только на чтение осуществляют кэширование на уровне отдельного ряда, а не на уровне страницы оперативной памяти, как это делает своп
  4. Явное управление обладает большей предсказуемостью.

Обновляемые наборы рядов

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

  • Набор рядов с немедленным обновлением.
  • Набор рядов с поддержкой отложенных изменений.

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

Основные характеристики

  • Страничная организация локального хранилища. Для увеличения производительности не допускается смешивание разных данных на одной странице. Хранилище оперирует следующими типами данных:
    • Список рядов множества
    • Данные рядов множества
    • Данные массивов
    • Заголовки BLOB полей
    • Сегменты BLOB полей
    • Список отложенных изменений
  • Кэширование на уровне страниц
  • 64-битная адресация виртуального пространства. Позволяет хранить гораздо больше 232 рядов
  • 64-битный доступ к временному файлу
  • Внутреннее представление данных использует формат IB типов
  • Отказоустойчивость в случае сбоя многошаговых операций
  • Автоматический и управляемый режим сохранения изменений в базе данных
  • Отключенный набор рядов
  • Пул SQL запросов
  • Раздельные транзакции для чтения и записи
  • Специальные возможности

Страничная организация и кэширование

В процессе проектирования и тестирования производительности локального хранилища были опробованы различные схемы хранения данных. Однако после серии смелых экспериментов мы пришли к классической архитектуре хранения всех данных в рамках одного файла.

Представление множества в виде списка обеспечивает хорошую производительность операций вставки и удаления рядов. Однако очень плохо увязывается с операцией скроллирования и получения ряда по абсолютному индексу. В частности при использовании метода IRowsetScroll::GetApproximatePosition для получения абсолютного индекса ряда провайдер перебирает все элементы списка. В то же время IBProvider отслеживает общее число рядов в множестве, поэтому накладные расходы, на получение этой информации, отсутствуют.

Тем не менее, стандартные способы навигации по множеству с использованием последовательного доступа или закладок, обеспечивают хорошую производительность. Это достигается за счет того, что при навигации выбираются только заголовки рядов - сами данные хранятся отдельно.

Список отложенных изменений обеспечивает очередность фиксации изменений в базе данных. Другой положительной стороной ведения этого списка является возможность быстрого перечисления рядов ожидающих изменения.

Хранение информации в IB-формате

Идентичный способ хранения используется и в наборах только на чтение, но в данном случае такое представление дает следующие преимущества

  • Хранилище просто не пропустит новые пользовательские данные, которые не совместимы с типом колонки. В случае использования отложенных изменений, это позволяет сразу выявлять ошибки, а не откладывать их до сохранения изменений в базе данных
  • Формирование параметров запроса на обновление данных сводится к установке указателей XSQLVAR на данные, загруженные из локального хранилища. Это исключает промежуточные преобразования и копирование данных, и обеспечивает хорошую производительность операции сохранения изменений в базе данных.

Поскольку OLEDB типы охватывают гораздо более широкий спектр данных, чем поддерживается InterBase, основная нагрузка ложится на встроенный конвертор типов.

Отказоустойчивость

На первый взгляд, задача установки новых значений колонок не содержит в себе ни каких сложностей. Однако принимая во внимание

  • Отдельное хранение BLOB и массивов
  • Возможность сбоя записи изменений в локальное хранилище

возникает множество проблем, связанных с обеспечением атомарности установки данных нового ряда. В общих чертах проблема была решена следующим образом:

  • Провайдер выполняет все операции по проверке и установке данных и регистрирует все этапы в журнале отката.
  • В случае успешного прохождения всех этапов, провайдер очищает список отката и возвращает управление клиенту.
  • В случае ошибки провайдер возвращает её описание. Журнал отката будет обработан при любой последующей операции связанной с модификацией данных.

В целом такая схема и уровень отказоустойчивости был обеспечен с единственной целью - исключить утечку места в структурированном хранилище.

Автоматический и управляемый режимы записи изменений в базу данных

Для сохранения изменений в наборе рядов провайдер использует обычный интерфейс взаимодействия с сервером базы данных в виде параметризованных SQL запросов.

Автоматический режим используется для задач, в которых пользователь не имеет возможности настроить создаваемый набор рядов. Например, при использовании средства просмотра таблиц базы данных через IBProvider или при использовании провайдера в качестве связанного сервера MSSQL. Поскольку, в общем случае, не возможно автоматизировать процесс генерации SQL запросов для записи изменений в базу данных, поэтому в IBProvider реализована поддержка только простейших запросов вида "select * from table", где table - таблица с первичным ключом.

Для выполнения обновления множества в автоматическом режиме провайдер выполняет системные запросы для получения дополнительной информации относительно таблицы:

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

На основании этой информации провайдер в состоянии правильно сформировать необходимые SQL запросы.

Несмотря на замкнутость автоматического процесса, вы имеете возможность делать подсказки относительно генерации запросов. См. свойства набора рядов " auto_insert_field_rule" и " auto_update_field_rule". Манипулируя этими свойствами можно корректно обрабатывать наличие колонок со значениями по умолчанию (DEFAULT) и минимизировать сетевой трафик, передавая только новые данные ряда.

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

Управляемый режим. Для преодоления ограничений автоматического режима, IBProvider позволяет явно определить запросы, которые следует использовать для обратной записи изменений в базу данных. Для этого у набора рядов определены три свойства: " delete_sql", " insert_sql", " update_sql". Для привязки SQL запросов к структуре выбранного множества используются составные параметры:

parameter = <prefix> { <param_type> | <gen_param_type> . <gen_name> } . <column_id>

<prefix> = "named_param_prefix"

<param_type> = {NEW | OLD}

<gen_param_type> = {GEN | GEN_N | GEN_NI }

<gen_name> = имя генератора, используемого для получения уникального значения колонки

<column_id> = {<aliase_column_name> | <full_column_name>}

<aliase_column_name> = название колонки в результирующем множестве. Может отличаться от базового имени колонки.

<full_column_name> = <base_table_name>.<base_column_name>

<base_table_name> = базовое название таблицы, в которой находится колонка

<base_column_name> = базовое название колонки

NEW Параметр привязан к новым данным колонки
OLD Параметр привязан к старым данным колонки
GEN Получить уникальное значение колонки
GEN_N Получить уникальное значение колонки, если текущее значение IS NULL
GEN_NI Получить уникальное значение колонки, если колонка не была инициализирована

При определении имен генераторов, таблиц и колонок можно использовать квотированные имена.

Изменив значение свойства " named_param_prefix" (по умолчанию это двоеточие), можно изменить префикс параметров запроса.

Пример:

Исходный запрос
"select cust_no as c_id, customer as c_name, on_hold from customer"

insert_sql
"insert into customer (cust_no, customer, on_hold)
 values (:gen.cust_no_gen.c_id, :new.c_name, :new.on_hold)"

delete_sql
"delete from customer where cust_no=:old.customer.cust_no"

update_sql
"update customer
 set cust_no=:new.c_id, customer=:new.c_name, on_hold=:new.on_hold
 where cust_no=:old.c_id"

Для того чтобы с таблицей CUSTOMER (база данных empoyers.gdb) можно было нормально работать, нужно или удалить или переписать триггер SET_CUST_NO.

Обратите внимание, что при формировании параметров запроса провайдер использует данные в формате колонки, связанной с параметром. Поэтому если тип колонки будет отличаться от ожидаемого типа параметра, то сервер должен будет самостоятельно выполнить необходимые преобразования. Среди известных ограничений механизма конвертирования некоторых версий InterBase - неспособность выполнять преобразования между BLOB и строками. Указанное ограничение не распространяется на выполнение параметризированных запросов через команды, в которых обеспечивается передача данных в том формате, в каком их ожидает сервер.

Отключенный набор рядов

В случае если необходимо выбрать и модифицировать данные без обратной записи изменений в базу данных, можно присвоить свойству набора рядов " disconnected_rowset" true. Одним из целевых назначений этого режима является создание внешнего механизма обратной записи изменений с использованием команд. При создании такого механизма также целесообразно включить режим отложенных изменений ( См. свойство набора рядов IRowsetUpdate), что позволяет организовывать транзакционные изменения набора рядов.

Пул SQL запросов

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

  • Создания запроса
  • Подготовки запроса
  • Выполнения запроса

InterBase SQL сервер позволяет:

  • Переподготавливать запрос. Выигрыш в пропуске этапа создания запроса.
  • Повторно выполнять запрос. Максимальный выигрыш, которого можно добиться.

Наиболее большой эффект от использования пула проявляется в автоматическом режиме с сохранением только модифицированных данных. Например, есть таблица с колонками (A1, A2, A3), в которой за один раз вы меняете только одну колонку. В этом случае провайдер будет генерировать запросы вида:
   "UPDATE ... SET A1=? WHERE..."
   "UPDATE ... SET A2=? WHERE..."
   "UPDATE ... SET A3=? WHERE..."

Дополнительно к этим запросам добавляется два системных запроса для получения

  • Списка первичных ключей таблицы
  • Списка колонок таблицы, которые можно модифицировать

Оптимальный размер пула ( См. свойство набора рядов query_pool_size) для выполнения этой задачи равен трем.

  1. Повторно используемый запрос для:
    • Получения списка первичных ключей (выполняется один раз)
    • Получение списка обновляемых колонок (выполняется один раз)
    • "UPDATE ... SET A1=? WHERE ..."
  2. "UPDATE ... SET A2=? WHERE ..."
  3. "UPDATE ... SET A3=? WHERE ..."

В этом случае при дальнейшей модификации любой колонки A1...A3 есть шанс повторного выполнения уже подготовленного запроса. Поиск подходящего запроса осуществляется через сравнение текста SQL. Если идентичный запрос отсутствует в пуле, то провайдер переподготовит запрос с максимальным временем простоя.

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

В случае использования управляемого режима, оптимальным размером пула будет сумма SQL запросов, перечисленных в свойствах " delete_sql", " insert_sql", " update_sql", и числа генераторов указанных в параметрах GEN_XXX запросов " insert_sql".

Манипулируя свойством " query_pool_size" можно

  • использовать только один запрос для выполнения всех операций (значение 1)
  • запретить повторное использование запросов (значение 0)

Обратите внимание, что " query_pool_size" определяет максимальное число запросов. Сами запросы создаются по мере необходимости.

Раздельные транзакции для чтения и записи

Обновляемые наборы рядов поддерживает два способа записи изменений в базу данных - с использованием основной транзакции, в которой производится чтение данных, и с использованием отдельной короткой транзакции. Кроме того, вы можете указать "мутирующий" режим:

  • Если выборка производится в автоматической транзакции (которая не контролируется через методы CommitTrans, RollbackTrans) то будет использовать отдельная транзакция
  • Если пользователь явно запустил транзакцию, в которой производится выборка данных, то именно она будет использоваться для модификации данных.

Вид транзакций для модификации данных определяется свойством набора рядов " modify_trans_type".

Специальные возможности

По умолчанию, обновляемый набор рядов обладает стандартными настройками, которые полностью обеспечивают ожидаемое поведение. Однако спецификация OLEDB определяет специфические режимы, которые могут потребоваться для некоторого класса задач:

  • Видимость собственных вставок. Если установить свойство " Own Inserts Visible" в false, то набор рядов не будет видеть новые ряды. В режиме отложенных изменений это свойство игнорируется.
  • Видимость собственных изменений и удалений. Если установить свойство " Own Changes Visible" в false, то набор рядов не будет видеть собственные изменения и удаления рядов. В режиме отложенных изменений это свойство игнорируется.
  • Не уничтожать удаленные ряды. Установив свойство " Remove Deleted Rows" в false, можно оставлять удаленные ряды. Правда, из них уже нельзя будет выбирать данные.
  • Не изменять новые ряды. Установив свойство " Change Inserted Rows" в false вы запрещаете изменять новые ряды набора.
  • Возвращать ряды ожидающие вставку. Установив свойство " Return Pending Inserts" в false, можно запретить провайдеру выбирать новые ряды, добавленные в режиме отложенных изменений.
  • Выборочное разрешение операций модификации данных. За разрешение/запрещение операций вставки/удаления/изменения отвечают отдельные биты свойства " Updatability".
  • Открытие набора для добавления новых рядов. Установив свойство " Append-Only Rowset" в true, вы открываете пустое множество со структурой исходного запроса. Это удобно, если вы хотите просто добавить новые ряды в таблицу и вам не нужно загружать уже существующие. В принципе, такого же эффекта можно добиться просто добавляя новые ряды, не выполняя операций перемещения по набору.


Назад Вперед Сборка сайта № 3.0.0.1660