Изображение квадрата Дюрера

ООО АВТОМАТИКА плюс

Rambler's Top100

Рейтинг@Mail.ru

Почему мне нравится Firebird

Версия от 17.03.2012, 23.03.2012 (исправление замечаний)

Обсуждение статьи смотрите на форуме SQL.RU

Содержание

Введение

Я сторонник СУБД Firebird. Знаю данную СУБД с 2002 г. Реализовал на ней множество проектов. Данная СУБД не разу меня не подводила. В России на Firebird'e работают тысячи копий разработанных с участием автора приложений (в том числе ПТК АЗСКМАЗС-ОФИС и др.). Базы не очень большие (как правило, не более 1 ГБ), однако в такой базе запросто умещается несколько миллионов записей (без BLOB'ов). Смею заверить, что за последние 5 лет не было ни одного сбоя по вине Firebird. (это при том, что по всей России работают тысячи операторов на АЗС). Неполадки в железе и операционной системе случаются несоизмеримо чаще, чем сбои в СУБД Firebird. Разумеется, бывали случаи, когда из-за аппаратного сбоя база данных оказывалась поврежденной, однако базы всегда легко восстанавливались (исключение - поломка HDD, в этом случае приходилось восстанавливать БД из резервной копии).

Сегодня в России несомненным лидером в области баз данных является СУБД SQLServer от компании Microsoft. При этом очень многие сокращают это название до "SQL": говорят "SQL" а подразумевают SQLServer. Зачастую руководство, директора, менеджеры, маркетологи не подозревают о существовании других СУБД, вследствие чего покупают SQLServer, не рассматривая альтернативные варианты. Помощь им в этом оказывают IT-специалисты, ведь SQLServer - это действительно удобная и мощная СУБД, способная удовлетворить практически любые потребности бизнеса. Популярность на Российском рынке возникла не на ровном месте. Компания Microsoft в рекламной области поработала на славу: SQLServer изучают в ВУЗах, публикуется огромное количество литературы на русском языке, активно работают специалисты по продвижению данной продукции на Российский рынок.

SQLServer - это платный продукт, но у Российского бизнеса деньги на него находятся. Имеются также бесплатные Express-редакции, ограничивающие многие возможности СУБД, но дающие законное право IT-специалисту как следует её освоить, не вкладывая дополнительных денежных средств. Ограничения Express-редакции действуют на размер базы данных, максимальный объем используемой памяти, количество процессоров и др. На мой взгляд, достаточно и одного ограничения - максимальный объем данных, хранимых в БД - 4 ГБ. Этого обычно достаточно для мелкого бизнеса, иногда - для среднего, но для крупного зачастую не хватает. Но крупный бизнес вполне может позволить себе подобные денежные траты.

Несмотря на абсолютное лидерство SQLServer'а (по крайней мере, на Российском рынке) сотни тысяч (по самым скромным подсчетам) разработчиков используют Firebird. В мире работают десятки, сотни миллионов копий программ, использующих базы данных Firebird. Многие из ценителей SQLServer'а используют Firebird. Вы скажете, что это абсурд? Вовсе нет. Это говорит о том, что не только у SQLServer'а перед Firebird'ом есть преимущества, но и наоборот. У Firebird'а также имеется немало преимуществ перед SQLServer'ом.

В крупном бизнесе специалист по SQLServer'у имеет образ делового, высокооплачиваемого сотрудника, умеющего ценить свое время. SQLServer для него - незаменимый помощник. Почти любая задача решается очень быстро за счет огромного количества возможностей, предоставляемых SQLServer'ом. Самый крупный бизнес обитает на уровне руководства и бухгалтерии. Именно там крутятся основные деньги и начисляются большие зарплаты, в том числе IT-специалистам. Руководству и бухгалтерии постоянно требуются какие-то отчеты, статистика, аналитика, причем как можно быстрее. И тут SQLServer - незаменимое средство. Здесь и мощное, надежное хранилище огромных объемов данных, непревзойденная бизнес-аналитика (OLAP), феноменальное время построения любых отчетов и многое другое. У руководства денег хватает также и на мощное железо: терабайтные RAID-массивы, десятки гигабайтов ОЗУ, большое число процессоров, системы кондиционирования и много еще чего вкусного, что позволяет SQLServer'у чувствовать себя великолепно.

Так зачем же нужен Firebird, еже ли ситуация с SQLServer'ом настольно безоблачна? А давайте подумаем, откуда у руководства и бухгалтерии вообще берутся все эти бесконечные данные, которые SQLServer с таким рвением постоянно перемолачивает? Разумеется, их кто-то вводит. Но не бухгалтерия, ни тем более директор. Первичные данные вводят пользователи, операторы, кассиры, автоматизированные системы и т.п. Пример: пусть у некоторой крупной огранизации по всей стране имеются 100 гипермаркетов (наподобие московского Ашана). В каждом гипермаркете непрерывно трудятся 100 кассиров, каждый из которых в среднем пробивает по 2000 товаров за рабочий день. Таким образом, каждый гипермаркет в среднем за день продает 200000 товаров, а вся сеть - 20000000 (20 млн) товаров в сутки (цифры оцень приблизительные, но они вполне могут соответствовать действительности). Каждый день, после закрытия смены в гипермаркете, информация о произведенных продажах передается в центральный офис организации, там она попадает в общую огромную базу данных и претерпевает обработку, необходимую для обеспечения более эффективной бизнес-аналитики.

