Использование InterBase на ОС Linux

Константин Кузнецов, klkuznetsov@ns.palata.ru

В данном материале я попытаюсь кратко изложить опыт двулетнего использования InterBase 4.0 на Linux. Основная цель данного материала развеять множество легенд о данном продукте, часть из которых, как это не странно, распространяет компания Interbase. При этом постараюсь излишне не вдаваться в подробности инсталляции, администрирования и настройки Linux, не касающиеся InterBase.

По моему опыту, данная версия IB, в присутствии libc.5.xx.so, устойчиво работает на следующих версиях ОС:
  • Redhat 4.2 – 6.0
  • Mandrake – 5.2 – 6.0
  • Slakware 4.0

Использовались ядра от 2.0.30 до 2.2.13. В данный момент я использую следующую конфигурацию: Slakware 4.0 (libc.5.so), ядро 2.2.13. При этом естественно компилировать что-либо работающее с IB (например IBPerl) можно только если весь Ваш Linux собран с libc5.
 

Настройка и установка

Прежде чем приступать к установке InterBase, необходимо изменить следующие установки в ядре, и пересобрать его:
  1. В файле /usr/src/linux/include/linux/socket.h установить параметр SOMAXCONN = как минимум количеству пользователей.
  2. /usr/src/include/linux/tasks.h – установить количество задач NR_TASKS как минимум вдвое большим количеству пользователей.
  3. Убедиться в наличии в ядре опции SYSVIPC.
  4. За одно я, как правило, увеличиваю число одновременно открытых файлов параметры NR_OPEN, OPEN_MAX, __FD_SETSIZE, до где-то 8192.
  5. Обязательно проверьте параметры семафоров, особенно если у Вас на сервере планируется выполнять что-нибудь еще, кроме InterBase. Параметры SEMMNI – 512, SEMMSL – 256, SEMOPM 100 в файле sem.h
Далее, собственно, первая легенда, распространяемая всеми кому не лень в статьях типа "Classic vs SuperServer". Заключается идея в том, что Classic InterBase почему-то обязательно должен работать от юзера root (суперпользователя), что весьма небезопасно. По моему опыту, это не совсем так. Поэтому делаем так:
  1. Создаем группу ibase;
  2. Пользователя ibase в группе ibase, с домашним каталогом /home/ibase;
  3. От юзера root ставим InterBase в каталог /home/ibase;
  4. Далее cd /home/ibase; chown –R ibase.ibase *
  5. В файле /etc/services строку вида
gds_db stream tcp nowait root /usr/interbase/bin/gds_inet_server gds_inet_server
заменить на
gds_db stream tcp nowait ibase /usr/interbase/bin/gds_inet_server gds_inet_server


Опции сокета

В случае, преобладания запросов с маленьким количеством данных, пересылаемых от сервера к клиенту (практически всегда), для ускорения работы, следует отключит буферизацию сетевых обменов для InterBase. Для чего следует откомпилировать программку следующего содержания. Комментарии сохранены специально:
/* From - Sat Oct 31 17:46:17 1998
Path: forums.borland.com!not-for-mail
From: Andy Bakun <abakun@reac.com>
Newsgroups: interbase.public.linux
Subject: fix for NAGLE problem without kernel recompile
Date: Thu, 29 Oct 1998 17:49:40 -0600
Organization: Another Netscape Collabra Server User
The Nagle algorithm option forces use of the TCP_NODELAY option on all network
connections, which is generally NOT what you want, since it can effect the
network transfer rate of other high bandwidth protocols (which is why I believe
it is commented out now in the linux kernel source). At my site, I wrote the
following program and used it as a wrapper in inetd.conf (see below for that
part)
set_TCP_NODELAY.c:
----------------------------------
*/
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
void main(int argc, char *argv[])
{
int value = 1;
setsockopt(0, IPPROTO_TCP, TCP_NODELAY, (char *)&value,sizeof(int));
setsockopt(1, IPPROTO_TCP, TCP_NODELAY, (char *)&value,sizeof(int));
setsockopt(2, IPPROTO_TCP, TCP_NODELAY, (char *)&value,sizeof(int));
execl(argv[1], NULL);
}
/*

----------------------------------
Compile with:
$ gcc set_TCP_NODELAY.c -o set_TCP_NODELAY
Copy the set_TCP_NODELAY executable to some well known location (I used
/usr/local/bin).
Edit your inetd.conf file, find the gds_db line. Change the sixth through the
last fields to read the following:
/usr/local/bin/set_TCP_NODELAY gds_inet_server /usr/interbase/bin/gds_inet_server
You essentially want to have inetd startup the set_TCP_NODELAY program, and give
it options to start up gds_inet_server. You probably had something like the
following for the sixth through last fields:
/usr/interbase/bin/gds_inet_server gds_inet_server
Note the order change. Those who want a full explaination of the argument order
should consult the man page for inetd(8).
What this little program does is set it's stdin, stdout, and stderr? streams to
not use the Nagle algorithm, then execs the Interbase gds_inet_server program.
Note that this is EXACTLY the same effect as compiling your kernel with No Nagle
enabled, but it only effects the programs you use it on (for our purposes,
gds_inet_server, but set_TCP_NODELAY can be used in general with programs
started from inetd that need it).
I hope you Interbase programmer guys include setting the TCP_NODELAY socket
option in a future release of gds_inet_server, if it isn't in the plan already.
:)
Andy Bakun.
abakun@reac.com
j park wrote:
> I've read various posts about those who have disabled their NAGLE algorithms
> for performance purposes. For whatever reason, we don't seem to be able to
> find that option to try it for ourselves; we're not stupid but maybe our
> eyesight is bad. We're running a version of Red Hat Linux and the Linux
> version seems to be 2.0.30.
>
> We were also told by the hardware vendor that distributes Linux on their
> machines that this is something they haven't heard of since the 1.x versions
> (which seems to agree with what I've seen) and that this option can be set
> per-process (instead of the whole kernel).
>
> Any comments?
>
> - NAGLED
*/

