Perl для системного администрирования

       

Мониторинг состояния сервера


В качестве последнего примера рассмотрим несколько способов наблюдения за состоянием SQL-сервера. Программы такого рода по своей природе похожи на службы наблюдения за сетью, уже рассмотренные в главе 5 «Службы имен TCP/IP».

Наблюдение за свободным пространством

Если на мгновение вникнуть в технические тонкости, то сервер баз данных - это место для хранения разного добра. И если места для его хранения не остается, то это либо плохо, либо очень плохо. Так что программы, помогающие следить за свободным и занятым пространством на сервере, действительно очень полезны. Посмотрим на DBI-программу, созданную для наблюдения за дисковым пространством на сервере Sybase.

Вот отрывок вывода программы, которая в графическом виде показывает, как любая база данных использует место на сервере. В каждом разделе отражено, сколько процентов пространства занято данными и журналом. В следующей диаграмме d соответствует пространству, занятому данными, а 1 - журналам. Для каждой диаграммы указан объем занятого и доступного пространства (в процентах):

| ddddddd |15.23%/5MB

hpotter--------1 |

| |0.90%/5МВ

| ddddddd |15.23%/5MB

dumbledore-----1 |

| |1.52%/5МВ

(dddddddd |16.48%/5MB



hgranger------- |

| |1.52%/5МВ

Iddddddd |15.23%/5MB

rweasley-------1

1 |3.40%/5МВ

(ddddddddddddddddddddddddddd |54.39%/2MB hagrid---------1 |

I - no log I

Вот как генерировался этот вывод:

use DBI;

Sad.Tiin = 'sa':

print "Введите паролэ дл? $adn:r-:

cho'iip($ow = <'STOIN,.-).

$dbh = DBI->connect('dbi:Sybase:',$admin,$pw);

die "Невозможно соединиться: $DBI::errstr\n" unless (defined $dbh);

ft получаем имена баз данных на сервере

$sth = $dbh->prepare(q{SELECT name from sysdatabases}) or

die "Невозможно подготовить запрос к sysdatabases: ".$abh->errstr."\n": $sth->execute or

die "Невозможно выполнить запрос к sysdatabases: ".$dbh->errstr. "\n":

while ($aref = $sth->fetchrow_arrayref) { push(@dbs, $aref->[0]);




}

$sth->finish;

tt

получаем состояние для каждой из баз данных foreach $db (@dbs) {

ff получаем и суммируем значения из поля size для всех

ft сегментов, не относящихся к журналам регистрации

$size = &querysum(qq{SELECT size FROM master.dbo.sysusages

WHERE dbid = db_id(\'$db\')

AND segmap != 4});

ft получаем и суммируем значения из поля size для сегмента,

ft соответствующего журналам регистрации $logsize = &querysum(qq

{SELECT size FROM master.dbo.sysusages

WHERE dbid = db_id(\'$db\')

AND segmap =4}):

ft переходим к другой базе данных и получаем информацию об

ft используемом пространстве $dbh->do(q{use $db}) or

die "Невозможно изменить базу данных на $db: ".$dbh->errstr."\n":

ft мы использовали функцию reserved_pgs, чтобы вернуть

ft количество страниц, используемых под данные (doampg) и

индекс (ioampg)

$used=&querysum(q{SELECT reserved_pgs(id,doampg)+reserved_pgs(id.ioampg)

FROM sysindexes

WHERE id != 8});

ft то же самое, только на этот раз получаем информацию по

ft журналам регистрации

$logused=&querysum(q{SELECT reserved_pgs(id, doampg)

FROM sysindexes

WHERE id=8}):

ft выводим информацию в графическом виде

&graph($do,Ssize,Slogsize,$used,Slogused): } $dbh->disconnect:

ft готовим/выполняем запрос SELECT, получаем сумму результатов sub querysum

 {

my($query) = shift; rm/($sth $aref, Ison);

$sth = $dbh->prepare($query) or

die "Невозможно подготовить запрос $que-~, .

Sdbn- -errstr.

 $sth->execute or

die "Невозможно выполнить запрос Sqjery ".

while ($aref=$stn->fetcnrow_arrayref) {

$sum += $aref->[0]: } $sth->finish;

$sum; }

# выводим в виде диаграммы имя базы данных, ее размер, размер журнала

 регистрации и информацию об использовании пространства sub graph

{

my($dbname,Ssize,Slogsize,Sused,Slogused) = @_;

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

tt

использованное пространство и общий обьем, отведенный под данные



printf("%.2f", ($used/$size-100)V,

print "%/". (($size * $pages)/1024)."MB\n";

print Sdbname.'-'x(14-length($dbname)).'-|'.(' 'x 49)."|\n";

if (defined Slogsize) { n строка для информации об

tt

использовании пространства под журналы регистрации

print ' 'х15 . "|- no log".(' 'х 41)."|\п": }

print "\n"; }

Читатель, разбирающийся в SQL, вероятно, удивится, зачем использовать специальную подпрограмму для суммирования данных из одного столбца вместо того, чтобы применить отличный оператор SUM из SQL. querysum() придумана только в качестве примера того, что можно сделать на лету из Perl. Подпрограмма на Perl подходит, скорее, для более сложных задач. Например, если нужно отдельно просуммировать данные, выбирая их по регулярному выражению, лучите

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



Где выполняется вся работа?



Вопрос, который может возникнуть при написании SQL-программ из Perl, звучит так: «Нужно ли обрабатывать данные на сервере при помощи SQL или на клиенте при помощи Perl? » Часто SQL-функции на сервере (например, SUMO) и операторы Perl пересекаются.

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

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

  • Насколько эффективно сервер обрабатывает определенный запрос?


  • Сколько данных обрабатывается?


  • Сколько нужно обрабатывать данные и насколько сложна эта обработка?


  • Каковы скорость сервера, клиента и сети (если она используется)?


  • Хотите ли вы, чтобы код можно было перенести на другой сервер баз данных?


  • Зачастую приходится испытать оба способа, прежде чем сделать выбор.



    Наблюдение за использованием процессорного времени на SQL-сервере





    В последнем примере этой главы DBI будет выводить обновляемую раз в минуту строку состояния, содержащую информацию об использовании процессорного времени на SQL-сервере. Чтобы сделать задачу более интересной, можно из одного и того же сценария одновременно наблюдать за двумя серверами. Комментарий к сценарию последует позже:

    use DBI:

    Ssyadmin = "sa':

    print "Пароль администратора Sybase:

    chomp($sypw = <STDltJ>) Smsadmin = "sa";

    print "Пароль администратора базы данных MS-SOL' ";

    chomp($mspw = <STDIN>);

    ft соединяемся с сервером Sybase

    Ssydbh = DBI->connect("dbi:Sybase:server=SYBASE",Ssyadmin.Ssypw);

    die "Невозмржно соединиться с сервером

    Sybase: $D8I::errstr\n" unless (defined Ssydbh);

    # включаем параметр ChopBlanks, чтобы удалить концевые пробелы из столбцов

    $sydbh->{ChopBlanks} = 1;

    п

    соединяемся с сервером MS-SOL (очень здорово, что мы можем

    # использовать для этого OBD::Sybase! )

    Smsdbh = DBI->connect("dbi:Sybase:server=MSSQL",Smsadmin,Smspw);

    die "Невозможно соединиться с сервером mssql: $DBI::errstr\n" unless (defined Smsdbh);

    # включаем параметр ChopBlanks, чтобы удалить концевые пробелы $msdbh->{ChopBlanks} = 1;

    $1=1; # выключаем буферизацию вывода STOOUT

    и инициализируем обработчик сигнала с тем, чтобы можно было

    # корректно завершиться $SIG{INT} = suu (Sbyebye = 1;};

    и бесконечный цикл, который завершится при установке

    ft нашего флага прерывания while (1) {

    last if (Sbyebye);

    и запускаем хранимую процедуру sp_monitor Ssystn = $sydbh->prepare(q{sp_monitor» or

    die "Невозможно подготовить sy sp_monitor:".$sydbh->errstr."\n";

    $systh->execute or

    die "Невозможно выполнить sy sp_monitor;".$sydbh->errstr."\n";

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

    # получаем информацию

    cpu_busy wbile($href = $systh->fetchrow_hasnref or

    $systh->{syb^more_results}) {



    К есть то, что нужно, перестаем спрашивать

    last if (defined $href->{cpu_busy}); ! $systh->firush;

    ft заменяем все, кроме %, значениями, которые мы

    в получили

    for (keys %{$href}) <

    $nref->{$_} =" s/. '-(\d+%)/\V; }

    # собираем все нужные нам данные в одну строку

     $info - "Sybase: (". $nref-Xcpu_busy}. "

    CPU), ". "(".$href->{io_busy}." 10), ". "(".$href->{idle}." idle) ";

     отлично, тпг.ерь сделаем то же самое и для другого сервера

     (MS-SQL)

    Smssth = $msdbh->prepare(q{sp_monitor}) or

    die "Невозможно подготовить ms spjnonitor;".

    $o)sdoh->errstr. "\л': $Disstb->execute or

    die "Невозможно выполнить ms spjronitor:".

    $nsdbn->errstr."\n": while($href = $mssth->

    fetchrow^hashref or $mssth->{syb_more_results})

    {

    # есть то, что нужно, перестаем спрашивать

    last if (defined $href->{cpu_busy»:

    }

    $mssth->finish;

    # заменяем все, кроме % for (keys %{$href» {

    Sirifo .= "MSSQL: (" . $href->{' cpu_busy'}." CPU), ".

    "C'.$href->{'io_busy'}." 10), ".

    "(".$riref->{'idle'}." idle)"; print " "x78,"\r"; print $info,"\r";

    sleep(5) unless (Sbyebye); }

    # попадаем сюда, только если мы прервали цикл

    $sydbh->disconnect;

    $msdbh->disconnect;

    Сценарий выводит эту строку на экран и обновляет ее каждые пять секунд:

    Sybase: (33% CPU), (33% 10), (0% idle) MSSQL: (0% CPU), (0%!0), (100% idle)

    Основу данной программы составляет хранимая процедура

    sp_mon:tor,

    существующая как на Sybase-, так и на MS-SQL-сервере.

    Вывод sp_mo-nitor выглядит примерно так:

    last_run current_run seconds

    Аид 3 1998 12:05АМ Аид 3 1998 12:05АМ 1

    cpu_busy io_busy idle

    0(0)-0% 0(0)-0% 40335(0)^0%

    packets^received packets_sent packet_errors

    1648(0) 1635(0) 0(0)

    total_read total_write total_errors connections

    391(0) 180(0) 0(0) 11(0)

    К сожалению, sp_monitor показывает непереносимую особенность Sybase, которая прекочевала к MS-SQL: множественные наборы результатов. Каждая из строк возвращается в виде отдельного результата. Модуль DBD: : Sybase справляется с этим, устанавливая специальный атрибут команды. Вот как возникла эта проверка:



    while($href = $systh->fetchrow_hashref or

    $systh->{syb_more_results}) {

    и вот почему следовало выйти из цикла до того, как были замечены нужные поля:

    # есть то, что нужно, перестаем спрашивать

    last if (defined $href->{cpu_busy});

    Сама программа будет выполняться в вечном цикле до тех пор, пока не получит сигнал прерывания (наиболее вероятно, что это будет нажатие клавиш <Ctrl>+<C> пользователем). Получив такой сигнал, мы делаем самую безопасную вещь из тех, что можно сделать с обработчиком сигнала, и устанавливаем флаг возврата. Подобную технологию рекомендуют использовать на страницах perlipc для безопасной обработки сигналов. После получения сигнала INT будет установлен соответствующий флаг, который выбросит нас из цикла на следующей итерации. Получение этого сигнала позволяет программе «деликатно» закрыть дескрипторы баз данных, перед тем как сбросить «этот бренный шум».

    Эта небольшая программа всего лишь затронула возможности наблюдения за состоянием сервера, доступные нам. Было бы несложно, взяв полученные от sp_monitor результаты, построить график, чтобы получить более наглядное представление о том, как используется сервер. Но... оставим заботы об украшательстве читателю.

     


    Содержание раздела