Итак, первичные данные вводятся операторами. При этом используется, как правило, специализированное программное обеспечение. Опять же к теме гипермаркетов: в России огромное количество организаций, осуществляющих разработку систем автоматизации (СА) магазинов, супермаркетов, гипермаркетов. Разработчики этих систем постоянно находятся в состоянии конкурентной войны, поэтому стремятся максимально удешевить свою продукцию и занять как можно больше позиций на рынке. Дешевая продукция еще не означает, что она качественная. Наоборот, если Вам продают что-то подешевле, значит нужно быть осторожным, под красивой оберткой может скрываться что угодно. Слишком высокая цена также не является признаком высокого качества, но часто рядом с высокими ценами обнаруживаются различные откатные схемы. В связи с высокой конкуренцией производителям СА не выгодно использовать дорогостоящий SQLServer, т.к. слишком мало желающих платить такие деньги в то время, как у конкурентов можно то же самое приобрести гораздо дешевле. Express-редакции использовать слишком рискованно, т.к. объем в 4 ГБ может быть достигнут очень быстро. Но дело не только в цене SQLServer'а. Не для кого не секрет, что данная СУБД любит мощное современное железо, а на стареньком показывает весьма средние результаты. Разработчики стремятся к тому, чтобы их СА могла работать на любом железе, даже самом слабом. Некоторые до сих пор разрабатывают СА под DOS и имеют успех на рынке.

Давайте представим, что SQLServer - это бесплатный продукт. Стали бы в таком случае разработчики СА использовать его в основе своей продукции? На это вопрос нельзя ответить однозначно. Если разработчик кроме SQLServer'а ничего не знает, то скорее всего - да. Если же разработчик знаком с другой СУБД (например, Firebird), то их шансы уравниваются.

SQLServer очень часто используют для ввода первичных данных: в магазинах, на АЗС, в терминалах и т.д. Однако существует хорошо известная проблема: многие программы операторского уровня (OLTP), основанные на SQLServer'е, часто подвисают на довольно длительное время. Я это могу подтвердить, поскольку многие из наших клиентов знают о мучениях операторов с SQLServer'ом не понаслышке. В частности, много раз упоминали АСУ АЗС, зависания в которой происходят очень часто, причем связывают эти зависания именно с SQLServer'ом. У нас также используется приложение, основанное на SQLServer'е и маркетологи, работающие с ним, часто жалуются на длительные подвисания (по их словам, в ходе подвисаний они успевают выпить чашку кофе). Но проблема не только в зависаниях. SQLServer'у требуется мощное железо. У нас есть компьютер с 1 ГБ ОЗУ и БД 800 МБ. Размер очень скромный для базы данных. Там же установлена программа "SQL Server Management Studio 2005" и имеется возможность "поиграться" с базой данных. Возможно я не буду объективным, т.к. привык работать с Firebird и IBExpert, где любая команда отрабатывает моментально на любом железе. Но что бы я не делал в "SQL Server Management Studio", любая моя команда в первый раз выполняется очень долго. Например, я хочу посмотреть данные, хранящиеся в той или иной таблице, а SQLServer сначала берет таймаут секунд на пять, и только после этого открывает окно с данными. Я запускаю диспетчер задач и вижу, что SQLServer занял всю доступную оперативную память; судя по индикатору жесткого диска выполняется непрерывная работа с файлом подкачки. Конечно Вы можете в любом момент заявить, что мне это все показалось, однако я привык верить самому себе, особенно если видел это своими глазами.

Из вышесказанного следует две вещи: 1) SQLServer существенно "тормозит" на слабых компьютерах; 2) многие программы (разумеется, не все) уровня OLTP, основанные на  SQLServer'е, часто подвисают, что мешает операторам работать.

Лично у меня складывается ощущение, что SQLServer развивается в основном в направлении OLAP (бизнес-аналитики), а внимание операторскому уровню (OLTP) уделяется по остаточному принципу.

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

Но на уровне OLAP никаких тормозов нет! SQLServer'у здесь уделяется самое пристальное внимание. Работают высокооплачиваемые IT-специалисты. Еще бы, за такие деньги!

В соответствие с названием данной статьи я вынужден при дальнейшем повествовании постепенно отыскивать недостатки СУБД SQLServer, сказывающиеся на работе операторского уровня и указывать на преимущества СУБД Firebird.

SQLServer: Чрезмерное потребление памяти

По моим наблюдениям, SQLServer стремится занять всю доступную оперативную память и держит ее до тех пор, пока ее не затребуют другие приложения. Я считаю, что для OLTP-уровня данное поведение SQLServer'а является существенным недостатком, напрямую связанным с кластерными индексами (в некоторых источниках - "кластеризованные" индексы) , соответствующими первичным ключам большинства таблиц (по умолчанию). SQLServer'у попросту некуда деваться и для сохранения высокой производительности он вынужден загружать страницы с данными в ОЗУ. Если термин "кластерный индекс" для Вас не ясен, предлагаю свое пояснение:

Сперва давайте разберемся, что такое индексы. Вы когда-нибудь были в крупной библиотеке? А ведь там сотни тысяч, миллионы книг! А как библиотекарше удается за 5 минут отыскать нужную книгу среди миллионов, например "Война и Мир"? Очень просто (на примере условного алфавитного каталога): она подходит к шкафу к надписью "В", открывает ящик с надписью "Во" и отыскивает секцию "Вой" и т.д., пока не отыщет карточку книги с указанием ее точного месторасположения. Индексы в базах данных - это программная эмуляция работы библиотеки. Они, как правило, организованы в виде "дерева двоичного поиска". Алгоритм поиска информации в этом дереве тот же самый (представьте, что шкаф в библиотеке - это корень дерева, ящики - это ветви, далее от них идут другие ветви - секции, а листья дерева - это карточки с описанием месторасположения книги).
А теперь представьте, что вы не обнаружили карточку книги (их не завели), а вместо этого указаны номер стеллажа и номер полки, на которой предположительно должна быть расположена искомая книга! На этой же полке еще 1000 книг помимо вашей, и Вам необходимо перебрать все книги на полке до тех пор, пока не найдете нужную (и не факт, что она там есть). К счастью, все книги на полке выставлены в алфавитном порядке и Вам потребуется совсем немного времени на поиски. Это и есть принцип кластерного индекса. Вместо того, чтобы заводить отдельную карточку на каждую книгу (книг-миллионы, поэтому потребуется миллионы карточек), проще хранить информацию с указанием номера стеллажа и полки. В любом случае вы подойдете к этой полке и отыщете нужную книгу (или не отыщите, если такой книги нет).
Разумеется, в базах данных нет понятия "полка", зато есть понятие "страница". Каждая страница - это непрерывный кусок памяти фиксированного размера (например, 8 КБ). В одной странице (полке) могут быть расположены 1 или несколько записей (книг), причем количество записей зависит от их длины.

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