Остается добавить лишь killall –HUP inetd.

Так же, для увеличения производительности, полезно указать на клиенте параметр соединения CACHE=n > 75.
 

Масштабирумость

Что собственно подразумевается под этим, наверно ни кому не известно. Мне почему-то думается о прозрачной переносимости наработок программиста между IBM PC и кластерами Digital Alpha под Open VMS. Если, имеется ввиду, два писюка с разным числом процессоров, то и Linux и Classic IB масштабируются великолепно (или хорошо на ядрах старой серии 2.0.XX). Как проверить? Берем машину примерно 2xPPro-200-256RAM, базу достаточно большую, но меньше чем объем оперативки (чтобы винтами не шуршал), коннектимся, запускаем серьезный запрос (желательно чтобы секунд 30 шел), измеряем время выполнения, смотрим в top – gds_inet_server – 50% ресурсов процессоров, открываем два коннекта к базе, запускаем два таких же запроса одновременно, смотрим в top – gds_inet_server – 50% процессоров – две одинаковые строчки, измеряем время выполнения каждого запроса – не изменилось. Делаем вывод: масштабируемость – линейная. Это был второй миф. Данное утверждение также проверялось на четырехпроцессорном PC.
 

unix vs winnt

Если у Вас планируется использование IB без долгих статистических запросов или с одним пользователем, есть постоянно присутствующий на работе MS Certified Professional, то смело ставьте на сервер WINDOWS.
 

Программ без ошибок не бывает?

Единственный серьезный глюк IB 4.0, обнаруженный за два года эксплуатации, – это следующий эффект: если пользователь выключил машину не отсоединившись от базы, его процесс gds_inet_server занимает все доступные ресурсы процессора и пишет в interbase.log – inet error 32 со скоростью ~10Мбайт/c. Данная проблема решилась просмотром состояния сетки и убийством процессов, у который сервис gds_db в состоянии CLOSE. Я запускаю следующий скрипт из-под crond каждые две минуты.
#!/bin/bash
#echo "netst";
#killall -HUP inetd;
netstat -p --tcp --ip 2>/dev/null|
grep gds_db|
grep "CLOSE "|
awk '{print $5 " " $6 " " gensub("/"," ","gggggggg",$7)}'|
while read host state pid proc
do
echo "Abnormally closed IB connections. Killing";
echo HOST: $host " "STATE: $state " " PID: $pid " "PROC: $proc;
kill -TERM $pid ;
killall -HUP inetd;
done
exit 0;

А также ln –sf /dev/console /usr/interbase/interbase.log.
 

Специфические средства разработки

Весьма удачной, на мой взгляд, является связка: Apache + perl + Ibperl = CGI, я думаю, в такой архитектуре, вполне можно построить приличную трехзвенку. Perl + IBPerl также очень удобен для перекачки, экспорта и импорта данных.

Привожу пример программы разбирающей и закачивающей в базы файлы формата CLF.
#!/usr/bin/perl
use IBPerl;
use strict;
print "Eating Apache log file...\n";
my $db = new IBPerl::Connection(
Server=>'altair',
Path=>'/bases/hit.gdb',
User=>'sysdba',
Password=>'****');
if ($db->{'Handle'}<0)
{
print "Connect error\n $db->{'Error'}\n";
};
my $count = 0;
my $tr = new IBPerl::Transaction( Database => $db );
if ($tr->{'Handle'}<0)
{
print "Transaction error\n $tr->{'Error'}\n";
};
my $query =<<SQL;
INSERT INTO APACHE_LOG (
AL_HOST,
AL_IDENT,
AL_AUTHUSER,
AL_DATE,
AL_METHOD,
AL_REQUEST,
AL_PROTO,
AL_STATUS,
AL_BYTES)
VALUES (
?,
?,
?,
?,
?,
?,
?,
?,
?)\;
SQL

