Советы для разработчика по работе с MySQL

1. Оптимизируйте запросы для кэша запросов

У большинства MySQL серверов включено кэширование запросов. Один из наилучших способов улучшения производительности — просто предоставить кэширование самой базе данных. Когда какой-либо запрос повторяется много раз, его результат берется из кэша, что гораздо быстрее прямого обращения к базе данных.
Основная проблема в том, что многие просто используют запросы, которые не могут быть закэшированны:

      // запрос не будет кэширован
      $r = mysql_query("SELECT username FROM user WHERE signup_date >= CURDATE()");
      // а так будет!
      $today = DATE("Y-m-d");
      $r = mysql_query("SELECT username FROM user WHERE signup_date >= '$today'");

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

2. Используйте EXPLAIN для ваших запросов SELECT

Используя EXPLAIN, вы можете посмотреть, как именно MySQL выполняет ваш запрос. Это может помочь вам избавиться от слабых мест производительности и других проблем в вашем запросе или в структуре таблиц.
Результат EXPLAIN покажет вам, какие используются индексы, как выбираются и сортируются таблицы и т.д.
Возьмите ваш SELECT запрос (он может быть сложным, с объединениями) и добавьте в начало ключевое слово EXPLAIN. Для этого вы можете использовать phpmyadmin. В результате вы получите очень интересную таблицу. Для примера, пусть я забыл добавить индекс в таблицу, которая участвует в объединении:

Explain для неоптимизированного запроса

После добавления индекса для поля group_id:
Explain после оптимизации запроса

Теперь вместо 7883 строк, выбираются только 9 и 16 строк из двух таблиц. Перемножение всех чисел в столбце rows даст число прямо пропорциональное производительности запроса.

3. LIMIT 1, когда нужна единственная строка

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

      // есть пользователи в Alabama?
      // можно так:
      $r = mysql_query("SELECT * FROM user WHERE state = 'Alabama'");
      IF (mysql_num_rows($r) > 0) {
      // ...
      }
      // но так лучше:
      $r = mysql_query("SELECT * FROM user WHERE state = 'Alabama' LIMIT 1");
      IF (mysql_num_rows($r) > 0) {
      // ...
      }

4. Индексируйте поля, по которым ищите

Индекс это не только основной или уникальный ключ. Это так же любые столбцы в таблице, которые вы используете для поиска и их можно проиндексировать.

Результат использования индекса

Как вы можете заметить, это правило также применимо для части строк, например — «last_name LIKE ‘a%’». При поиске с начала строки, MySQL использует индекс этого столбца.
Вы так же должны понимать, что это не сработает для регулярных выражений. Например, когда вы ищите слово (т.е. «WHERE post_content LIKE ‘%apple%’»), то от обычного индекса не будет никакого толку. Лучше будет использовать полнотекстовый поиск или создать вашу собственную систему индексации.

5. Индексируйте поля для объединения и используйте для них одинаковые типы столбцов