Следует отметить, что использование кластеризованного индекса во многих случаях сокращает количество операций чтения диска, что ускоряет поиск записей (если поиск ведется по кластерному индеку).

Не для кого не секрет, что любая СУБД стремится держать в ОЗУ в первую очередь страницы индекса, а страницы с данными - по остаточному принципу. Это совершенно оправданно, поскольку индексы - это средство для повышения скорости поиска информации в БД. Любой SQL-запрос отрабатывает быстрее, если в выражении фильтрации используются индексированные поля (с минимальным количеством повторяющихся значений).

SQLServer не является исключением из этого правила. Он также для ускорения запросов стремится держать индексы в ОЗУ. Но проблема в том, что страницы с данными также входят в состав кластеризованного индекса, поэтому SQLServer стремится держать в памяти также и страницы с данными. Иными словами, SQLServer стремится загрузить полностью всю БД в ОЗУ.

По мнению автора (я могу ошибаться), отсюда вытекает другой ранее отмеченный недостаток: в первый раз SQL-запросы выполняются очень долго. Дополнительное время уходит в том числе на загрузку страниц с данными в ОЗУ.

Следует еще раз отметить, что вследствии потребления большого объема ОЗУ SQLServer'ом, другие приложения, установленные на этом же компьютере, могут испытывать хроническую нехватку физической памяти, что приводит к постоянной работе с файлом подкачки и к общей деградации системы. SQLServer стремится занять всю доступную память в соответствии с настройками по-умолчанию, которые устанавливаются сразу после инсталляции SQLServer'а. По видимому, Microsoft считает, что СУБД SQLServer должна быть установлена на выделенном сервере без каких-либо дополнительных программ.  (с помощью команды sp_configure можно ограничить максимальный объем памяти, выделяемой SQLServer'у; при этом другие программы буду чувствовать себя лучше, но производительность SQLServer'а может снизиться).

С учетом отмеченных особенностей SQLServer'а можно вывести следующую рекомендацию: объем установленной на компьютере ОЗУ должен соответствовать размеру БД. Чем больше размер БД, чем больший объем ОЗУ необходим. Высокопроизводительный жесткий диск является также весьма желательным.

Firebird: Минимализм в потреблении памяти

В Firebird первичный ключ не является кластерным, поэтому ему не требуется столь большого объема ОЗУ. Объем памяти, который может занять Firebird  (для каждой БД) регулируется с помощью конфигурационного файла firebird.conf. Firebird, как правило, не мешает работе другим приложениям, запущенным на компьютере. При наличии большого объема памяти можно заставить Firebird загрузить в ОЗУ всю БД целиком, однако чаще всего в этом нет столь острой необходимости. Важно, чтобы ОЗУ хватало для размещения индексных страниц.

SQLServer: замедление операций вставки и обновления

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

Недостаток: при добавлении записи в конец таблицы (т.е. если выдерживается сортировка по первичному кластеризованному ключу) обеспечивается высокая производительность, однако если запись добавляется в середину таблицы или изменяется значение ключевого поля, то это может повлечь за собой значительную работу по реорганизации страниц с данными, т.е. может существенно замедлить работу СУБД. Лично я не проверял этот момент, но ряд авторов отмечают существенное ухудшение производительности.

В Firebird реорганизация страниц с данными не происходит (может произойти перестроение индекса, но это не приводит к значительным замедлениям).

Насколько я понимаю, разработчики баз данных SQLServer хорошо знают об указанной проблеме; по этой причине (в том числе из-за нее) очень часто встречаются БД, таблицы которых содержат первичный ключ, состоящий из единственного целочисленного поля IDENTITY (автоинкремент). (кстати, такой подход удобен "в принципе", не зависимо от типа индексов).

SQLServer: вездесущие блокировки

Существует извечный спор на тему "что лучше: версионники или блокировочники". SQLServer - блокировочник, Firebird - версионник. По мнению автора, любую задачу можно решить с помощью версионника. (я могу ошибаться, как и все люди). Многие считают, что любую поставленную задачу можно решить с помощью блокировочника. Возможно они правы. Но для меня совершенно очевидно, что характер СУБД следует учитывать еще на этапе проектирования БД. Те задачи, которые можно запросто сделать с помощью версионника, могут потребовать высокого мастерства от любителей блокировочников (и наоборот).

К сожалению, на практике очень часто приходится сталкиваться с тем, что разработчики БД не учитывают характер СУБД, в результате чего и возникают те самые подвисания, способные довести оператора до нервного срыва.

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

Итак, суть блокировок вкратце:

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

Классический пример блокировки: пользователь А открыл окно для добавления записей в справочник номенклатуры, при этом произошел старт транзакции. Особенность данного окна такова, что в нем можно внести сразу несколько записей, после чего можно либо подтвердить все изменения (кнопка "ОК"), либо отменить их (кнопка "Отмена"). Допустим, пользователь А добавил новую запись номенклатуры, но не успел нажать "ОК" (его срочно куда-то вызвали). Затем пользователь Б хочет со своего компьютера открыть аналогичное окошко. Однако вместо открытия окна справочника номенклатуры программа у пользователя Б зависает. Он не может продолжить свою работу до тех пор, пока пользователь А не вернется и нажмет "ОК".

