Перечень статей   Choose language


Распределенный и обтекающий запрос

SQL более гибок и удобен для распределенных запросов (сбор данных из нескольких баз данных и рассовывание их в несколько баз данных), чем фирменные программы; в т.ч. более удобен для репликации, чем фирменные программы. Но нет необходимого синтаксиса. Попытаемся устранить этот пробел.

При работе с несколькими базами данных, существует два чистых случая: выбор необходимой базы на основе ее идентификатора - назовем этот случай распределенным запросом (distributed query, dQ), и на основе значения индекса для записи - назовем этот случай обтекающим запросом (ambient query, aQ) [1]. Для того, чтобы в дальнейшем отличать упомянутые запросы от обычных, назовем последние одиночными (single query, sQ). Одноименные таблицы разных баз данных будем называть генеральной таблицей.

Распределенный запрос

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

В целях безопасности предлагаю концепцию полного недоверия одной базы данных другой:

И концепцию крайней простоты клиента:

Таким образом одна база данных не может породить и ввести sql-команду в другую базу данных ни прямо, ни косвенно (прося клиента перенаправить команду). А клиент не может сконструировать новую sql-команду на основе разбора (parse) введенной.

Первая база данных отправляет клиенту специальную команду, которая заставляют клиента сделать подстановку в последней отправленной первой базе данных sql-команде, запомненной в стеке клиента, и выслать результат преобразования второй базе данных. Специальные команды должны быть настолько ограниченными, чтобы не допускать порождения sql-команды, вредной для второй базы данных (подробности на с.135-161 отдельного pdf-документа).

В SQL не должна существовать возможность установить общество-по-умолчанию (дабы запрос по умолчанию отправлялся не в одну таблицу, а во все таблицы общества), т.к. автоматическая вставка (INSERT) во все базы данных еще имеет смысл, а визуализация (SELECT) одинаковых данных всех баз - никакого. Таким образом используемое общество устанавливается принудительно с помощью префикса перед именем схемы [3].

Сбор записей из баз данных общества может выполняться во всех базах одновременно (SELECT ... PARALLELLY, с.139), если мы хотим сравнить записи, содержащиеся в них, или не знаем, в какой из баз данных они расположены, но хотим получить ответ максимально быстро - или последовательно (SELECT ... SEQUENTIALLY, с.140, первая возвращенная запись останавливают перебор баз данных), если хотим уменьшить количество операций во всех компьютерах общества. SELECT без постфикса выполняется одновременно или последовательно в зависимости от состояния переключателя, например 'hobo': 'SET HOBO PARALLELLY', 'SET HOBO SEQUENTIALLY'.

Модификация данных (UPDATE, DELETE) всегда выполняется одновременно, а не последовательно, т.к. ни одну базу данных заранее нельзя исключить из обработки.

Обтекающий запрос

Декларация необходимости введения

Используемое во всех запросах общество-по-умолчанию может быть установлено директивой, например 'SET AUTOSOCIETY societyname', или установлено принудительно с помощью префикса перед именем схемы через восклицательный знак [3]. В любом случае запрос сначала отправляется в базу-данных-по-умолчанию, которая хранит определение общества, и которая дает указание клиенту сделать подстановку в последней SQL-команде, сохраненной в его стеке, и действовать в соответствии с результатом трансформации.

Только в базе-данных-по-умолчанию [4] мы должны указать, на основе каких полей какой генеральной таблицы какого общества должно происходить разбрасывание записей. Таким образом помимо обычных индексов для поиска записей, в базе-данных-по-умолчанию нужны отдельного рода индексы для поиска других баз данных - будем называть их глоссариями и создавать их командой 'CREATE GLOSSARY glossaryname ON tablename (fieldname) FOR societyname'.

Совокупность индексов одного и того же поля одноименных таблиц разных баз данных (включая базу-данных-по-умолчанию) одного общества (независимо от того, имеют индексы одинаковое название или нет) будем называть генеральным индексом [5]. Совокупность уникальных индексов - уникальным генеральным индексом. Глоссарий и уникальный генеральный индекс обеспечивают unique constraint для генеральной таблицы.

Поскольку глоссарий и генеральный индекс индексируют одно и тоже поле, предлагаю в глоссарии хранить не сами проиндексированные значения, а только диапазоны значений, принадлежащих той или иной базе данных. Для любых двух баз эти диапазоны не пересекаются, иначе вместо aQ мы получим dQ [6].

