расставляй правильно приоритеты и не отвлекайся на мелочи

Советы для чайников: увеличиваем производительность L.A.M.P.

Вольный перевод,
автор «Stew»
LAMP performance for dummies (e.g. me)

Советы для среднеразмерных веб приложений базирующихся на L.A.M.P. (прим. hb: L.A.M.P. — Linux Apache MySQL PHP)

Под среднеразмерным подразумевается приложение, в котором Вы имеете 4-5 одновременно работащих пользователя на десять таблиц содержащих в каждой около полумиллина строк и при условии, что Вы уже выполнили всю базовую оптимизацию — тщательно выбрали тип таблиц, добавили индексы, как следует спроектировали базу данных и т.д.

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

  • Если большинство операций — это операции чтения, включите MySQL query cache
  • Не используйте mysqldump для бакапирования. Пользователям не понравится ждать пять минут разрешения на запись, потому что в это время таблицы будут заблокированы. Используйте основанную на mysqlhotcopy или LVM систему, если сможете.
  • Если Вы возвращаете большие объемы данных в PHP, но Вам немедленно не нужны полные записи этих строк (или, к примеру, Вы будете выполнять только mysql_num_rows()), тогда используйте mysql_unbuffered_query вместо mysql_query.
  • xdebug — PHP профилировщик (hb: программа сбора информации, по использованию ресурсов, работе модулей и т.д.), с kcachegrind это будет настоящей «горячей штучкой» для кодера. Невероятно удобно, видеть как долго работает Ваш код и почему.
  • Очень безобидный in_array() … и очень медленный. Если переменные уникальные, то используйте ассоциативные массивы, к примеру, вместо такой конструкции:



    $data = array("a", "b" .. large amount of data .. "zzz");

    for ($i=0; $i < 1000; $i++) {

       print "is c in array? ".in_array("c", $data);

    }




    используйте такую:


    $data = array("a", "b" .. large amount of data .. "zzz");

    $a_data = array();

    foreach ($data as $point) {$a_data[$point] = true;}

    for ($i=0; $i < 1000; $i++) {

       print "is c is array? ".isset($a_data["c"]);

    }




    (hb: естественно если Вы намереваетесь очень активно использовать in_array())

  • key_buffer_size — значение размера памяти, которое MySQL выделяет себе для хранения в ней индексов и которое является очень хорошим параметром. На практике оно должно быть 25%-50% от общей RAM памяти сервера баз данных (естествеенно, уменьшите его если Вы используете память и под другие нужные вещи). Значение по умолчанию — 8МБ, но однажды, когда индексы разрастутся, Вам явно потребуется больше, чем 8МБ.
    Как узнать, что Вам нужен больший буфер под индексы?

    mysql> SHOW STATUS LIKE ‘%key_read%’;
    +——————-+———+
    | Variable_name       | Value     |
    +——————-+———+
    | Key_read_requests | 6375479 |
    | Key_reads             | 130562  |
    +——————-+———+
    2 rows in set (0.00 sec)

    Должно быть по крайней мере 100 key_read_requests (из памяти) на каждый key_reads (с диска), как видно выше.

  • Много сортируете? Увеличьте sort_buffer_size но примите во внимание, что key_buffer_size распределяется на каждое соединение — т.е. увеличьте его до 32МБ и MySQL «сожрет» 32МБ x 4 = 128МБ, если у Вас будет 4 одновременно открытых соединения (также помните, что Ваш код — возможно, иногда сбоит или «забывает» закрывать соединения).
  • Часто делаете ORDER BY? Увеличьте read_rnd_buffer_size. Распределяется на каждое соединение, как выше.
  • Разборка часто используемых данных с диска иногда может быть более быстрой, чем использование memcached или чего-то подобного (Нет, правда. Это менее накладно и к тому-же файлы уже кэшируются самой ОС, поэтому они уже находятся в памяти).
  • serialize() и unserialize() очень медленны. Используйте, где это возможно JSON (нет ничего более гибого, к сожалению)
  • Перефразирую: лучшая оптимизация — это заблаговременное устранение «порожней» работы. Вам действительно необходимо вернуть все данные записей или только первые несколько тысяч? Вам действительно нужно их сортировать? Будет быстрее, если Вы выберете данных больше чем Вам надо и начнете их отсеивать уже в коде?
  • Откажитесь от ORDER BY RAND(). Сделайте это более изящно в коде (Может просто случайным образом выбрать несколько ID?).
  • SQL_CALC_FOUND_ROWS может оказаться быстрее, чем COUNT() — но не всегда.
4 комментария на статью:
  1. Anonymous:

    На сколько мне не изменяет память, то key_buffer_size для таблиц MyISAM.
    Для загрузки большого количыества данных пользую LOAD DATA INFILE — скорость в разы выше, чем дамповый INSERT.

  2. bridennis:

    Память Вам не изменяет ;-)
    key_buffer_size для MyISAM и ISAM таблиц.
    Насчет LOAD DATA INFILE полезная подсказка, спасибо.

  3. Павел:

    Функцию mysql_num_rows() нельзя использовать с результатом запроса, возвращённым функцией mysql_unbuffered_query (См. Замечание — http://php.net/mysql_unbuffered_query)

  4. bridennis:

    Совершенно верное замечание, но там немного другой контекст, так что все в силе.

Ответить
Обязательные поля помечены *