Для смягчения проблемы блокировок предусмотрено несколько уровней блокировок: на уровне таблицы / на уровне страницы / на уровне записи и др. (см. Блокировки в MS SQL Server 2000). По умолчанию используется блокировка на уровне таблицы. Для управления блокировками в язык SQL внесено несколько специфичных для SQLServer конструкций (подсказок). Составлять запросы приходится с учетом уровней блокировок, что требует от программиста дополнительных знаний, выходящих за рамки стандарта SQL. Кроме того, управление блокировками на уровне страниц и особенно на уровне записей может потребовать существенных ресурсов процессора и памяти. Любая блокировка приводит к созданию в памяти объекта блокировки  (ему выделяется 96 байт). При использовании блокировок на уровне отдельных записей может оказаться недостаточно оперативной памяти, что приведет к активной работе с жестким диском (это потенциальные тормоза). Таким образом, при необходимости модификации большого числа записей, блокировку на уровне записей лучше не использовать. Тем более блокировка на уровне записи также не избавляет от зависания при попытке чтения заблокированной записи другими клиентами.

Firebird: жизнь без блокировок

Напомню, что Firebird является версионником (см. Многоверсионность в двух словах), т.е. не страдает никакими проблемами, свойственными блокировочнику. Операция чтения записи никогда не приводит к зависаниям, даже если кто-либо параллельно осуществляет модификацию той же самой записи. Читатель никогда не блокирует писателя (и наоборот). Существует лишь одно ограничение: несколько пользователей одновременно не могут изменять одну и ту же запись.

Но все-таки иногда возникает необходимость временно установить блокировку на ту или иную запись (см. Как заблокировать запись в InterBase/Firebird). В Firebird'e это решается приёмом, известным как "холостое обновление", либо с помощью конструкции SELECT FOR UPDATE WITH LOCK. В обоих случаях создается новая версия записи, и именно она блокируется. При этом операция чтения старой версии записи не блокируется.

Firebird: с транзакциями - полный порядок!

Любые операции чтения / записи в БД осуществляются в контексте транзакций. Если в ходе записи в базу данных выяснилось, что записываемая информация ошибочна, то достаточно произвести откат транзакции и никакая ошибочная информация в базу данных записана не будет. Если же Вы (или прикладная программа) убеждены в корректности информации, то после подтверждения транзакции информация появится в базе данных.

В СУБД Firebird имеется лишь 2 уровня изоляции транзакций: SNAPSHOT (моментальный снимок зафиксированных изменений) и READ COMMITED (невоспроизводимое чтение зафиксированных изменений). В случае SNAPSHOT транзакция видит все зафиксированные в БД изменения на момент своего старта, а также все изменения, которые были внесены в контексте этой самой транзакции. В случае READ COMMITED транзакция видит все зафиксированные изменения, как свои, так и чужие, причем не важно, были ли они до ее старта или после. Этих двух уровней изоляции транзакций с запасом хватает для решения любой поставленной задачи. Уровень изоляции SNAPSHOT - самый мощный. Его целесообразно использовать при построении отчетов. При этом гарантируется, что после запуска транзакции SNAPSHOT в отчет не попадут никакие новые данные, появившиеся в БД после старта транзакции. Оба типа транзакций для Firebird обходятся очень дешево и не требуют практически никаких ресурсов.

SQLServer: ох и странные эти уровни изоляции транзакций!

Совершенно иная ситуация с уровнями изоляции транзакций в SQLServer'е. Их гораздо больше, они требуют намного больше ресурсов процессора и памяти, они допускают различные странности, но ни один из них не дотягивает до уровня SNAPSHOT, который с самого начала имеется в Firebird. Некоторые критики ругают Firebird за то, что в нем нет тех же самых уровней изоляции. Но, по мнению автора, критики заблуждаются (вероятно, из-за недостаточной осведомленности).

Причина в том, что разработчики SQLServer'а изначально реализовали довольно таки своеобразные уровни изоляции транзакций: READ UNCOMMITTED, READ COMMITTED, REPEATABLE READ, SERIALIZABLE (см. статью), а затем добились того, что они вошли в официальный стандарт SQL-92 (меня там не было, поэтому могу ошибаться). Рассмотрим эти уровни изоляции более подробно:

- READ UNCOMMITTED (грязное чтение) - уровень изоляции, допускающий чтение данных, которые еще не были сохранены в БД. Клиент А еще не вызвал COMMIT для подтверждения транзакции, а клиент Б уже видит данные, добавленные клиентом А. Это, извиняюсь за выражение, дикость, которая может привести к совершенно непредсказуемым последствиям. Однако это единственный уровень изоляции, который гарантирует, что читатель никогда не зависнет. Т.е. это попросту борьба с зависаниями! Хороший пример: быстрое определение приблизительного количества записей в таблице. Напомню, что в случае Firebird'а читатель никогда не виснет, поэтому уровень READ UNCOMMITTED не требуется.

- READ COMMITTED (невоспроизводимое чтение зафиксированных изменений) - уровень изоляции, гарантирующий, что данные, полученные в результате запроса, действительно имеются в БД (т.е. транзакция подтверждена). Если один и тот же SELECT-запрос выполнить несколько раз повторно, например:
SELECT * FROM PRICELIST WHERE PRICE BETWEEN 100 AND 200
то далеко не факт, что каждый раз он вернет одни и те же строки, ведь за это время цену для одного или нескольких товаров могли поменять: минуту назад было 5 позиций по цене от 100 до 200 руб, а сейчас уже 6.
READ COMMITTED у SQLServer и Firebird очень похожи, однако SQLServer на данном уровне изоляции зависнет, если на таблицу или запись действует блокировка.