Если ваше приложение содержит много объединений таблиц, вам необходимо проиндексировать в обеих таблицах поля, используемые для объединения. Это повлияет на то, как MySQL делает внутреннюю оптимизацию объединений.
Так же эти столбцы должны быть одного типа. Например, если вы объединяете столбец DECIMAL со столбцом INT из другой таблицы, MySQL не сможет использовать хотя бы один из индексов. Даже кодировки символов должны быть одного типа для строковых столбцов.

      // выборки компаний в штате пользователя
      $r = mysql_query("SELECT company_name FROM users
      JOIN companies ON (users.state = companies.state)
      users.id = $user_id");
      // обе колонки state должны быть проиндексированны
      // они обе должны иметь один тип данных и кодировку символов
      // а иначе MySQL сделает полную выборку из этих таблиц

6. Не используйте ORDER BY RAND()

(Имеется в виду выборка единственной строки. Примечание переводчика)

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

Таким образом вы выберите случайный номер, который меньше количества строк и используете его для смещения в LIMIT.

7. Избегайте SELECT *

Чем больше данных считывается из таблицы, тем медленнее запрос. Это увеличивает время работы с хранилищем данных. Также, когда сервер базы данных установлен отдельно от web-сервера, будет большая задержка при передаче данных по сети.
Прописывать, какие именно столбцы из запроса вам нужны — хорошая привычка.

      // не очень хорошо:
      $r = mysql_query(«SELECT * FROM USER WHERE user_id = 1»);
      $d = mysql_fetch_assoc($r);
      echo «Welcome {$d['username']}»;
 
      // лучше:
      $r = mysql_query(«SELECT username FROM USER WHERE user_id = 1»);
      $d = mysql_fetch_assoc($r);
      echo «Welcome {$d['username']}»;
 
      // разница более значительна при большем наборе данных.

8. Старайтесь всегда создать поле ID

В каждой таблице нужно поле id, которое будет PRIMARY KEY, AUTO_INCREMENT, а так же иметь тип INT. Так же неплохо, чтобы оно было UNSIGNED, т.к. вряд ли у идентификатора будут отрицательные значения.
Даже если в вашей таблице пользователей есть уникальное поле username, не делаете его основным ключом. Использование поля VARCHAR, как основного ключа, очень медлительно. Да и структура вашего кода, относящаяся к пользователям, будет гораздо лучше, если у каждого пользователя будет свой внутренний идентификатор.
Есть так же и внутренние операции MySQL, использующие первичный ключ. И это становиться очень важно для более сложных конфигураций базы данных (кластеры, распараллеливание и т.д.)
Исключение из этого правила составляют «таблицы ассоциаций», используемые для связи «многие-ко-многим» между 2 таблицами. Например, таблица «posts_tags», содержит 2 поля: post_id, tag_id, который используется для объединения между двумя таблицами «Posts» и «Tags». Эта таблица будет иметь первичный ключ составленный из 2 полей.

9. Используйте ENUM вместо VARCHAR

ENUM — очень быстрый и компактный тип поля. Значения в нем храниться так же, как TINYINT, но отображаются как в строковом поле. Это делает его незаменимым в некоторых случаях.
Если у вас есть поле, в котором будет вполне определенный набор значений, используйте ENUM вместо VARCHAR. Например, если есть поле «status», его значения могут быть «active», «inactive», «pending», «expired» и т.д.
Можно даже получить от MySQL «совет» о том, как перестроить таблицу. Если у вас есть поле VARCHAR, MySQL может предложить заменить его на ENUM. Для этого используется PROCEDURE ANALYSE(), описанная ниже.

10. Используйте подсказки от PROCEDURE ANALYSE()

PROCEDURE ANALYSE() анализирует структуру вашей таблицы и данные в ней, и выдает возможные советы по оптимизации. Это возможно только при наличии реальных данных в таблице, т.к. анализ делается в основном на их основе.
Например, если вы создали первичный ключ типа INT, а записей не очень много, MySQL может предложить заменить его на MEDIUMINT. Или, если используется VARCHAR в котором есть несколько уникальных значений, будет предложен ENUM.
В phpmyadmin в структуре таблице есть ссылка «Анализ структуры таблицы», результат которой может быть, например, следующим:

Подсказки в phpmyadmin от работы функции PROCEDURE ANALYSE()

11. Используйте NOT NULL, если это возможно

Если есть особые причины использовать NULL — используйте его. Но перед этим спросите себя — есть ли разница между пустой строкой и NULL (для INT — 0 или NULL). Если таких причин нет, используйте NOT NULL.
NULL занимает больше места и, к тому же, усложняет сравнения с таким полем. Избегайте его, если это возможно. Тем не менее, бывают веские причины использовать NULL, это не всегда плохо.
Из документации MySQL:
«Столбцы NULL занимают больше места в записи, из-за необходимости отмечать, что это NULL значение. Для таблиц MyISAM, каждое поле с NULL занимает 1 дополнительный бит, который округляется до ближайшего байта».

11. Используйте NOT NULL, если это возможно

Если есть особые причины использовать NULL — используйте его. Но перед этим спросите себя — есть ли разница между пустой строкой и NULL (для INT — 0 или NULL). Если таких причин нет, используйте NOT NULL.
NULL занимает больше места и, к тому же, усложняет сравнения с таким полем. Избегайте его, если это возможно. Тем не менее, бывают веские причины использовать NULL, это не всегда плохо.
Из документации MySQL:
«Столбцы NULL занимают больше места в записи, из-за необходимости отмечать, что это NULL значение. Для таблиц MyISAM, каждое поле с NULL занимает 1 дополнительный бит, который округляется до ближайшего байта».

12. Prepared Statements

Есть множество преимуществ в использовании prepared statements, как для безопасности, так и для улучшения производительности. Prepared statements фильтруют значения данных, добавляемых в запрос, что защищает запросы от SQL инъекций. Конечно, вы можете фильтровать переменные вручную, но тут может сказаться человеческая забывчивость и невнимательность. Конечно, это не столь важно при использовании какого-либо фреймворка или ORM.
Поскольку статья посвящена оптимизации, отмечу также выгоды для нее. Они проявляются, когда запрос выполняется много раз в приложении. Вы можете использовать для prepared statement разные значения, но MySQL будет разбирать запрос только один раз.
Кроме того, последние версии MySQL компилируют prepared statements в бинарную форму, что позволяет повысить эффективность.
Раньше многие программисты избегали prepared statements по одной единственной причине — они не кэшировались MySQL, но с версии 5.1 это не так.
Посмотрите mysqli extension для использования prepared statements или воспользуйтесь абстракцией базы данных, например, PDO.

      // создаем a prepared statement
      IF ($stmt = $mysqli->PREPARE("SELECT username FROM user WHERE state=?")) {
      // привязываем значения
      $stmt->bind_param("s", $state);
      // выполняем
      $stmt->EXECUTE();
      // привязываем результат
      $stmt->bind_result($username);
      // получаем данные
      $stmt->fetch();
      printf("%s is from %s\n", $username, $state);
      $stmt->close();
      }

13. Небуферизованные запросы

Обычно, делая запрос, скрипт останавливается и ждет результата его выполнения. Вы можете изменить это, используя небуферизованные запросы.
Хорошее описание есть в документации функции mysql_unbuffered_query():

«mysql_unbuffered_query() отправляет SQL-запрос в MySQL, не извлекая и не автоматически буферизуя результирующие ряды, как это делает mysql_query(). С одной стороны, это сохраняет значительное количество памяти для SQL-запросов, дающих большие результирующие наборы. С другой стороны, вы можете начать работу с результирующим набором срезу после получения первого ряда: вам не нужно ожидать выполнения полного SQL-запроса»

Однако есть определенные ограничения. Вам придется считывать все записи или вызывать mysql_free_result() прежде, чем вы сможете выполнить другой запрос. Так же вы не можете использовать mysql_num_rows() или mysql_data_seek() для результата функции.

14. Храните IP в UNSIGNED INT

Многие программисты хранят IP адреса в поле типа VARCHAR(15), не зная что можно хранить его в целочисленном виде. INT занимает 4 байта и имеет фиксированный размер поля.
Убедитесь, что используете UNSIGNED INT, т.к. IP можно записать как 32 битное беззнаковое число.
Используйте в запросе INET_ATON() для конвертирования IP адреса в число, и INET_NTOA() для обратного преобразования. Такие же, такие функции есть и в PHP — ip2long() и long2ip() (в php эти функции могут вернуть и отрицательные значения. замечание от хабраюзера The_Lion).

      $r = "UPDATE users SET ip = INET_ATON('{$_SERVER['REMOTE_ADDR']}') WHERE user_id = $user_id";

15. Таблицы фиксированного размера (статичные) — быстрее

Если каждая колонка в таблице имеет фиксированный размер, то такая таблица называется «статичной» или «фиксированного размера». Пример колонок не фиксированной длины: VARCHAR, TEXT, BLOB. Если включить в таблицу такое поле, она перестанет быть фиксированной и будет обрабатываться MySQL по-другому.
Использование таких таблицы увеличит эффективность, т.к. MySQL может просматривать записи в них быстрее. Когда надо выбрать нужную строку таблицы, MySQL может очень быстро вычислить ее позицию. Если размер записи не фиксирован, ее поиск происходит по индексу.
Так же эти таблицы проще кэшировать и восстанавливать после падения базы. Например, если перевести VARCHAR(20) в CHAR(20), запись будет занимать 20 байтов, вне зависимости от ее реального содержания.
Используя метод «вертикального разделения», вы можете вынести столбцы с переменной длиной строки в отдельную таблицу.

16. Вертикальное разделение

Вертикальное разделение — означает разделение таблицы по столбцам для увеличения производительности.
Пример 1. Если в таблице пользователей хранятся адреса, то не факт что они будут нужны вам очень часто. Вы можете разбить таблицу и хранить адреса в отдельной таблице. Таким образом, таблица пользователей сократиться в размере. Производительность возрастет.
Пример 2. У вас есть поле «last_login» в таблице. Оно обновляется при каждом входе пользователя на сайт. Но все изменения в таблице очищают ее кэш. Храня это поле в другой таблице, вы сведете изменения в таблице пользователей к минимуму.
Но если вы будете постоянно использовать объединение этих таблиц, это приведет к ухудшению производительности.

17. Разделяйте большие запросы DELETE и INSERT

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

      while (1) {
      mysql_query("DELETE FROM logs WHERE log_date <= '2009-10-01' LIMIT 10000");
      IF (mysql_affected_rows() == 0) {
      // удалили
      break;
      }
      // небольшая пауза
      usleep(50000);
      }

18. Маленькие столбцы быстрее

Для базы данных работа с жестким диском, возможно, является самым слабым местом. Маленькие и компактные записи обычно лучше с точки зрения производительности, т.к. уменьшают работу с диском.
В документации к MySQL есть список требований к хранилищам данных для всех типов данных.
Если ваша таблица будет хранить мало строк, то не имеет смысла делать основной ключ типом INT, возможно лучше будет сделать его MEDIUMINT, SMALLINT или даже TINYINT. Если вам не нужно хранить время, используйте DATE вместо DATETIME.
Однако будьте осторожны, что бы не вышло как с Slashdot.

19. Выбирайте правильный тип таблицы

Два основных типа таблиц — MyISAM и InnoDB, у каждого есть свои плюсы и минусы.
MyISAM хорошо считывает из таблиц большое количество данных, но он плох для записи. Даже если вы изменяете всего одну строку, блокируется вся таблица, и ни один процесс не может ничего из нее прочитать. MyISAM очень быстро выполняет запросы типа SELECT COUNT(*).
У InnoDB более сложный механизм хранения данных, и он может быть медленнее, чем MyISAM, для маленьких приложений. Но он поддерживает блокировку строк, что более эффективно при масштабировании. Так же поддерживаются некоторые дополнительные функции, такие операции как транзакции.
Подробнее:MyISAM Storage EngineInnoDB Storage Engine

20. Используте ORM

Используя ORM, можно получить определенную оптимизацию работы. Все, что можно сделать с помощью ORM, можно сделать и вручную. Но это требует дополнительной работы и более высокого уровня знаний.
ORM замечателен для «ленивой» загрузки данных. Это означает выборку данных по мере необходимости. Но необходимо быть осторожным, т.к это может привести к появлению множества маленьких запросов, что приведет к снижению производительности.
ORM также может объединять несколько запросов в пакеты, вместо отправки каждого отдельно.
Моя любимая ORM для PHP — Doctrine. Я уже писал статью об установке Doctrine в CodeIgniter.

21. Будьте осторожны с постоянными соединениями

Постоянные соединения предназначены для уменьшения расходов на установление связи с MySQL. Когда соединение создается, оно остается открытым после завершения работы скрипта. В следующий раз, этот скрипт воспользуется этим же соединением.mysql_pconnect() в PHPНо это звучит хорошо только в теории. Из моего личного опыта (и опыта других), использование этой возможности не оправдывается. У вас будут серьезные проблемы с ограничением по числу подключений, памятью и так далее.
Apache создает много параллельных потоков. Это основная причина, почему постоянные соединения не работаю так хорошо, как бы хотелось. Перед использованием mysql_pconnect() посоветуйтесь с вашим сисадмином.

Источник

Освобождаем консоль

Большинство Linux-приложений с графическим пользовательским интерфейсом при запуске из консоли не освобождают консоль, а запирают сесию и начинают выводить в ней свои диагностические сообщения. Нажатие Ctrl+C в этом случае приводит к остановке приложения.Сейчас я покажу как заставить свои приложения освобождать консоль.

Добавляем в функцию main() своего приложения следующий код:

      if (fork() != 0){
          exit(0);
      }
 
      close(0);
      close (1);
      close(2);
 
      int fd = open("/dev/null", O_RDWR);
      dup2(fd, 0);
      dup2(fd, 1);
      dup2(fd, 2);

Первые три строки создают новую копию процесса нашей программы и убивают родительский процесс. Затем с помощью функции close() закрываем все дескрипторы ввода-вывода, унаследованные от родительского процесса, создаем новые дескрипторы, адресованные в /dev/null, и присваиваем их своей программе.

Установка модулей Perl вручную и используя CPAN

Установка модулей для Perl требуется для работы различных утилит, применяемх в повседневной рутине администрирования. Установка Perl модулей вручную с удоволетворением всех зависимостей это довольно скучный и нудный процесс.

Установка Perl модулей с испоьльзованием CPAN намного более лучшее решение, так как все зависимости определятся и установятся автоматически. В это статье я опишу оба метода установки модулей.

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

<strong>Can’t locate</strong> XML/parser.pm in @INC (@INC contains:
/usr/lib/perl5/5.10.0/i386-linux-thread-multi
/usr/lib/perl5/5.10.0
/usr/local/lib/perl5/site_perl/5.10.0/i386-linux-thread-multi
/usr/local/lib/perl5/site_perl/5.10.0
/usr/lib/perl5/vendor_perl/5.10.0/i386-linux-thread-multi
/usr/lib/perl5/vendor_perl/5.10.0 /usr/lib/perl5/vendor_perl
/usr/lib/perl5/site_perl/5.10.0 .)

Установка Perl модулей вручную

Download Perl module

Перейдите на веб-сайт CPAN Search и найдите модуль который вам нужно скачать. В этом пример мы скачаем и установим модуль XML:Parser Perl. Я скачаю XML-Parser-2.36.tar.gz в /home/download

$ cd /home/download
$ gzip -d XML-Parser-2.36.tar.gz
$ tar xvf XML-Parser-2.36.tar
$ cd XML-Parser-2.36

Собираем модуль

$ perl Makefile.PL
Checking if your kit is complete…
Looks good
Writing Makefile for XML:Parser::Expat
Writing Makefile for XML:Parser
$ make
$ make test

Устанавливаем модуль

$ make install

Это простой модуль без зависимостей, поэтому он установился без проблем. Обычно, любой модуль Perl имеет несколько зависимостей. Ставить все модули поочередно описанным выше методом скучнейшая задача. Я рекомендую использовать для установки CPAN метод, описанный ниже. Вручную стоит собирать модули в случае отсутсвия подключения к интернету.

Автоматическая установка Perl модулей с использованием CPAN

Проверяем установлен ли CPAN

Для установки Perl модулей используя CPAN, убедитесь что команда cpan работает. В этом примере, модуль CPAN ещё не установлен.

$ cpan
-bash: cpan: command not found
$ perl -MCPAN -e shell
Can’t locate CPAN.pm in @INC (@INC contains:
/usr/lib/perl5/5.10.0/i386-linux-thread-multi
/usr/lib/perl5/5.10.0
/usr/local/lib/perl5/site_perl/5.10.0/i386-linux-thread-multi
/usr/local/lib/perl5/site_perl/5.10.0
/usr/lib/perl5/vendor_perl/5.10.0/i386-linux-thread-multi
/usr/lib/perl5/vendor_perl/5.10.0
/usr/lib/perl5/vendor_perl /usr/lib/perl5/site_perl/5.10.0 .).
BEGIN failed–compilation aborted.

Установка модуля CPAN с помощью yum

$ yum install perl-CPAN

Результат работы команды:

Loaded plugins: refresh-packagekit
updates-newkey                       | 2.3 kB     00:00
primary.sqlite.bz2                   | 2.4 MB     00:00
Setting up Install Process
Parsing package install arguments
Resolving Dependencies
Transaction Summary
=============================================================================
Install      5 Package(s)
Update       0 Package(s)
Remove       0 Package(s)
Total download size: 1.0 M
Is this ok [y/N]: y
Downloading Packages:
(1/5): perl-ExtUtils-ParseXS-2.18-31.fc9.i386.rpm     |  30 kB     00:00
(2/5): perl-Test-Harness-2.64-31.fc9.i386.rpm         |  70 kB     00:00
(3/5): perl-CPAN-1.9205-31.fc9.i386.rpm               | 217 kB     00:00
(4/5): perl-ExtUtils-MakeMaker-6.36-31.fc9.i386.rpm   | 284 kB     00:00
(5/5): perl-devel-5.10.0-31.fc9.i386.rpm              | 408 kB     00:00
 
Installing     : perl-ExtUtils-ParseXS                             [1/5]
Installing     : perl-devel                                        [2/5]
Installing     : perl-Test-Harness                                 [3/5]
Installing     : perl-ExtUtils-MakeMaker                           [4/5]
Installing     : perl-CPAN                                         [5/5]
 
Installed: perl-CPAN.i386 0:1.9205-31.fc9
Dependency Installed:
  perl-ExtUtils-MakeMaker.i386 0:6.36-31.fc9
  perl-ExtUtils-ParseXS.i386 1:2.18-31.fc9
  perl-Test-Harness.i386 0:2.64-31.fc9
  perl-devel.i386 4:5.10.0-31.fc9
Complete!

Настраиваем span

При первом вызове cpan вы должны указать некоторые конфигурационные параметры как показано ниже. Я покажу только важные параметры конфигурации. Значения по умолчанию принимаются нажатием клавиши enter.

$ cpan
Sorry, we have to rerun the configuration dialog for CPAN.pm due
to some missing parameters…
CPAN build and cache directory? [/root/.cpan]
Download target directory? [/root/.cpan/sources]
Directory where the build process takes place? [/root/.cpan/build]
Always commit changes to config variables to disk? [no]
Cache size for build directory (in MB)? [100]
Let the index expire after how many days? [1]
Perform cache scanning (atstart or never)? [atstart]
Cache metadata (yes/no)? [yes]
Policy on building prerequisites (follow, ask or ignore)? [ask]
Parameters for the ‘perl Makefile.PL’ command? []
Parameters for the ‘perl Build.PL’ command? []
Your ftp_proxy? []
Your http_proxy? []
Your no_proxy? []
Is it OK to try to connect to the Internet? [yes]
First, pick a nearby continent and country by typing in the number(s)
(1) Africa
(2) Asia
(3) Central America
(4) Europe
(5) North America
(6) Oceania
(7) South America
Select your continent (or several nearby continents) [] 5
(1) Bahamas
(2) Canada
(3) Mexico
(4) United States
Select your country (or several nearby countries) [] 4
(2) ftp://carroll.cac.psu.edu/pub/CPAN/
(3) ftp://cpan-du.viaverio.com/pub/CPAN/
(4) ftp://cpan-sj.viaverio.com/pub/CPAN/
(5) ftp://cpan.calvin.edu/pub/CPAN
(6) ftp://cpan.cs.utah.edu/pub/CPAN/
e.g. ‘1 4 5′ or ‘7 1-4 8[] 2-16
cpan[1]> o conf commit
commit: wrote ‘/usr/lib/perl5/5.10.0/CPAN/Config.pm’
cpan[2]> quit
No history written (no histfile specified).
Lockfile removed.

Установка Perl модулей с использованием CPAN

Вы можете использовать один из указанных тут методов для установки новых модулей:

$ /usr/bin/perl -MCPAN -einstall Email::Reply’
(или)
$ cpan
cpan shell — CPAN exploration and modules installation (v1.9205)
ReadLine support available (maybe install Bundle::CPAN or Bundle::CPANxxl?)
cpan[1]> install “Email::Reply”;

В результате выполнения команды вы увидите

CPAN: Storable loaded ok (v2.18)
Going to read /root/.cpan/Metadata
Database was generated on Mon, 15 Sep 2008 11:02:52 GMT
Running install for module 'Email::Reply'
Running make for R/RJ/RJBS/Email-Reply-1.202.tar.gz
CPAN: LWP::UserAgent loaded ok (v2.036)
CPAN: Time::HiRes loaded ok (v1.9711)
Fetching with LWP:
ftp://carroll.cac.psu.edu/pub/CPAN/authors/id/R/RJ/RJBS/Email-Reply-1.202.tar.gz
CPAN: checksum security checks disabled because Digest::SHA not installed.
Please consider installing the Digest::SHA module.
CPAN: Compress::Zlib loaded ok (v2.008)
Email-Reply-1.202/
Email-Reply-1.202/Changes
CPAN: File::Temp loaded ok (v0.18)
Warning: prerequisite Email::Abstract 2.01 not found.
Warning: prerequisite Email::MIME::Creator 1.41 not found.
Writing Makefile for Email::Reply
---- Unsatisfied dependencies detected during ----
----       RJBS/Email-Reply-1.202.tar.gz      ----
Email::Abstract [requires]
Email::MIME::Creator [requires]
Shall I follow them and prepend them to the queue
of modules we are processing right now? [yes]
[Note: CPAN automatically detects that Email::Abstract
       and Email::MIME::Creator is required]
Fetching with LWP:
ftp://server/pub/CPAN/authors/id/R/RJ/RJBS/Email-Abstract-2.134.tar.gz
make — OK
make install  — OK
Fetching with LWP:
ftp://server/pub/CPAN/authors/id/R/RJ/RJBS/Email-MIME-Creator-1.454.tar.gz
make — OK
make install  — OK
Warning: prerequisite Email::Simple::Creator 1.4 not found.
Writing Makefile for Email::MIME::Creator
Email::Simple::Creator [requires]
Shall I follow them and prepend them to the queue
of modules we are processing right now? [yes]
[Note: CPAN automatically detects that Simple::Creator is required]
Fetching with LWP:
ftp://carroll.cac.psu.edu/pub/CPAN/authors/id/R/RJ/RJBS/Simple-Creator.tar.gz
make — OK
make install  — OK
Fetching with LWP:
CPAN.pm: Going to build R/RJ/RJBS/Email-Reply-1.202.tar.gz
make — OK
make install  — OK

В примере выше Email::Reply зависит от нескольких других модулей. CPAN автоматически определил зависимости и установил Email::Reply и все другие необходимые модули.

Установка расширений PECL

Введение в установку PECL

» PECL — это репозиторий расширений PHP, которые доступны вам через систему » PEAR. Эта часть руководства предназначена для демонстрации того, как вы можете получить и установить расширения PECL.

Эти инструкции подразумевают, что /your/phpsrcdir/ является путем к каталогу с дистрибутивом исходного кода PHP, а extname — это имя расширения PECL. Поэтому, приведем в соответствие. Эти инструкции также подразумевают знакомство с » командой pear. Информация в руководстве PEAR для команды pear также применима для команды pecl.

Для того, чтобы расширение можно было использовать, оно должно быть собрано, установлено и загружено. Методы, описанные ниже, предоставляют вам различные рекомендации по поводу того, как собрать и установить расширения, но сами они не будут автоматически загружены. Расширения могут быть загружены через добавление директивы extension в файл php.ini, или путем использования функции dl().

В процессе сборки модулей PHP важно, чтобы у вас были правильные версии необходимых утилит (autoconf, automake, libtool и т.д.). Информацию об этих утилитах и их версиях можно посмотреть тут » Anonymous CVS Instructions.

Загрузка расширений PECL

Есть несколько вариантов для загрузки расширений PECL, в том числе:

  • » http://pecl.php.net/Вебсайт PECL содержит информацию о различных расширениях, которые предоставлены PHP Development Team. Информация, доступная на этом вебсайте, включает в себя: лог изменений, новости релизов, требования и другие подобные детали.
  • pecl download extnameРасширения PECL, которые были опубликованы на сайте PECL, доступны для скачивания и установки через » команду pecl. Также, могут быть также указаны особые ревизии.
  • CVSБольшинство расширений PECL, также, находятся в CVS. Веб-интерфейс для просмотра доступен по адресу » http://cvs.php.net/pecl/. Для загрузки напрямую из CVS используется следующая последовательность команд. Стоит отметить, что пользователь cvsread имеет пароль phpfi:
    $ cvs -d:pserver:cvsread@cvs.php.net:/repository login
    $ cvs -d:pserver:cvsread@cvs.php.net:/repository co pecl/extname
  • Загрузка для Windows Пользователи Windows могут найти скомпилированные расширения PECL путем скачивания Collection of PECL modules со страницы » PHP Downloads, либо через » PECL Snapshot, либо расширения DLL на странице » PECL4WIN. Для компиляции PHP под Windows прочитайте соответствующий раздел
  • PECL для пользователей Windows

    Как и в случае DLL расширения PHP, установка проста и заключается в копировании файла DLL расширения PECL в директориюextension_dir и подключением затем его через php.ini. Например, добавьте следующую строку в ваш php.ini:

    extension=php_extname.dll

    После выполнения этих действий, перезапустите web-сервис.

    Компиляция общих расширений с помощью команды pecl

    PECL позволяет легко создавать общие расширения PHP. Используя » команду pecl, выполните следующее:

    $ pecl install extname

    Эта команда загрузит исходный код для расширения extname, скомпилирует и установит extname.so в вашу директорию extension_dir. Файл extname.so может быть затем загружен в php.ini

    По умолчанию, команда pecl не будет устанавливать пакеты, отмеченные состоянием alpha или beta. Если нет доступных стабильных версий пакетов, вы можете установить beta версию пакета, используя следующую команду:

    $ pecl install extname-beta

    Также, вы можете установить определенную версию используя такой вариант:

    $ pecl install extname-0.1

    Замечание: После подключения расширения в php.ini необходимо перезапустить web-сервис для того, чтобы изменения вступили в силу.

    Компиляция общих расширений с помощью phpize

    Иногда, использование инсталятора pecl не подходит. Это может быть связано с тем, что вы находитесь за файерволом или из-за того, что расширение, которое вы хотите установить, недоступно в PECL-совместимом пакете (к примеру, расширения из CVS, которые еще не зарелизены). Если вам необходимо собрать такое расширение, вы можете использовать низкоуровневые утилиты для выполнения сборки вручную.

    Команда phpize используется для подготовки окружения для расширения PHP. В следующем примере директория, где находятся исходные коды расширения, называется extname:

    $ cd extname
    $ phpize
    $ ./configure
    $ make
    # make install

    В случае успешной установки будет создан файл extname.so и помещен в PHP extensions directory. Вам будет необходимо добавить строку extension=extname.so в php.ini перед тем, как вы станете использовать это расширение.

    Если в системе отсутствует phpize, но существует возможность установки заранее скомпилированных пакетов (типа RPM), убедитесь, что установлена соответствующая devel версия пакета PHP, так как они часто содержат команду phpize с подходящими файлами заголовоков для сборки PHP и его расширений.

    Используйте phpize —help для просмотра дополнительной информации.

    Компиляция расширений PECL статически в PHP

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

    $ cd /your/phpsrcdir/ext
    $ pecl download extname
    $ gzip -d &lt; extname.tgz | tar -xvf -
    $ mv extname-x.x.x extname

    В результате будет создана следующая директория:

        /your/phpsrcdir/ext/extname

    После этого, выполните заново сборку конфигурационного скрипта PHP и затем соберите PHP как обычно:

    $ cd /your/phpsrcdir
    $ rm configure
    $ ./buildconf --force
    $ ./configure --help
    $ ./configure --with-extname --enable-someotherext --with-foobar
    $ make
    $ make install

    Замечание: Для запуска скрипта ‘buildconf’ вам необходимы autoconf версии 2.13 и automake версии 1.4+ (более новые версии autoconf могут работать, но это не поддерживается).

    Одна из двух опций —enable-extname или —with-extname используется в зависимости от расширения. Обычно, в случае, когда расширение не требует подключения внешних библиотек, используется —enable. Для того, чтобы убедиться в этом, выполните следующую команду после buildconf:

    $ ./configure --help | grep extname

    Умное Кеширование и Версионность в Javascript/CSS

    Подключая внешние CSS и Javascript, мы хотим снизить до минимума лишние HTTP-запросы.

    Для этого .js и .css файлы отдаются с заголовками, обеспечивающими надежное кеширование.

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

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

    Простое кеширование ETag

    Самый простой способ кеширования статических ресурсов — использование ETag.

    Достаточно включить соответствующую настройку сервера (для Apache включена по умолчанию) — и к каждому файлу в заголовках будет даваться ETag — хеш, который зависит от времени обновления, размера файла и (на inode-based файловых системах) inode.

    Браузер кеширует такой файл и при последующих запросах указывет заголовок If-None-Match с ETag кешированного документа. Получив такой заголовок, сервер может ответить кодом 304 — и тогда документ будет взят из кеша.

    Выглядит это так:

    Первый запрос к серверу (кеш чистый)

    GET /misc/pack.js HTTP/1.1
    Host: javascript.ru

    Вообще, браузер обычно добавляет еще пачку заголовоков типа User-Agent, Accept и т.п. Для краткости они порезаны.

    Ответ сервераСервер посылает в ответ документ c кодом 200 и ETag:

    HTTP/1.x 200 OK
    Content-Encoding: gzip
    Content-Type: text/javascript; charset=utf-8
    Etag: "3272221997"
    Accept-Ranges: bytes
    Content-Length: 23321
    Date: Fri, 02 May 2008 17:22:46 GMT
    Server: lighttpd

    Следующий запрос браузера
    При следующем запросе браузер добавляет If-None-Match: (кешированный ETag):

    GET /misc/pack.js HTTP/1.1
    Host: javascript.ru
    If-None-Match: "453700005"

    Ответ сервера
    Сервер смотрит — ага, документ не изменился. Значит можно выдать код 304 и не посылать документ заново.

    HTTP/1.x 304 Not Modified
    Content-Encoding: gzip
    Etag: "453700005"
    Content-Type: text/javascript; charset=utf-8
    Accept-Ranges: bytes
    Date: Tue, 15 Apr 2008 10:17:11 GMT

    Альтернативный вариант — если документ изменился, тогда сервер просто посылает 200 с новым ETag.

    Аналогичным образом работает связка Last-Modified + If-Modified-Since:

    1. сервер посылает дату последней модификации в заголовке Last-Modified (вместо ETag)
    2. браузер посылает дату закешированной версии в заголовке If-Modified-Since(вместо If-None-Match)
    3. Если скрипт не изменился — ответ содержит только код 304

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

    Умное кеширование. Версионность

    Общий подход для версионности — в двух словах:

    1. Во все скрипты добавляется версия (или дата модификации). Например, http://javascript.ru/my.js превратится в http://javascript.ru/my.v1.2.js
    2. Все скрипты жестко кешируются браузером
    3. При обновлении скрипта версия меняется на новую: http://javascript.ru/my.v2.0.js
    4. Адрес изменился, поэтому браузер запросит и закеширует файл заново
    5. Старая версия 1.2 постепенно выпадет из кеша

    Дальше мы разберем, как сделать этот процесс автоматическим и прозрачным.

    Жесткое кеширование

    Жесткое кеширование — своего рода кувалда которая полностью прибивает запросы к серверу для кешированных документов.

    Для этого достаточно добавить заголовки Expires и Cache-Control: max-age.

    Например, чтобы закешировать на 365 дней в PHP:

    header("Expires: ".gmdate("D, d M Y H:i:s", time()+86400*365)." GMT");
    header("Cache-Control: max-age="+86400*365);

    Или можно закешировать контент надолго, используя mod_header в Apache:

    Header add "Expires" "Mon, 28 Jul 2014 23:30:00 GMT"
    Header add "Cache-Control" "max-age=315360000"

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

    Большинство браузеров (Opera, Internet Explorer 6+, Safari) НЕ кешируют документы, если в адресе есть вопросительный знак, т.к считают их динамическими. Именно поэтому мы добавляем версию в имя файла. Конечно, с такими адресами приходится использовать решение типа mod_rewrite, мы это рассмотрим дальше в статье.P.S А вот Firefox кеширует адреса с вопросительными знаками.

    Автоматическое преобразование имен

    Разберем, как автоматически и прозрачно менять версии, не переименовывая при этом сам файлы.

    Имя с версией -> Файл

    Самое простое — это превратить имя с версией в оригинальное имя файла.

    На уровне Apache это можно сделать mod_rewrite:

    RewriteEngine on
    RewriteRule ^/(.*\.)v[0-9.]+\.(css|js|gif|png|jpg)$    /$1$2    [L]

    Такое правило обрабатывает все css/js/gif/png/jpg-файлы, вырезая из имени версию.

    Например:

    /images/logo.v2.gif -> /images/logo.gif
    /css/style.v1.27.css -> /css/style.css
    /javascript/script.v6.js -> /javascript/script.js

    Но кроме вырезания версии — надо еще добавлять заголовки жесткого кеширования к файлам. Для этого используются директивы mod_header:

    Header add "Expires" "Mon, 28 Jul 2014 23:30:00 GMT"
    Header add "Cache-Control" "max-age=315360000"

    А все вместе реализует вот такой апачевый конфиг:

    RewriteEngine on
    # убирает версию, и заодно ставит переменную что файл версионный
    RewriteRule ^/(.*\.)v[0-9.]+\.(css|js|gif|png|jpg)$ /$1$2 [L,E=VERSIONED_FILE:1]
     
    # жестко кешируем версионные файлы
    Header add "Expires" "Mon, 28 Jul 2014 23:30:00 GMT" env=VERSIONED_FILE
    Header add "Cache-Control" "max-age=315360000" env=VERSIONED_FILE

    Из-за порядка работы модуля mod_rewrite, RewriteRule нужно поставить в основной конфигурационный файл httpd.conf или в подключаемые к нему(include) файлы, но ни в коем случае не в .htaccess, иначе команды Header будут запущены первыми, до того, как установлена переменная VERSIONED_FILE. Директивы Header могут быть где угодно, даже в .htaccess — без разницы.

    Автоматическое добавление версии в имя файла на HTML-странице

    Как ставить версию в имя скрипта — зависит от Вашей шаблонной системы и, вообще, способа добавлять скрипты (стили и т.п.).

    Например, при использовании даты модификации в качестве версии и шаблонизатора Smarty — ссылки можно ставить так:

    <link href="{version src='/css/group.css'}" rel="stylesheet" type="text/css" />

    Функция version добавляет версию:

    function smarty_version($args){
     
      $stat = stat($GLOBALS['config']['site_root'].$args['src']);
      $version = $stat['mtime'];
     
      echo preg_replace('!\.([a-z]+?)$!', ".v$version.\$1", $args['src']);
    }

    Результат на странице:

    <link href="/css/group.v1234567890.css" rel="stylesheet" type="text/css" />

    Отпимизация

    Чтобы избежать лишних вызовов stat, можно хранить массив со списком текущих версий в отдельной переменной

    $versions['css'] = array(
      'group.css' => '1.1',
      'other.css' => '3.0',
    }

    В этом случае в HTML просто подставляется текущая версия из массива.

    Можно скрестить оба подхода, и выдавать во время разработки версию по дате модификации — для актуальности, а в продакшн — версию из массива, для производительности.

    Применимость

    Такой способ кеширования работает везде, включая Javascript, CSS, изображения, flash-ролики и т.п.

    Он полезен всегда, когда документ изменяется, но в браузере всегда должна быть текущая актуальная версия.