# print "$query\n";
my $st = new IBPerl::Statement( Transaction => $tr, Stmt => $query );
if ($st->{'Handle'}<0)
{
print "Statement error\n $st->{'Error'}\n";
};
 
while (<>)
{
# print "$_\n";
my (
$client,
$identuser,
$authuser,
$datetime,
$tz,
$method,
$url,
$proto,
$status,
$bytes) =
/^(\S+) (\S+) (\S+) \[(\S+) (\S+)\] "(\S+) (.*?) (\S+)" (\S+) (\S+)$/ or { next};

if ($bytes =~ /-+/) {
$bytes='0'};
if ($bytes eq "") {
$bytes='0'};
if ($status =~ /-+/) {
$status='0'};
if ( $st->execute(
$client,
$identuser,
$authuser,
$datetime,
$method,
$url,
$proto,
$status,
$bytes
) ) {
print "Executing errot\n $st->{Error}\n";
print "$_\n";
print "In string \n $query\n";
print "LINE: $client $identuser $authuser $datetime $method $url $proto $status $bytes ";
} ;
$count++;
};
$st->close;
if ($tr->commit) {
print "Commit error\n $tr->{Error} \n "};
print "Total processed $count lines\n\n";
$db->disconnect;
exit 0;

С наилучшими пожеланиями Константин Кузнецов.

С автором можно связаться по адресу klkuznetsov@ns.palata.ru


Дополнения к статье

Сергей Чернышов, chern@ftcenter.ru:

Сегодня получил по рассылке ссылку на IB40LinuxHOWTO. Эксплуатирую эту систему около 2 лет. Хочу поделиться опытом насчет IB4Linux – в основном, это касается компиляции клиентских программ на платформах с glibc2 (сие потребовалось при переходе с RedHat 4.2 на 6.1). Условие сборки и, вроде бы, нормальной работы клиентов:
  • статическая компоновка с libgds.a
  • должен быть прилинкован модуль примерно следующего содержания
 
/* Make IB4 compatible with glibc */
#include <sys/stat.h>
#include <setjmp.h>
int _xstat(int ver,const char *filename, struct stat *statbuf) { return __xstat(ver,filename,statbuf); }
int _fxstat(int ver,const int fildes, struct stat *statbuf) { return __xstat(ver,fildes,statbuf); }
int __setjmp(jmp_buf env) { return setjmp(env); }
/* End of compat section */

ЭТО ПРОХОДИТ ТОЛЬКО ДЛЯ IB4!
 
P.S. Еще недавно столкнулся с проблемой обработки дат на системах с compat-glibc – запрос select cast("now" as date) from rdb$database всегда выдает время GMT а не локальное. Оказалось, надо сделать mkdir /usr/lib/zoneinfo cd /usr/lib/zoneinfo ln -s localtime /etc/localtime Видимо, это баг compat-glibc.

Владимир Гончаров, vova@rpb.ru:

Я не тонкий знаток InterBase 4.0, но хотел бы добавить (по памяти – в последний раз эту процедуру я дел больше года назад):
  • следует заменить суидные программы $INTERBASE/bin от пользователя root на аналогичные от ibase. То есть, преположим, что gsec имеет атрибуты
rwsr-xr-x 1 root root gsec
тогда
chown ibase gsec
chmod +s gsec

поможет проблеме безопасности. Насколько я помню, возможность некорректного обращения к локальным файлам посредством программ от InterBase существовала(?) и при локальных вызовах.
  • проблему interbase.log можно решить радикальнее-
ln -s /dev/null $INTERBASE/interbase.log
но у меня не было причин изучать его содержимое. Также, эта проблема появилась то ли с 2.1.47, то ли с 2.1.6x. То есть ядра, 2.0.x этой болезни не подвержены
  • я бы не стал изменять параметры ядра (по крайней мере, некоторые) посредством изменения ядерных исходников. Для этого есть /proc и аппарат sysctl, иначе необходимость срочной замены ядра (вследствии security flaw, например), может повлечь за собой серьезные проблемы (может не наложится очередной патч, к примеру)
  • не совсем понятны рассуждения о масштабируемости. Приведенный пример ни о чем не говорит.

И в заключение, сервер InterBase 4.0 в моей организации стоит больше трех лет, закрывая довольно важное финансовое приложение на ядре 2.0.30. Максимальный uptime был больше года (UPS вскипел:(, но, правда, версия стоит немного другая InterBase for SCO под эмулятором iBCS. Переход на чисто Линуковую версию так и не состоялся про причине ненадобности – все и так работает :)

C уважением, Владимир Гончаров

Подпишитесь на новости Firebird в России

Подписаться