- REPEATABLE READ (воспроизводимое чтение зафиксированных изменений). В отличии от READ COMMITTED, "гарантирует" (по мере своих возможностей), что при повторных вызовах одного и того же SELECT-запроса будут возвращены одни и те же данные. Но здесь имеются свои проблемы! Фактически REPEATABLE READ блокирует строки сразу после чтения, т.е. никто другой их изменить/удалить/прочитать не сможет, пока транзакция REPEATABLE READ не завершится. В остальном она ничем не отличается от READ COMMITTED. Еже ли в таблицу PRICELIST добавят новую запись, например с ценой 150 руб, то она окажется в результирующем наборе данных, несмотря на "воспроизводимое" чтение.

- SERIALIZABLE - Самый высокий уровень изолированности; транзакции полностью изолируются друг от друга. На этом уровне результаты параллельного выполнения транзакций для базы данных в большинстве случаев можно считать совпадающими с последовательным выполнением тех же транзакций (по очереди в каком-либо порядке) (см. Википедию) (на текущий момент с данным уровнем изоляции я не сталкивался, поэтому от себя добавить ничего не могу).

В SQLServer'е сравнительно недавно реализовали уровень изоляции SNAPSHOT, а также имитацию многоверсионности. Однако от разработчиков БД требуются дополнительные знания и навыки для успешной и эффективной работы с этим новым уровнем изоляции. Поддержка SNAPSHOT в SQLServer'е требует еще больших ресурсов процессора и памяти (здесь активно задействована стандартная база данных TEMPDB, расположенная на жестком диске, что приводит к дополнительным тормозам).

Журнал транзакций - непременный атрибут каждой СУБД?

На профессиональных форумах по базам данных часто звучит следующее категорическое утверждение: СУБД, не имеющая журнала транзакций (далее - ЖТ) не может рассматриваться в качестве серьезной (она непременно является плохой). Это, прежде всего, камень в огород Firebird'а. Причем утверждают это в том числе профессиональные разработчики. По мнению автора, подобная категоричность в выводах является следствием недостаточной их осведомленности в механизмах работы СУБД Firebird. В принципе, в защиту Firebird'а можно сказать такую простую фразу: он не нуждается в отдельном журнале транзакций! Некоторые не представляют, каким образом вообще СУБД может обходиться без ЖТ. Для них подойдет такой ответ: ЖТ является встроенным в БД Firebird.

Для работы SQLServer'а ЖТ необходим, поскольку в нем временно сохраняется информация о всех произведенных изменениях в базе данных. Если транзакция не была подтверждена, то изменения в "основной" файл базы данных не попадут. А если транзакция подтверждена, то изменения должны рано или поздно попасть в "основной" файл. СУБД SQLServer автоматически принимает решение, в какой момент информацию из файла ЖТ перелить в "основной" файл. На самом деле разделение на "основной" файл и файл "журнала транзакций" очень условно! SQLServer требует, чтобы оба файла всегда присутствовали.
ЖТ позволяет SQLServer'у автоматически исправлять ошибки, возникающие вследствие перебоев электропитания. Разумеется, источник бесперебойного питания выручает, но не всегда (операторы/уборщицы нередко его "случайно" вырубают, к тому же срок службы его не бесконечен).

Очевидно, что для выполнения процесса перемещения информации из ЖТ в основной файл требуются ресурсы и время (иногда существенное). При отсутствии мощного железа в эти моменты могут происходить ощутимые тормоза. Для улучшения ситуации рекомендуют выносить ЖТ на отдельный жесткий диск. Но нужно помнить, что при поломке любого из дисков БД окажется "убитой" (обычно для избежания подобных проблем ставят RAID-массивы, как минимум с зеркалированием, а также регулярно выполняют создание резервной копии БД).

Приличная СУБД может обойтись без журнала транзакций

Firebird является версионником, поэтому не нуждается в ЖТ. Любые изменения в БД Firebird могут вноситься только в контексте запущенной транзакции. Каждая транзакция имеет свой порядковый номер TID, а каждая запись в БД помечается соответствующим TID. Если транзакция подтверждается, то запись с этим TID будет доступна для дальнейших операций. Если транзакция не подтверждается, то запись не будет доступной. Что может быть проще? Не следует считать, что запись попадает в файл БД немедленно, сразу после выполнения команд INSERT/UPDATE. Firebird не обращается к жесткому диску без необходимости. Однако при подтверждении транзакции гарантируется, что изменения немедленно будут сохранены в файле БД (вернее, информация гарантированно будет передана контроллеру жесткого диска). Firebird является на удивление живучей СУБД в условиях периодических проблем с электропитанием. А "случайные" перезагрузки компьютеров происходят у операторов постоянно. Любой уважающий себя оператор стремится обмануть систему учета денежных средств и положить деньги клиента в свой карман, а не в кассу. Давно замечено, что самый надежный способ обмануть практически любую систему учета - в нужный момент нажать клавишу "Reset". Именно в таких условиях и приходится работать Firebird'у. Работает годами и не на что не жалуется. Главное - не забывать периодически делать бэкапы.

Firebird - только для мелких баз данных?

Хорошо известно, что SQLServer способен работать с любыми объемами данных, лишь бы железо не подвело. Сегодня никого не удивляют даже терабайтные базы данных. Еще бы, ведь у каждого сейчас на персональном компьютере терабайтные винчестеры, есть на чем поэкспериментировать. Тестирование Firebird показывает, что он прекрасно справляется с терабайтными БД даже на обычном десктопном компьютере с 32-разрядной WinXP (см. База размером 1 терабайт на Firebird, часть 2). Интересно посмотреть, как бы себя повел SQLServer на аналогичном железе...

