пятница, 13 февраля 2009 г.

Перенос дампа большой базы с MySQL на PostgreSQL


Перенести большую базу (размер дампа около 10 Гб) с MySQL на PostgreSQL просто удалением несовместимостей в дампе, созданном mysqldump, и накатыванием его на psql не получается - процесс заливки дампа занимает несколько суток. Далее приводится пошаговое руководство по сокращению этого времени до пары часов.
  1. создаем дамп без данных (mysqldump -d --skip-opt --compatible=postgresql);
  2. накатывам этот дамп на psql;
  3. дампим полученнную базу - и делим полученный файл дампа на две части - в первой - только операторы CREATE TABLE, а ALTER TABLE ... ADD CONSTRAINT и CREATE INDEX - во второй. В файле дампа они расположены строго последовательно. Смысл этой операции в том, чтобы заливать данные в базу без индексов, а только потом создать индексы - так получится в разы быстрее;
  4. создаем базу заново - и накатываем на нее схему без индексов;
  5. для дампа данных придется писать скрипт. Смысл его такой: выбираем все поля table_name в таблице information_schema.tables, где table_schema равна имени нашей переносящейся базы - и для каждой из этих таблиц запускам select * into outfile;
  6. заливаем эти файлы в psql с помощью оператора copy from;
  7. накатываем вторую часть схемы - индексы - и наша база перенесена.

понедельник, 2 февраля 2009 г.

строим netflow-proxy



Задача: принимать поток netflow и передавать дальше только пакеты, соответствующие определенным условиям. Возможно решение с помощью пакета nfdump
Нам интересны три утилиты из этого пакета. Первая и главная - nfcapd - демон который будет принимать netflow-поток и сохранять его в файлы.
Важные аргументы:
  • -l dirname - директория, где будем эти файлы хранить
  • -t - интервал ротации файлов в секундах (по умолчанию - 600)
  • -x команда (в кавычках), запускаемая после ротации каждого файла. Допустимы символы расширения. Практически важен %f - имя новосозданого файла.
  • -R host/port - адрес для пересылки сдублированного потока - сама по себе интересная возможность, но нам нужна более сложная конфигурация - и мы будем возиться с предыдущей опцией. Для ее полезного использования нам нужны еще две команды из пакета:
nfdump - очень похожа на всем знакомую tcpdump (только выражения фильтра нужно брать в кавычки). Необходимые аргументы:
  • -r - файл, из которого читаем (т.е. созданный nfcapd)
  • -w - файл, куда пишем netflow (- означает stdout). Если аргумент пропущен, получаем на выходе не netflow, а человеко-читаемый вывод. Полезно для тестирования.
  • -f filename - имя файла, содержащего выражение фильтра (может быть разбито на несколько строк)
И - последний штрих - команда nfreplay - посылает netflow из файла снова в сеть. Важные аргументы:
  • -H - адрес, куда будем слать netflow
  • -r filename - файл, содержащий пакеты для посылки (по умолчанию - stdin)
  • -v [59] - версия netflow
Теперь собираем все в кучу. Вызывам nfcapd с опицией -x '/path/to/myscript %f', а в этот скрипт помещаем что-то вроде:
nfdump -r $1 'src net my.ney.wo.rk/24' -w - | nfreplay -H my.nf.col.lector
rm $1

Перед тем, как подать на систему нагрузку, тестовые пакеты netflow можно создать с помощью ipcad

среда, 21 января 2009 г.

Ручная доработка CentOS 5.2 под дисковый контроллер Fibrochannel QLogic


Собственно проблемы большой нет - CentOS 5.2 из коробки успешно загружается на Blade-сервере IBM с контроллером QLogic QMI2472 - беда в том, что длится загрузка на этом железе примерно 13 (!) минут. Больше половины этого времени занимает device-mapper, вызываемый из /etc/rc.sysinit, - он что-то пытается сделать с дисками массива, недоступными данному хосту. Никаких правильных путей его загасить я не нашел - потому грубо правил сам файл - строку:
if [ -c /dev/mapper/control ]; then
заменил на:
if [ 0 = 1 -a -c /dev/mapper/control ]; then
(или у кого какие еще варинты заведомо невыполнимого условия?) - после этого загрузка явно пошустрела.
Еще одна не очень приятная особенность CentOS - она монтирует файловые системы не по имени устройства, а по LABEL (причем, LABEL - это просто точка монтирования - /, /var и т. д.) Если у нас на контроллере есть диски для нескольких хостов - то как быть? Быть так. Метку правим такой командой:
# tune2fs -L MY-UNIQUE-LABEL /dev/sd??
и потом исправляем метку со стандартной на нашу в файлах /etc/fstab (первый столбец) и /boot/grub/menu.lst (строка kernel аргумент root=LABEL=MY-UNIQUE-LABEL).

вторник, 13 января 2009 г.

Борьба с дублированием писем в Postfix/ClamSMTPD

Проблема обнаружилась такая: на postfix есть clamsmtpd и virtual_aliases_map, в которой есть запись вида:
ivanov@mydomain.ru: ivanov@mydomain.ru, petrov@mydomain.ru
(т.е. почта Иванова посылается ему - и еще Петрову), так вот, в такой конфигурации Иванов получает все нормально, а Петров - в трех экземплярах. Методом чтения инета - и научного тыка - найдено следующее решение:
в main.cf добавляем строку:
receive_override_options = no_address_mappings
в master.cf в этих двух строках добавляем выделенные красным опции:
smtp inet n - n - - smtpd -o content_filter=dummy
-o receive_override_options=