На случай, если будут вставляться записи с близкими индексируемыми значениями (они попадут преимущественно в один интервал, и нагрузка ляжет преимущественно на одну базу данных), предлагаю индексировать не сами значения, а их хэш-суммы; глоссарии создавать командой 'create HASH GLOSSARY glossaryname on tablename (fieldname) for societyname', и хранить в них диапазоны хэш-сумм. Такой глоссарий равномерно разбросает записи с близкими индексируемыми значениями по всем базам общества. Использовать нехэширующие или хэширующие глоссарии - зависит от того, какие aQ-запросы преимущественно выполняются: вычисляющие корреляцию записей (тогда лучше использовать 'create GLOSSARY'), или не делающие этого (тогда лучше 'create HASH GLOSSARY'). Вычисление корреляции по близким индексируемым значениями, находящимся в разных базах, породит большой траффик.

Предложение по реализации

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

После добавления новой базы данных в общество ('APPEND nick TO societyname'), часть записей из тех, что ранее внесли бы в другую базу (INSERT), будут направлены в новую - иначе она останется незадействованной. Для сохранения равномерного распределения нагрузки нужно, чтобы 1/(M+1) часть потока записей, вставляемых ранее в каждую из M баз, была направлена в добавленную базу. А для этого адресующие в них диапазоны должны быть расщеплены на двое в пропорции M:1. Только INSERT будет выполняться по новому набору диапазонов - SELECT, UPDATE, DELETE будут выполняться и по старому, и по новому наборам. А значит в глоссарии нужно хранить все предыдущие наборы диапазонов и URLs [7], соответствующие им.

После изъятия базы данных из общества ('REFUSE nick FROM societyname'), новые записи, которые ранее внесли бы в эту базу, будут направлены в оставшиеся M-1 базы - иначе записи будут потеряны. Для сохранения равномерного распределения нагрузки нужно, чтобы в каждую из них была направлена 1/(M-1) часть этого потока. А для этого адресовавший в изъятую базу диапазон должны быть расщеплен на M-1 равных частей. Чтобы SELECT, UPDATE, DELETE выполнялись в наименьшем количестве баз, за все время существования диапазон должен указывать на наименьшее их количество, поэтому предлагаю при повторном добавлении изъятой базы наделять ее в первую очередь диапазонами, ранее принадлежавшими ей, а не участками диапазонов других баз. И считать базы идентичными, если у них совпадает URL [7]. А значит в глоссарии опять же нужно хранить все предыдущие наборы диапазонов и URLs, соответствующие им.

Примечания

[1] Надо заметить, что вставка (INSERT) записи в случайную базу общества, или в каждую следующую базу для каждой следующей записи, хотя и указывает базу на основе значения, вычисленного для записи, по существу является случаем dQ: это хорошо видно на примере остальных трех операторов SELECT, UPDATE, DELETE, которые не знают, где расположены записи, подпадающие под их WHERE

[2] Таким образом одно sql-выражение с обществом означает множество sql-выражений с прозвищами. Кроме того, чтобы прозвища, несколько обществ или несколько упоминаний одного общества никогда не указали на одну и ту же базу данных одновременно, поместим перед ними символ '%';

insert into        %c:tab select * from   %s:tab;
insert into  %default:tab select * from   %c:tab;
а чтобы несколько упоминаний одного общества наоборот всегда синхронно указыли на одну и ту же базу данных, поместим любое слово (одинаковое) и символ '%' перед этими упоминаниями

insert into m%s:tab
  select * from %s:tab
  -- 'm%s' and '%s' are different societies
  where @fld > (select @f from m%s:tab);
  -- 'into m%s:tab' and 'from m%s:tab' are the same database

[3] Для dQ и aQ должны использоваться разные разделители, например двоеточие для первого и восклицательный знак для второго: 'societyname:tablename' и 'societyname!tablename'

[4] И ни в какой другой, чтобы избежать даже возможной обратной пересылки данных

[5] Его легко создать dQ-запросом 'CREATE INDEX societyname:indexname ON tablename (fieldname)'

[6] В этом смысле глоссарии всегда 'unique', не требуется писать 'create UNIQUE glossary'

[7] Таким образом глоссарий представляет собой множество записей некой системной таблицы с тремя колонками: нижняя и верхняя границы диапазона, URL. Не лишним будет для ее поля 'URL' иметь системный (т.е. недоступный для команд 'alter index, drop index') индекс. Кроме того, если внешний мир каждую схему базы видит как отдельную базу данных (с.176-178), то база-схема идентифицируется уже не одним URL, а тройкой параметров - URL, OS user, schema. И как следствие базы-схемы являются совпадающими только при совпадении всех трех параметров, и все три они должны храниться в глоссарии, и упомянутый системный индекс должен строиться по всем трем (итого вместе с именем пользователя в ОС и путем к вложенной схеме, упомянутая системная таблица будет иметь пять колонок)



Тюрин Дмитрий, dmitryturin@yandex.ru



Перечень статей   Choose language


Используются технологии uCoz