Firebird: Компактные базы данных

Размер файла базы данных Firebird в несколько раз меньше, чем размер БД SQLServer с таким же количеством данных. При этом нужно учитывать, что у SQLServer'а есть еще и журнал транзакций, который запросто может оказаться по объему в десяток раз больше, чем основной файл базы данных. Реальный пример: основной файл - 800 МБ, файл ЖТ: 16 ГБ. В результате общий размер - почти 17 ГБ. (без сомнения, можно отрегулировать максимальный размер ЖТ, но тенденция, по мнению автора, налицо)

SQLServer: Зачем нужна база данных TEMPDB?

TEMPDB - это служебная база данных. Она используется очень часто:

- Для временного хранения результирующего набора данных перед выдачей клиенту. Даже при выполнении простейшего запроса
SELECT * FROM MyTable SQLServer сначала скопирует все данные из MyTable во временную таблицу (не обязательно на диске; она может располагаться в ОЗУ) и только после этого начнет отдавать результат клиенту (это вполне логичное поведение, позволяющее снизить вероятность блокировок, ведь на клиента в дальнейшем эти записи могут передаваться очень долго, особенно при медленном канале связи), причем клиенту будут переданы все записи, даже если их несколько миллионов. Разумеется, если запрос вернул всего лишь несколько сотен строк, то дисковую память можно и не задействовать, но а если миллионы, то могут возникнуть ощутимые тормоза (операция копирования миллиона записей из рабочей БД в дополнительную область - операция не самая быстрая, особенно если этот кэш находится на то же самом диске).
Firebird лишен этих недостатков. Для простых запросов никакие дополнительные временные таблицы или файлы ему не нужны, не требуется почти никаких ресурсов процессора / ОЗУ. Даже если в таблице миллиарды записей, то запрос
SELECT * FROM MyTable
отработает моментально, а клиент получит ровно столько записей, сколько он попросит (по умолчанию на клиента передаются записи в том количестве, которое пользователь может увидеть у себя на экране; в дальнейшем остальные записи будут подгружаться на клиента по мере необходимости, т.е. если пользователь произвел соответствующее действие, например скроллинг в конец таблицы, либо вызвал команду FetchAll).

- Для промежуточного хранения данных при выполнении сложных SQL-запросов (объединение, соединение, фильтрация, сортировка и т.д.). Firebird поступает аналогичным образом; временные файлы располагаются в каталоге TEMP ОС, либо в каталоге, указанном в файле конфигурации firebird.conf.

- При создании временных таблиц в хранимых процедурах (или функциях). Хранимые процедуры/функции (далее - "хранимки") в SQLServer разрабатываются на специальном языке Transact-SQL (сокр. T-SQL). В Firebird используется язык P-SQL. Одним из наиболее важных целей хранимок является возвращение результата в виде таблицы. Эта цель достигается в обоих случаях, но по-разному. В T-SQL внутри хранимки создается временная таблица, а затем она заполняется данными, после чего работа хранимки завершается, а строки из временной таблицы передаются на клиента.
В Firebird'е хранимые процедуры, возвращающие клиенту данные, называются "селективными". У них совсем другой принцип. Никакие временные таблицы не создаются. Выходным параметрам (полям набора данных), указанным при объявлении процедуры, присваиваются значения, после чего вызывается команда SUSPEND. Она приостанавливает работу хранимой процедуры до тех пор, пока клиент не загрузит созданную только что запись. Как мы видим, у Firebird'а и в этом случае преимущество - не совершается никаких лишних действий, почти не задействуются ресурсы процессора / ОЗУ.

- Для поддержки уровня изоляции SNAPSHOT (в TEMPDB хранятся версии записей, т.е. обеспечивается поддержка механизма версионности).
Заложенная изначально версионность - это важное преимущество СУБД Firebird. На поддержку версионности Firebird'у требуется гораздо меньше ресурсов.

Генератор или автоинкремент: что лучше?

Каждая запись в таблице БД должна отличаться от остальных записей. Отличительным признаком каждой записи является первичный ключ, состоящий из одного или нескольких полей. Чаще всего первичный ключ (в любой БД) состоит из единственного целочисленного поля (очень часто оно объявляется как "ID     INTEGER"). Любая СУБД предоставляет механизм генерации числовой последовательности, что позволяет пронумеровать каждую запись таблицы собственным уникальным числовым значением (1, 2, 3, 4, 5 и т.д.). Этот механизм в рассматриваемых СУБД реализовано по-разному: в SQLServer'е используются автоинкрементные (IDENTITY) поля, а в Firebird'е - генераторы.

В случае IDENTITY-полей сгенерированное значение можно узнать только после добавления новой записи. Заранее определить значение для автоинкрементного поля невозможно. (просьба поправить, если автор ошибается) Это ограничение влечет за собой массу сложностей у разработчиков баз данных SQLServer. Просто львиная доля сообщений на форумах посвящена одному у тому же вопросу: как заранее получить значения для автоинкрементного поля и использовать его в других таблицах. Определить сгенерированное значение можно после добавления записи примерно таким образом: SELECT @Ident = @@IDENTITY, где "@@IDENTITY" - системная переменная в языке "Transact-SQL" SQLServer'а, которая содержит последнее сгенерированное значение. По мнению автора, данный механизм очень слабый и его нельзя сравнивать с реализованным с Firebird'е механизмом генераторов. Единственное преимущество IDENTITY-полей в том, что из нельзя редактировать (SQLServer ругается на любую попытку изменения значения IDENTITY-поля в существующей записи).