127.0.0.1:10026 inet n - n - - smtpd

-o content_filter=
-o receive_override_options=no_unknown_recipient_checks, no_header_body_checks, no_address_mappings
-o smtpd_helo_restrictions=
-o smtpd_sender_restrictions=
-o smtpd_recipient_restrictions=permit_mynetworks,reject
-o mynetworks_style=host
-o smtpd_authorized_xforward_hosts=127.0.0.0/8
(пробелы между добавлены в строку receive_override_options, чтобы поместилась в блоге - в нормальном файле этих пробелов нет; строки, продолжающие команду вызова (начинающиеся с ), должны предваряться символом табуляции).

четверг, 25 декабря 2008 г.

Тюнинг DBD::mysql


Ниже описаны два параметра соединения с сервером БД MySQL с помощью модуля DBD::mysql, которые могут использоваться для повышения производительности.
  1. mysql_server_prepare - операторы сохраняются на стороне сервера.
  2. Активируется с помощью добавления выражения mysql_server_prepare=1 к DSN, напр.:


    $dbh = DBI->connect('DBI:mysql:db1:localhost;mysql_server_prepare=1', 'user', 'pass');


    По идее такой подход должен повышать производительность массовых запросов. Проверяем на заведомо слабой машине (сервер и клиент на одном и том же компьютере). Миллион insert выполняется быстрее на 11%, миллион update - на 19%, миллион delete - на 11%. Стоило оно того? Не знаю. Но вроде противопоказаний не обнаружено.


  3. mysql_use_result - результат запроса не сохраняется на клиенте перед использованием (умолчательное поведение называется mysql_store_result). Может быть применено:
    1. на уровне database handler: добавлением mysql_use_result=1 к DSN или оператором

      $dbh->{mysql_use_result}=1;

    2. или на уровне statement handler - в операторе:

      $dbh->prepare('select * from bigtable', {mysql_use_result => 1});

      или (после prepare и до execute) оператором:

      $sth->{mysql_use_result} = 1;

    Пользу от такого поведения можно почуствовать, если оператор возвращает очень много записей - порядка нескольких миллионов. При умолчательном mysql_store_result perl (по крайней мере, v 5.8.8 на FreeBSD 7.0) пожирает всю доступную память - и умирает, а при mysql_use_result - ничего, живет - и даже обходится не очень большим количеством памяти. К недостаткам этого метода работы с результатом man-страница DBD::mysql (3) относит

    tends to block other processes,

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

четверг, 18 декабря 2008 г.

Обработка сигнала ALRM в Perl


Если мы запускаем из скрипта на Perl какую-то внешнюю программу, может случиться так, что программа эта насмерть зависнет - и следующая после system строчка скрипта никогда не выполнится. Бороться тут можно двумя способами - запуском задания в фоне и обработкой сигнала ALRM. Первый способ не рассматриваем по причине его очевидности (& писать все умеют - fork/exec тоже всем известен), второй немножко интереснее. Простой пример кода:
alarm 60;
system "/very/slow/command";
alarm 0; # это мы сбрасываем будильник, если он не сработал
Если команда, вызванная system, провозится больше минуты, то скрипт аварийно завершится с сообщением "Alarm clock". Если такое поведение нас не устраивает (т.е. зависание дочерней программы не является фатальным), то все несколько сложнее. Последовательность alarm/system/alarm 0 нужно поместить внутрь оператора
eval {}; - и поместить в начало этого блока собственную обработку сигнала ALRM - в самом простом случае такую:
local $SIG{ALRM} = sub {die "Signal ALRM fired\n"};
Этот die не приведет к выходу из программы - только завершит оператор eval {}; - что нам и требовалось. Еще необходимо учесть один момент - описанная схема - прекрасный генератор zombie-процессов. Для борьбы с ними где-то в программе нужно поместить такой цикл:
use POSIX ':sys_wait_h';
while (waitpid(-1, WNOHANG) > 0) {};
Подробнее про работу с сигналами можно почитать в perlipc (1).

среда, 26 ноября 2008 г.

Bounce report на русском языке в Postfix


Сама идея посылки сообщений от SMTP-сервера пользователю на любом языке, кроме английского, лично мне кажется в корне неверной, но иногда очень просят ;( Тогда приступаем.
Шаблоны сообщений сервера (всего их 4: failure_template, delay_template, success_template и verify_template) лежат в файле, описывамом в main.cf как bounce_template_file
т.е. добавляем к нашему конфигу примерно такую строку:
bounce_template_file = /etc/postfix/bounce.cf

Теперь дело за малым - создать сам файл. Шаблоны по умолчанию (чтобы было, что править) можно получить либо командой:
$ postconf -b
либо лично у меня уже был файлик bounce.cf.default в директории с конфигами postfix
Пишем в наш файл что-то типа:

success_template = <
EOF
Charset: windows-1251

From: MAILER-DAEMON (Mail Delivery System)
Subject: Successful Mail Delivery Report

Ваше письмо успешно доставлено получателю.
Подробности приведены ниже.
С уважением, Программа Postfix на сервере $myhostname
--------------------------------------------------------------------
EOF

(...и три других шаблона в том же духе).

Прошу обратить внимание, что указанный charset действует только в теле письма, а не в заголовках - они должны быть в гольном ascii. За справкой о прочих возможностях/невозможностях оращайтесь к man-странице bounce(5) - там все довольно неплохо описано.