В Firebird'е генерация числовой последовательности осуществляется с помощью генераторов (см. Генераторы и их использование). Генератор - это самостоятельный объект базы данных, который сам по себе ни с какими полями не связан. Это попросту некий счетчик, хранящийся в базе данных. При вызове GEN_ID(GeneratorName, 1) происходит увеличение генератора "GeneratorName" на единицу и функция возвращает полученное значение. В дальнейшем это значение можно использовать в хранимых процедурах, триггерах, операторах INSERT, UPDATE и даже SELECT. Более того, на одну таблицу можно настроить несколько генераторов и при этом никаких сложностей не возникнет.
Пример добавления записи:
INSERT INTO MYTABLE (ID) VALUES (GEN_ID(GEN_MYTABLE,1))
Очень часто для автоматической генерации значений используют триггеры, например:
ACTIVE BEFORE INSERT POSITION 0
AS
BEGIN
  IF (NEW.ID IS NULL) THEN
    NEW.ID=GEN_ID(GEN_MYTABLE,1);
END

Теперь не требуется отдельно вызывать функцию GEN_ID, триггер все сделает сам!

Таким образом, механизм генераторов в Firebird'е гораздо мощнее, чем механизм автоинкремента, реализованный в SQLServer'е. Генераторами гораздо проще и удобнее управлять. Генераторы дают возможность разработчику БД (даже начинающему) заниматься именно разработкой, а не бесчисленными экспериментами с "@@IDENTITY" и "Transact-SQL".

Нужны ли триггеры строкового уровня (row-level)?

Триггер - это своего рода процедура, которая хранится в базе данных и срабатывает при возникновении определенного события. Существуют триггеры уровня DDL, срабатывающие при внесении изменений в структуру базы данных, и триггеры уровня DML, срабатывающие при изменении данных, хранящихся в БД. DML-триггеры могут срабатывать при возникновении следующих событий: вставка (команда INSERT), изменение (команда UPDATE), удаление (команда DELETE). Триггеры могут применяться в самых различных ситуациях:
- проверка корректности и согласованности данных;
- обеспечение ссылочной целостности;
- удовлетворение сложных правил бизнес-логики;
- журналирование выполненных действий;
- и многое другое.

Особенность DML-триггеров в Firebird'е такова, что они срабатывают для каждой записи. Безусловно это удобно. Казалось бы, что иначе и быть не должно.

Но не все в нашем мире так просто. В SQLServer'е отсутствуют триггеры строкового уровня. Триггеры в SQLServer'е действуют на уровне всей команды (statement-level). Например, при изменении 100 записей с помощью команды UPDATE, триггер сработает лишь один раз и в нем разработчик должен каким-то образом произвести обработку сразу двух временных таблиц: DELETED и INSERTED, в каждой из которых по 100 записей.
Разработка триггеров в SQLServer'е гораздо сложнее, чем в Firebird'е. Объем кода получается намного больше при приблизительно равном количестве возможностей. Разработчик баз данных Firebird может разрабатывать собственные несложные триггеры с первых дней знакомства с этой СУБД (ему даже SQL знать не обязательно). Разработчик баз данных SQLServer перед программированием первого триггера обязан освоить огромный объем теоретического материала, выучить язык SQL, выучить язык "Transact-SQL", получить солидную практическую подготовку и лишь после этого у него начнет что-то получаться. Производительность триггеров в Firebird'е - неизменно высокая (нужно постараться, чтобы допустить какие-либо тормоза), а производительность триггеров в SQLServer'е зависит исключительно от опыта разработчика, от качества владения языками SQL и T-SQL! (разумеется, что при грамотном написании триггеров производительность операций сразу над множеством строк может оказаться выше, чем построчная обработка)

SQLServer: важные преимущества

Хотелось бы отметить некоторые важные (по мнению автора) преимущества СУБД SQLServer (относительно СУБД Firebird):

- Огромное количество литературы. Русскоязычной литературы по SQLServer'у гораздо больше, чем по всем остальным СУБД (вместе взятым).

- Встроенная репликация. По умолчанию любая таблица в SQLServer'е содержит служебную информацию, необходимую для работы механизма репликации.

- Встроенный генератор отчетов.

- Выборка из множества баз данных. SQLServer позволяет из одного SELECT-запроса выбрать данные из различных баз данных, расположенных как на этом же сервере, так и на удаленных серверах.

- Встроенные средства шифрования данных.

- Встроенный полнотекстовый поиск. Позволяет быстро отыскать документ по заданному текстовому фрагменту (поиск осуществляется в BLOB-полях).

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

Несмотря на наличие большого количества преимуществ, автор затрудняется назвать преимущества, способствующие повышению эффективности OLTP-системы (за исключением, может быть, встроенной репликации). Все найденные автором преимущества имеют значение только для OLAP.

Firebird: важные преимущества

Ниже приведены важные (по мнению автора) преимущества Firebird по сравнению с SQLServer.

- Стоимость. Firebird абсолютно бесплатен. Это не говорит о том, что разработчики Firebird - законченные альтруисты и готовы годами работать за бесплатно! Разработка спонсируется из различных источников, но для нас Firebird - бесплатен!

- Простота установки. Установка Firebird из инсталлятора занимает не более минуты. Но есть еще так называемая "ручная установка" - в этом случае Firebird устанавливается и запускается моментально.

- Одновременная работа большого количества пользователей без каких-либо зависаний и блокировок! Firebird в OLTP-системе без труда поддерживает работу одновременно нескольких сотен пользователей (максимальное число одновременных подключений зависит от характеристик серверного железа, возможностей ОС, настроек конфигурации Firebird, а также от того, насколько корректно написана клиентская часть приложения базы данных).

- Кроссплатформенность. Firebird работает под Windows (32- и 64-битные), различные версии Linux (32- и 64-битные), Solaris (Sparc и Intel), HP-UX (PA-Risc) и MacOS X. Клиентская и серверная части Firebird могут работать под разными операционными системами. Многие предпочитают использовать Linux в качестве серверной ОС, поскольку такое решение на практике получается более надежным и дешевым.

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

- Удобные средства администрирования. Для администрирования баз данных Firebird предназначен замечательный инструмент - IBExpert. Данная программа является бесплатной для жителей стран СНГ, в том числе для российского пользователя. Можно считать данный инструмент кроссплатформенным (на Linux'е он без проблем запускается под Wine). По мнению автора, IBExpert значительно удобнее, чем "SQL Server Management Studio". Складывается ощущение (возможно, немного субъективное), что разработчиков "SQL Server Management Studio" интересует только сам факт наличия той или иной возможности, но их совершенно не беспокоит, насколько удобно ими пользоваться. Нельзя сказать, что в IBExpert'е все идеально, но работать с ним получается намного быстрее и удобнее (это все-таки коммерческий проект).

- Не требователен к ресурсам. Для работы с многогигабайтными (и даже терабайтными) БД Firebird'у достаточно средненькой персоналки. Разумеется, производительность СУБД Firebird напрямую зависит от характеристик железа: процессора, памяти, жесткого диска / SDD. При необходимости, Firebird может использовать более 2 ГБ ОЗУ, но при этом нужна 64-разрядная версия ОС и Firebird'а. И не нужно забывать о настройках файла конфигурации firebird.conf.

- Оперативная помощь и поддержка. Огромный объем информации Вы найдете на сайте ibase.ru. Задать вопрос Вы можете на форуме sql.ru. Вам очень быстро ответят на вопрос, либо укажут направление поиска. На форуме присутствуют (практически ежедневно) ключевые разработчики СУБД Firebird.

Firebird: некоторые особенности

В соответствии с темой данной статьи я, казалось бы, должен говорить только о недостатках SQLServer'а и только о достоинствах СУБД Firebird. Конечно, есть у СУБД Firebird некоторые недостатки (инвертируйте раздел "SQLServer: важные преимущества" и вы их увидите). Но есть такие, которые сложно назвать недостатками (как об этом периодически напоминают различные критики Firebird), скорее это особенности, которые никак не сказываются на работе системы в целом. Этот раздел больше именно для таких критиков, чем для простых разработчиков.

- Активная работа с жестким диском. Firebird критики часто ругают за то, что он "пишет в файл базы данных по каждому чиху". Это действительно так. SQLServer точно также пишет по каждому чиху, но не в основной файл БД, а в журнал транзакций. За это его не ругают. А Firebird ругают. Значит есть за что. У Firebird'а нет журнала транзакций, а значит он вынужден писать в то, что имеется - файл базы данных. В файле БД имеется заголовочная зона, состоящая из нескольких страниц, в том числе с информацией об активных транзакциях и с счетчиками генераторов. Это очень важные страницы, поэтому Firebird пишет в них чаще всего, а иногда - очень часто (разумеется, только когда нужно). При очень активном добавлении большого количества новых записей страница с генераторами может оказаться тем узким горлышком, которое тормозит работу СУБД. Но это только в теории! А в действительности современные жесткие диске содержат кэш на отложенную запись (32 МБ, 64 МБ и больше), так что "недостатки" Firebird'а вполне успешно "разруливаются" электроникой жесткого диска.
В теории подобный способ записи страницы генераторов на твердотельный накопитель SSD может быстро вывести его из строя (в SSD количество перезаписей ячеек памяти ограничено). В реальности - годами у людей работает и проблем не возникает, т.е. электроника SSD успешно разруливает данную ситуацию.

- "Нестабильность" курсора. Когда я впервые услышал этот термин, то не на шутку взволновался, подумал, что Firebird глючит и работает нестабильно. Оказалось, что все намного проще и нет причин для паники! Под этим термином подразумеваются две довольно разные ситуации:
1) из разряда "не руби сук, на котором сидишь" (см. FAQ). Выполнение запроса
INSERT INTO TABLE1 SELECT * FROM TABLE1
приведет к тому, что Firebird зациклится и будет добавлять записи в таблицу TABLE1, пока его не прервут каким-либо образом (ну что тут скажешь! Не нужно так делать!) Кстати, в Firebird 3 данную "проблему" исправили.
2) если в транзакции READ COMMITED выполнить запрос
SELECT * FROM TABLE1
из таблицы с большим числом записей и не спеша двигаться в конец таблицы, то мы запросто может встретить новые записи, добавленные в таблицу после выполнения указанного SELECT-запроса.
Таким образом, SELECT-запрос в рамках транзакции READ COMMITED не является "атомарным". И за это критики ругают Firebird! Но кто сказал, что SELECT-запрос должен быть во всех случаях "атомарным"? (это чьи-то фантазии!) В данном вопросе каждая СУБД поступает так, как считает правильным.
Для Firebird транзакция по умолчанию - это SNAPSHOT, и здесь SELECT-запрос является атомарным на 100%!

Заключение

В интернете можно встретить немало статей, посвященный сравнению различных СУБД между собой. При этом используют примитивные SQL-запросы, прогоняют их на нескольких СУБД и сравнивают результаты. По мнению автора, цена таких тестов - пять копеек. Я намеренно не пошел по этому пути; в данной статье отсутствуют конкретные цифры. Вместо этого я показал логику работы различных механизмов работы СУБД и указал их преимущества и недостатки. Я постарался быть предельно объективным, однако не исключаю некоторые перекосы. Если в данной статье Вы обнаружили оскорбительный для себя материал, либо заведомо неверную информацию, то Вы можете сообщить автору об этом.
В статье напрямую так и не прозвучал четкий ответ на вопрос "Почему мне нравится Firebird". Одной фразой его не сформулируешь! Я постарался ответить на него очень подробно, надеюсь у меня получилось.

Логинов Дмитрий © 2005-2015