MySQL

Советы для разработчика по работе с 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() посоветуйтесь с вашим сисадмином.

Источник

By sysadmin on 16.02.2010 | Development, MySQL
Метки: ,

Отличия типов datetime и timestamp в MySQL

На днях я столкнулся с тем, что многие разработчики не знают в чём отличие типов данных DATETIME и TIMESTAMP в MySQLе, а так же как хранить дату и время, если необходимо учитывать разные часовые пояса для разных пользователей веб-приложения. Поэтому хочу дать ниже разъяснения с пояснениями.

DATETIME
Хранит время в виде целого числа вида YYYYMMDDHHMMSS, используя для этого 8 байтов. Это время не зависит от временной зоны. Оно всегда отображается при выборке точно так же, как было сохранено, независимо от того какой часовой пояс установлен в MySQL. Даю пример:

mysql> CREATE TABLE `dt1` ( col datetime NOT NULL );
mysql> SET @@SESSION.time_zone='+00:00';
mysql> SELECT now();
+---------------------+
| now() |
+---------------------+
| 2009-06-04 18:13:56 |
+---------------------+
 
mysql> INSERT INTO dt1 VALUES(now());
 
mysql> SET @@SESSION.time_zone='+01:00';
mysql> SELECT now();
+---------------------+
| now() |
+---------------------+
| 2009-06-04 19:14:20 |
+---------------------+
 
mysql> INSERT INTO dt1 VALUES(now());
 
mysql> SET @@SESSION.time_zone='+00:00';
mysql> SELECT * FROM dt1;
+---------------------+
| col |
+---------------------+
| 2009-06-04 18:14:10 |
| 2009-06-04 19:14:27 |
+---------------------+

TIMESTAMP
Хранит 4-байтное целое число, равное количеству секунд, прошедших с полуночи 1 января 1970 года по усреднённому времени Гринвича (т.е. нулевой часовой пояс, точка отсчёта часовых поясов). При получении из базы отображается с учётом часового пояса. Часовой пояс может быть задан в операционной системе, глобальных настройках MySQL или в конкретной сессии. Запомните, что сохраняется всегда количество секунд по UTC (универсальное координированное время, солнечное время на меридиане Гринвича), а не по локальному часовому поясу. Пример:

mysql> CREATE TABLE tm1 (col TIMESTAMP NOT NULL);
mysql> SET @@SESSION.time_zone = '+00:00';
mysql> SELECT now();
+---------------------+
| now() |
+---------------------+
| 2009-06-04 18:24:54 |
+---------------------+
mysql> INSERT INTO tm1 VALUES(now());
mysql> SELECT * FROM tm1;
+---------------------+
| col |
+---------------------+
| 2009-06-04 18:25:08 |
+---------------------+
 
mysql> SET @@SESSION.time_zone = '+01:00';
mysql> SELECT now();
+---------------------+
| now() |
+---------------------+
| 2009-06-04 19:25:21 |
+---------------------+
mysql> INSERT INTO tm1 VALUES(now());
mysql> SELECT * FROM tm1;
+---------------------+
| col |
+---------------------+
| 2009-06-04 19:25:08 |
| 2009-06-04 19:25:26 |
+---------------------+
 
mysql> SET @@SESSION.time_zone = '+00:00';
mysql> SELECT * FROM tm1;
+---------------------+
| col |
+---------------------+
| 2009-06-04 18:25:08 |
| 2009-06-04 18:25:26 |
+---------------------+

Ещё одно отличие! TIMESTAMP по умолчанию NOT NULL, а его значение по умолчанию равно NOW().

mysql> INSERT INTO dt1 VALUES(NULL);
ERROR 1048 (23000): COLUMN 'col' cannot be NULL
mysql> INSERT INTO tm1 VALUES(NULL);
Query OK, 1 ROW affected (0.00 sec)
mysql> SELECT * FROM tm1;
+---------------------+
| col |
+---------------------+
| 2009-06-04 18:25:08 |
| 2009-06-04 18:25:26 |
| 2009-06-04 18:32:50 |
+---------------------+


Дополнение.
Для тех, кого смущает использование функции NOW().

mysql> SET @@SESSION.time_zone = '+00:00';
mysql> INSERT INTO dt1 VALUES('2009-06-04 22:00:00');
mysql> SET @@SESSION.time_zone = '+01:00';
mysql> SELECT * FROM dt1;
+---------------------+
| col |
+---------------------+
| 2009-06-04 22:00:00 |
+---------------------+
 
mysql> SET @@SESSION.time_zone = '+00:00';
mysql> INSERT INTO tm1 VALUES('2009-06-04 22:00:00');
mysql> SET @@SESSION.time_zone = '+01:00';
mysql> SELECT * FROM tm1;
+---------------------+
| col |
+---------------------+
| 2009-06-04 23:00:00 |
+---------------------+

Источник

By sysadmin on 24.11.2009 | MySQL
Метки: , , ,

Тюнинг тредов в Mysql (mysql threads tunning)

mysqld это «один процесс — много тредов». В достаточно приближенном понимании тред (thread) это когда процесс копирует сам себя, и родительский процесс передает треду какую то задачу для решения.
Создание треда отличается от обычного fork() в основном тем, что fork порождает +1 процесс, тогда как деление на треды использует другую, более производительную технологию ветвления без клонирования данных в памяти.

При каждом подключении клиента, mysql создает тред, который это подключение обрабатывает.

Если у вас средне нагруженый mysqld, то убедитесь что mysqld работает на нормальной реализации тредов. К примеру, FreeBSD ниже 6 ветки имеет ненормальную, глючную реализацию тредов pthread. Основные глюки проявляются при работе на SMP (более 1 процессора). pthread «не умеет» нормально раскидывать треды по разным процессорам и в результате мы имеем ситуацию, когда Mysqld висит только на одном процессоре вместе с кучей своих детишек. Все остальные процессоры курят, даже если мускуль съел все процессорное время своего процессора.

Более детальная информация как правильно заставить работать mysql под FreeBSD может быть найдена тут: http://dev.mysql.com/doc/refman/5.1/en/freebsd.html

Итак, посмотрим что у вас творится с тредами. Если сервер слабо нагружен и имеет 1-2 подключения одновременно, то в этой оптимизации особого смысла нет. Гораздо ощутим результат, когда сервер серъезно нагружен и имеет много конкурентных запросов.

Посмотрим статус

shell> mysqladmin extended-status

Обратим внимание на значения:

Threads_cached
Threads_connected
Threads_created
Threads_running

Если Threads_cached равно нулю, у вас отключен кеш тредов. Т.е. при каждом подключении создается новый тред, а при отключении он уходит в /dev/null ;). Это плохо. Когда включен кеш тредов, то при отключении клиента тред уходит в кеш, а при новом подключении он не создается, а берется из кеша. Это гигантская экономия ресурсов при больших нагрузках. В некоторых ситуациях нагрузка уменьшалась в разы при использовании thread cache.

Threads_cached — количество тредов в кеше

Threads_connected — грубо говоря, количество открытых подключений

Threads_running — Сколько тредов сейчас «работают»

Threads_created — а вот и краеугольный камень. Сколько тредов было создано со старта сервера. Если в кеше нет свободного треда, то он создается. Если кеша нет вообще, то при каждом подключении создается тред. А при отключении убивается. Тоже самое происходит, если кеш тредов слишком маленький. Короче. Если это значение у вас измеряется тысячами, то у вас не все впорядке. Оно должно быть чуть больше Threads_cached в идеальном случае.

Что делать если Threads_created имеет слишком высокое значение?

В my.cnf есть такой параметр thread_cache_size (или просто thread_cache). Его необходимо увеличивать эксперементальным путем до той поры пока Threads_created не примет порядок цифр Threads_cached.

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

Либо периодически запускать такую команду:

shell> echo "SHOW GLOBAL STATUS" | mysql | grep Threads_connected | awk '{print $2;}'

Определили среднее значение, записали в my.cnf (thread_cache), сделали рестарт сервера. Пошли, покурили, выпили чайку. Смотри статус (команда указана выше). Если Threads_created продолжает неугомонно расти выше значения кеша, увеличиваем переменную thread_cache. И так до победы, но не советую растить эту величину выше нескольких сотен — рискуете получить Out Of Memory.

При увеличении этой величины также надо мониторить Resident Set Size процесса mysqld в долгосрочной динамике. Это колонка RSS в утилите «top». Означает сколько mysql сейчас занимает места в оперативе. Не советую растить его выше половины объема вашей оперативной памяти, т.к. получите пенальти, когда начнут ужиматься системные кеши.

Нормальное значение thread_cache, при котором Threads_created держится на приемлимом уровне, а mysql умеренно жрет память колеблется от 5 до 100 в зависимости от нагрузки и величины доступного ОЗУ.

Слишком высокое значение thread_cache может необоснованно кушать лишнюю память.

Если вас это не спасло, thread_cache превышает мыслимые нормы и Mysql продолжает жрать память и процессор и порождать треды, то необходима комплексная оптимизация: 1. системы, 2. ядра, 3. логики скриптов и запросов 4. других стартовых параметров mysqld

Источник
By sysadmin on 18.03.2009 | MySQL
Метки: ,

mysqladmin — примеры использования

В 15 примерах использования команд mysqladmin ниже, используется пароль root Mysql tmppassword. Поменяйте его на ваш пароль

1. Как изменить пароль root на Mysql?

$ mysqladmin -u root -ptmppassword password 'newpassword'
$ mysql -u root -pnewpassword
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 8
Server version: 5.1.25-rc-community MySQL Community Server (GPL)
Type 'help;' or '\h' for help. Type '\c' to clear the buffer.
mysql>

2. Как проверить работает ли MySQL сервер?

$ mysqladmin -u root -p ping
Enter password:
mysqld is alive

3. Как посмотреть какая версия MySQL используетсяI am running?

Эта команда так же отображает текущий статус сервера.

$ mysqladmin -u root -ptmppassword version
mysqladmin  Ver 8.42 Distrib 5.1.25-rc, for redhat-linux-gnu on i686
Copyright (C) 2000-2006 MySQL AB
This software comes with ABSOLUTELY NO WARRANTY. This is free software,
and you are welcome to modify and redistribute it under the GPL license
Server version          5.1.25-rc-community
Protocol version        10
Connection              Localhost via UNIX socket
UNIX socket             /var/lib/mysql/mysql.sock
Uptime:                 107 days 6 hours 11 min 44 sec
Threads: 1  Questions: 231976  Slow queries: 0  Opens: 17067
Flush tables: 1  Open tables: 64  Queries per second avg: 0.25

4. Как посмотреть статус MySQL сервера?

$ mysqladmin -u root -ptmppassword status
Uptime: 9267148
Threads: 1  Questions: 231977  Slow queries: 0  Opens: 17067
Flush tables: 1  Open tables: 64  Queries per second avg: 0.25

Команда status отображает следующую информациюcommand displays the following information:

  • Uptime: Время безотказной работы в секундах
  • Threads: Общее количество клиентов, подключенных к серверу.
  • Questions: Общее количество запросов к серверу с момента запуска.
  • Slow queries: Общее количество запросов, чьё время выполнения было больше чем значение переменной long_query_time.
  • Opens: Total number of tables opened by the server.
  • Flush tables: How many times the tables were flushed.
  • Open tables: Total number of open tables in the database.

5. Как просмотреть статус переменных MySQL и их текущее значение?

$ mysqladmin -u root -ptmppassword extended-status
+-----------------------------------+-----------+
| Variable_name                       | Value     |
+-----------------------------------+-----------+
| Aborted_clients                       | 579       |
| Aborted_connects                    | 8         |
| Binlog_cache_disk_use             | 0         |
| Binlog_cache_use                    | 0         |
| Bytes_received                       | 41387238  |
| Bytes_sent                            | 308401407 |
| Com_admin_commands          | 3524      |
| Com_assign_to_keycache        | 0         |
| Com_alter_db                        | 0         |
| Com_alter_db_upgrade           | 0         |

6. Как отобразить все системные переменные MySQL сервера и их значения?

$ mysqladmin  -u root -ptmppassword variables
+---------------------------------+---------------------------------+
| Variable_name                   | Value                           |
+---------------------------------+---------------------------------+
| auto_increment_increment        | 1                               |
| basedir                         | /                               |
| big_tables                      | OFF                             |
| binlog_format                   | MIXED                           |
| bulk_insert_buffer_size         | 8388608                         |
| character_set_client            | latin1                          |
| character_set_database          | latin1                          |
| character_set_filesystem        | binary                          |
skip.....
| time_format                     | %H:%i:%s                        |
| time_zone                       | SYSTEM                          |
| timed_mutexes                   | OFF                             |
| tmpdir                          | /tmp                            |
| tx_isolation                    | REPEATABLE-READ                 |
| unique_checks                   | ON                              |
| updatable_views_with_limit      | YES                             |
| version                         | 5.1.25-rc-community             |
| version_comment                 | MySQL Community Server (GPL)    |
| version_compile_machine         | i686                            |
| version_compile_os              | redhat-linux-gnu                |
| wait_timeout                    | 28800                           |
+---------------------------------+---------------------------------+

7. Как отобразить все запущенные процессы/запросы в базе mysql?

$ mysqladmin -u root -ptmppassword processlist
+----+------+-----------+----+---------+------+-------+------------------+
| Id | User | Host      | db | Command | Time | State | Info             |
+----+------+-----------+----+---------+------+-------+------------------+
| 20 | root | localhost |    | Sleep   | 36   |       |                  |
| 23 | root | localhost |    | Query   | 0    |       | show processlist |
+----+------+-----------+----+---------+------+-------+------------------+

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

$ mysqladmin -u root -ptmppassword -i 1 processlist
+----+------+-----------+----+---------+------+-------+------------------+
| Id | User | Host      | db | Command | Time | State | Info             |
+----+------+-----------+----+---------+------+-------+------------------+
| 20 | root | localhost |    | Sleep   | 36   |       |                  |
| 23 | root | localhost |    | Query   | 0    |       | show processlist |
+----+------+-----------+----+---------+------+-------+------------------+
+----+------+-----------+----+---------+------+-------+------------------+
| Id | User | Host      | db | Command | Time | State | Info             |
+----+------+-----------+----+---------+------+-------+------------------+
| 24 | root | localhost |    | Query   | 0    |       | show processlist |
+----+------+-----------+----+---------+------+-------+------------------+

8. Как создать базу MySQL?

$ mysqladmin -u root -ptmppassword create testdb
$ mysql -u root -ptmppassword
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 705
Server version: 5.1.25-rc-community MySQL Community Server (GPL)
Type 'help;' or '\h' for help. Type '\c' to clear the buffer.
mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| sugarcrm           |
| testdb             |
+--------------------+
4 rows in set (0.00 sec)

Замечание: Для отображения все таблиц в базе данных, общего количества колонок, строк, индексов и прочее…. используйте команду mysqlshow.

9. Как удалить существующую базу MySQL?

$ mysqladmin -u root -ptmppassword drop testdb
Dropping the database is potentially a very bad thing to do.
Any data stored in the database will be destroyed.
Do you really want to drop the 'testdb' database [y/N] y
Database “testdb” dropped
$ mysql -u root -ptmppassword
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 707
Server version: 5.1.25-rc-community MySQL Community Server (GPL)
Type ‘help;’ or ‘\h’ for help. Type ‘\c’ to clear the buffer.
mysql> show databases;
+——————–+
| Database           |
+——————–+
| information_schema |
| mysql              |
| sugarcrm           |
+——————–+
3 rows in set (0.00 sec)

10. Как перегрузить все привилегии и права на таблицы?

$ mysqladmin -u root -ptmppassword reload;

Команда Refresh сбросит все таблица и закроет/откроет лог-файлы.

$ mysqladmin -u root -ptmppassword refresh

11. Как образом выполнить безопасную остановку MySQL сервера?

$ mysqladmin -u root -ptmppassword shutdown
$ mysql -u root -ptmppassword
ERROR 2002 (HY000): Can't connect to local MySQL server
through socket '/var/lib/mysql/mysql.sock'

Вы можете использовать команду “/etc/rc.d/init.d/mysqld stop” для остановки сервера. Для запуска выполните “/etc/rc.d/init.d/mysql start”

12. Список всех mysqladmin flush комманд.

$ mysqladmin -u root -ptmppassword flush-hosts
$ mysqladmin -u root -ptmppassword flush-logs
$ mysqladmin -u root -ptmppassword flush-privileges
$ mysqladmin -u root -ptmppassword flush-status
$ mysqladmin -u root -ptmppassword flush-tables
$ mysqladmin -u root -ptmppassword flush-threads
  • flush-hosts: Сбросить всю информацию в кэше хостов.
  • flush-privileges: Перезагрузить права.
  • flush-status: Очистить статус переменных.
  • flush-threads: Flush the thread cache.

13. Как убить подвешенный клиентский процесс в MySQL ?

Сперва определите подвешенный процесс используя команду processlist.

$ mysqladmin -u root -ptmppassword processlist
+----+------+-----------+----+---------+------+-------+------------------+
| Id | User | Host      | db | Command | Time | State | Info             |
+----+------+-----------+----+---------+------+-------+------------------+
| 20 | root | localhost |    | Sleep   | 64   |       |                  |
| 24 | root | localhost |    | Query   | 0    |       | show processlist |
+----+------+-----------+----+---------+------+-------+------------------+

Затем используйте команду kill и нужный process_id. Для завершения нескольких процессов разделите process id запятыми.

$ mysqladmin -u root -ptmppassword kill 20
$ mysqladmin -u root -ptmppassword processlist
+----+------+-----------+----+---------+------+-------+------------------+
| Id | User | Host      | db | Command | Time | State | Info             |
+----+------+-----------+----+---------+------+-------+------------------+
| 26 | root | localhost |    | Query   | 0    |       | show processlist |
+----+------+-----------+----+---------+------+-------+------------------+

14. Как запустить или остановить репликацию MySQL на slave-сервер?

$ mysqladmin  -u root -ptmppassword stop-slave
Slave stopped
$ mysqladmin  -u root -ptmppassword start-slave
mysqladmin: Error starting slave: The server is not configured as slave;
fix in config file or with CHANGE MASTER TO

15. Как скомбинировать несколько команд mysqladmin вместе?

В примере ниже скомбинированы команды process-list, status и version для полного вывода статуса сервера.

$ mysqladmin  -u root -ptmppassword process status version
+----+------+-----------+----+---------+------+-------+------------------+
| Id | User | Host      | db | Command | Time | State | Info             |
+----+------+-----------+----+---------+------+-------+------------------+
| 43 | root | localhost |    | Query   | 0    |       | show processlist |
+----+------+-----------+----+---------+------+-------+------------------+
Uptime: 3135
Threads: 1  Questions: 80  Slow queries: 0  Opens: 15  Flush tables: 3
Open tables: 0  Queries per second avg: 0.25
mysqladmin  Ver 8.42 Distrib 5.1.25-rc, for redhat-linux-gnu on i686
Copyright (C) 2000-2006 MySQL AB
This software comes with ABSOLUTELY NO WARRANTY. This is free software,
and you are welcome to modify and redistribute it under the GPL license
Server version          5.1.25-rc-community
Protocol version        10
Connection              Localhost via UNIX socket
UNIX socket             /var/lib/mysql/mysql.sock
Uptime:                 52 min 15 sec

Вы можете также использовать краткую форму записи:

$ mysqladmin  -u root -ptmppassword pro stat ver

Используйте опцию -h для подключения к удаленному MySQL серверу и выполнения команды.

$ mysqladmin  -h 192.168.1.112 -u root -ptmppassword pro stat ver
By sysadmin on 02.03.2009 | MySQL
Метки: , , , ,

Хранение изображений в базе данных MySQL

Для хранения изображений в базе данных MySQL необходимо определить одно из полей таблицы как производное от типа BLOB. Сокращение BLOB означает большой двоичный объект. Тип хранения данных BLOB обладает несколькими вариантами:

  • TINYBLOB — может хранить до 255 байт
  • BLOB — может хранить до 64 килобайт информации
  • MEDIUMBLOB — до 16 мегабайт
  • LONGBLOB — до 4 гигабайт

Соответсвенно, для хранения изображений нам надо создать таблицу images с двумя полями:

  • id — уникальный ID изображения
  • content — поле для хранения изображения

Для сохранения файла изображения в базе данных необходимо прочитать файл в переменную и создать запрос на добавление данных в таблицу. Допустим у нас есть форма для загрузки файла изображения на сервер:

<form action="putimage.php" enctype="multipart/form-data" method="post"> Изображение: <input name="image" type="file" />
<input type="submit" value="Загрузить" />
</form>Обработчик формы - файл putimage.php:
// Проверяем пришел ли файл
if( !empty( $_FILES['image']['name'] ) ) {
// Проверяем, что при загрузке не произошло ошибок
if ( $_FILES['image']['error'] == 0 ) {
// Если файл загружен успешно, то проверяем - графический ли он
if( substr($_FILES['image']['type'], 0, 5)=='image' ) {
// Читаем содержимое файла
$image = file_get_contents( $_FILES['image']['tmp_name'] );
// Экранируем специальные символы в содержимом файла
$image = mysql_escape_string( $image );
// Формируем запрос на добавление файла в базу данных
$query="INSERT INTO `images` VALUES(NULL, '".$image."')";
// После чего остается только выполнить данный запрос к базе данных
mysql_query( $query );
}
}
}
?>

Извлечь сохраненный файл изображения можно следующим образом (файл image.php):

if ( isset( $_GET['id'] ) ) {
// Здесь $id номер изображения
$id = (int)$_GET['id'];
if ( $id > 0 ) {
$query = "SELECT `content` FROM `images` WHERE `id`=".$id;
// Выполняем запрос и получаем файл
$res = mysql_query($query);
if ( mysql_num_rows( $res ) == 1 ) {
$image = mysql_fetch_array($res);
// Отсылаем браузеру заголовок, сообщающий о том, что сейчас будет передаваться файл изображения
header("Content-type: image/*");
// И  передаем сам файл
echo $image['content'];
}
}
}
?>

Чтобы вывести изображение в HTML-документе, делаем так:

<img src="image.php?id=17" alt="" />

И последнее: графические файлы иногда имеют довольно большой размер, убедитесь, что настройки сервера позволяют работать с таким объемом данных. В файле php.ini это директивы post_max_size — определяет максимальный объем данных передаваемых методом POST, и upload_max_filesize — определяет максимальный размер загружаемого файла. Так же проверьте, позволяют ли настройки MySQL обрабатывать запросы с большим объемом данных (директива max_allowed_packet файла my.ini).

By sysadmin on 13.02.2009 | FreeBSD, Linux, MySQL
Метки: , , ,

Восстановление root-пароля в MySQL

Сменить утерянный пароль достаточно просто и требуется проделать следующие шаги:
1. Остановить mysql-сервер

$> /usr/local/etc/rc.d/mysql-server stop

2. Запустить mysqld с опцией —skip-grant-tables

$> mysqld_safe --skip-grant-tables &

3. Соединиться с mysql-сервером

$> mysql -u root

4. Установить пароль root

mysql> USE mysql;
mysql> UPDATE USER SET password=PASSWORD('root_password') WHERE USER='root' AND host='localhost';
mysql> FLUSH privileges;
mysql> quit

5. Остановить сервер

$> killall mysqld_safe

6. Запустить сервер и проверить новый пароль.

By sysadmin on 08.02.2009 | MySQL
Метки: , , ,

Хранение файлов в MySQL и их быстрая раздача

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

Но у такого классического похода множество недостатков:

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

Больше о проблемах, возникающих при хранении файлов отдельно от БД можно почитать в презентации SQL Antipatterns, раздел Phantom Files, страница 60. Кстати, автор презентации предлагает решение — хранить файлы прямо в БД, в поле типа BLOB. Правда следует замечание, что это должно быть взвешенное решение в каждом конкретном случае. Ведь при таком способе хранения файлов вебсервер должен при каждом запросе вызывать некий скрипт, который будет извлекать файл из БД и отдавать пользователю, что неминуемо отрицательно скажется на производительности.
Для поиска решения данной проблемы был проведен мозговой штурм и придумано несколько вариантов решения проблемы:

1. Перед удалением записи делать SELECT с тем же условием и получать имена файлов, которые надо удалить. Проблема в том, что если удаляемых файлов много, эта операция может занять некоторое время и по хорошему на это время надо блокировать таблицу на чтение и запись, а во многих случаях это недопустимо.
2. Перед удалением устанавливать у удаляемых записей метку «подлежит удалению», получить все записи с этой меткой и удалить файлы, связанные с этими записями, и наконец удалить все записи с этой меткой. Запросы, работающие с этой таблицей следует доработать, чтобы они не выбирали записи с установленным флагом. Недостатки — необходимость правки множества запросов, к тому же у нас в проекте записи на удаление отбираются достаточно сложным SELECT, которые нельзя переделать в один UPDATE.
3. Первые два способа пытаются решить проблему «потерянных» файлов при удалении записей в БД, которая возникает при «классическом» способе хранения файлов, однако они не решают остальных проблем такого подхода, поэтому мы попытались придумать решения, использующие положительные моменты хранения файлов прямо в БД и избавиться от недостатков, присущих этому подходу.
4. Использовать триггеры. К сожалению, MySQL не имеет в своем языке поддержки команд работы с файлами, такие команды пришлось бы реализовывать самостоятельно, ковыряясь в исходниках MySQL. Из минусов — файлы должны храниться на том же хосте, что и БД, необходимость доработки MySQL, таких готовых решений мы не нашли.
5. Хранить файлы в БД, но отдавать их напрямую вебсервером, без участия PHP. Реализовать это можно, написав модуль к вебсерверу (nginx например) который позволял бы отдавать файлы напрямую из MySQL или применив драйвер файловой системы MySQLfs. Такой подход решает все перечисленные выше проблемы, но его недостаток — дополнительные накладные расходы на хранение файлов в MySQL.
6. Специализированный Storage Engine для MySQL, хранящий записи как файлы.

Остановимся более подробно а последнем пункте. Ведь что собой представляет файловая система — это специализированная БД, которая по ключу «имя файла» позволяет получить запись — его содержимое. То есть можно реализовать свой механизм хранения данных для MySQL, в котором каждая запись будет иметь три поля:

CREATE TABLE `data_storage`.`files` (
`id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY ,
`path` VARCHAR( 255 ) ,
`data` BLOB
) ENGINE = FILES

Вставлять данные в такую таблицу можно только в поле `data`, при этом они просто сохраняются в файл, уникальное имя ему при этом генерируется автоматически (используя в качестве префикса поле `id`) — например 764533, а в поле `path` автоматически подставляется правильный путь, по которому MySQL положил наши данные — например ‘/mnt/storage/mysqldata/76/45/33/764533_myfile.jpg’. Таким образом к данным, сохраненным в такой таблице можно обращаться как к простым файлам, и при этом MySQL будет поддерживать целостность данных. Таким образом этот способ хранения файлов лишен практически всех недостатков классического подхода (кроме ограничения доступа, но и его можно сделать используя простой скрипт и заголовок X-Accel-Redirect nginx) и в то же время никак не уменьшает производительность при отдаче файлов клиентам.
Проблема за малым — не удалось найти готовой реализации такого движка хранения данных для MySQL, хотя идея общем то простая. Возможно кто-то из хабролюдей подскажет ссылку на готовую реализацию такого storage engine, идея ведь плавает на поверхности, и ее точно кто-то уже мог реализовать.

Автор: Sheder
Источник: shedar.habrahabr.ru

By sysadmin on 07.02.2009 | MySQL
Метки: ,

Резервное копирование баз MySQL

Резервное копирование баз MySQL. Гораздо более сложной представляется задача создания копии такой динамичной структуры, как база данных MySQL.

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

Сделать копию всех статических HTML- и прочих документов просто. Так же несложно периодически «откладывать в сторонку» и копии скриптов. Гораздо более сложной представляется задача создания копии (далее backup) такой динамичной структуры, как база данных MySQL. Основные трудности, которые возникают перед администратором размещенного на хостинге сайта, обычно бывают такие:

  1. Отсутствие физического доступа к файлам базы данных. Как правило, провайдеры хостинга предоставляют возможность работы с базой данных только через скрипты или специальный mysql-клиент, но не дают прав на доступ непосредственно к файлам, в которых содержатся данные из MySQL-базы.
  2. Отсутствие у администратора знаний о том, как вообще надо делать backup. Обычно такая задача возникает только, когда «клюнул жареный петух». То есть, в случае аварии, вторжения хакеров или в других внештатных ситуациях. Веб-мастеры просто не готовы к немедленному backup и начинают судорожно изучать документацию по MySQL, а время идет…
  3. В случае, если веб-мастер не владеет в достаточной мере навыками работы со специализированными утилитами из пакета MySQL, могут возникать трудности, связанные с ограничениями, налагаемыми хостинг-провайдером на пользовательские аккаунты. Например, если база очень большая и ее размер превышает лимит на доступную пользователю память (RAM), backup сделать будет сложно. Нужно пользоваться тонкими настройками утилит резервного копирования, что иногда тоже вызывает трудности на практике.

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

Как сделать копию базы MySQL

Существует программа mysqldump, позволяющая быстро и просто производить операции по созданию резервных копий баз MySQL. Также mysqldump дает возможность делать очень тонкие настройки для управления процессом создания резервных копий баз данных или отдельных таблиц. Можно сказать, что mysqldump — это основной инструмент, которым Вам придется пользоваться в том случае, если Вы будете делать backup MySQL.

Сразу возьмем простую задачу, которую будем решать с помощью mysqldump, и разберемся, что к чему. Есть хостинг, есть база данных DBNAME, которую выделил Вам хостинг-провайдер. Есть хост HOST, на котором размещен сервер MySQL, логин LOGIN к нему, порт PORT, на котором работает сервер, а также пароль PASS. Имея все эти данные, можно сделать dump (дамп, копию) базы DBNAME так (выполняем в unix shell):

> mysqldump -uLOGIN -PPORT -hHOST -pPASS DBNAME > dump.txt

После выполнения данной команды в файле dump.txt у нас будет копия MySQL-базы DBNAME. Это произойдет только в том случае, конечно, если все параметры Вы зададите верно, в соответствии с настройками своего хостинга. Сразу нужно сказать, что программа mysqldump производит вывод результатов прямо Вам на STDIN, то есть, на экран. Нужно перенаправлять вывод в какой-либо файл. Например, как в данном случае — » > dump.txt «. Если этого не сделать, а база большая, Вы получите на экран все те мегабайты информации, которые в ней содержатся.

Немного расскажем о том, что же делает mysqldump. Эта программа создает сценарий восстановления Ваших данных. То есть, вывод mysqldump — это не какие-то абстрактные и нечитаемые двоичные данные, а осмысленный текст сценария. Например, если в Вашей базе была таблица test, в которой было поле test2 с типом данных integer и одна-единственная запись «1111», то mysqldump создаст примерно такой сценарий:

# MySQL dump 8.14
#
# Host: HOST Database: DBNAME
#--------------------------------------------------------
# Server version 3.23.39-log
 
#
# Table structure for table 'test'
#
 
CREATE TABLE test (
 test2 int(11) default NULL
) TYPE=MyISAM;
 
#
# Dumping data for table 'test2'
#
 
INSERT INTO test2 VALUES ('1111');

Таким образом, mysqldump «опишет» все Ваши таблицы и создаст INSERT-команды для восстановления данных в таблицах. Итак, мы перенаправляем вывод mysqldump в текстовый файл, который потом будем использовать для восстановления. Рассмотрим и этот процесс — воссоздание базы из резервной копии.

Для восстановления будем пользоваться стандартной программой mysql, которая входит в комплект поставки MySQL наряду с mysqldump. Допустим, у нас имеется backup в файле dump.txt. Нам нужно восстановить его в рабочую базу. Например, мы случайно удалили нашу базу данных, а теперь пытаемся исправить эту незадачу. Делаем так :

> mysql -uLOGIN -PPORT -hHOST -pPASS DBNAME < dump.txt

То есть, заставляем mysql-клиент соединиться с сервером и выполнить сценарий, который у нас имеется. После выполнения этой команды в Вашей базе появятся таблицы и данные из резервной копии. Учитывайте то, что данные будут просто восстанавливаться по сценарию из dump.txt. То есть, если таблицы, которые упоминаются в дампе базы, уже существуют и имеют другую структуру, тут явно возникнет ошибка. Просто посмотрите на сценарий и на рабочую базу и представьте, что Вы вручную выполняете команды из сценария. Если уверены, что все будет хорошо — смело восстанавливайте.

Рассмотрим более тонкие настройки mysqldump:

-databases позволяет сделать так, что mysqldump включит в сценарий восстановления команды

CREATE DATABASE /*!33333 IF NOT EXISTS*/ DBNAME и USE DBNAME

. Это позволит создавать рабочие базы «с нуля». То есть, без использования —databases подразумевается, что пользователь восстанавливает одну базу данных и явно указывает, куда нужно помещать восстанавливаемые данные. Если же backup создается с целью сделать полностью рабочую копию данных, например, на другом MySQL-сервере, то нужно использовать этот ключ;

-all-databases позволяет сделать копии всех баз данных, которые существуют на данном MySQL-сервере. Если же нужно сделать копии только некоторых баз, нужно просто указать их через пробел при вызове mysqldump из командной строки (см. выше);

Ключ -help. Программа mysqldump имеет множество версий. Посмотреть, какие возможности поддерживаются конкретно Вашей версией, можно с помощью этого ключа;

-add-drop-table — ключ, который заставит mysqldump добавлять в итоговый сценарий команду drop table перед созданием таблиц. Это позволит избежать некоторых ошибок при восстановлении базы из резервной копии. Конечно, нужно учитывать то, что таблицы, находящиеся в рабочей копии (если таблицы с таким же именем существуют в backup), перед восстановлением из резервной копии будут удалены из основной базы и пересозданы из backup;

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

-result-file=… — этот ключ можно использовать для перенаправления вывода в файл. Можно использовать обычное unix-перенаправление командой «>», а можно — вот этот ключ. Кому что нравится;

Кроме перечисленных ключей mysqldump имеет и еще некоторое количество очень полезных возможностей, которые Вы можете применять по обстоятельствам. Полная документация по mysqldump доступна на странице http://www.mysql.com/doc/m/y/mysqldump.html.

Еще один очень полезный совет по использованию mysqldump в хостинговой среде. Как правило, при использовании хостинга на пользователя налагаются некоторые ограничения. Например, нельзя занять больше некоторого количества физической памяти (RAM, ОЗУ). mysqldump по умолчанию помещает все полученные от MySQL-сервера данные в память, а потом записывает все это на диск. Соответственно, если провайдер дает Вам занять, например, 30Мб памяти, а база, копию которой Вы делаете с помощью mysqldump, занимает 50Мб, конечно, тут возникнет ошибка — mysqldump не сможет отработать корректно и завершится аварийно, о чем Вам сообщит. Чтобы «заставить» mysqldump писать данные сразу на диск, а не хранить их, пусть даже и временно, в памяти, используйте ключ —quick. Это решит проблему.

Автоматизация резервного копирования

Теперь подумаем, как бы нам автоматизировать процесс создания резервных копий базы данных. Итак, существует программа — cron. Она позволяет запускать процессы в указанное пользователем время или с определенной периодичностью. Сразу оговоримся — cron в общем случае существует только под Unix, так что, если Вы используете для хостинга ОС Windows, проконсультируйтесь со своим хостинг-провайдером о том, как лучше запускать процессы в нужное время. Да и вообще, пожалуй, этот пункт будет интересен только unix-пользователям.

В unix shell запускаем crontab -e и создаем такое правило запуска процесса создания копий базы:

0 0 * * * mysqldump -uLOGIN -PPORT -hHOST -pPASS DBNAME
	| gzip -c &gt; `date "+%Y-%m-%d"`.gz

Эта команда, запускаясь из cron в полночь (00:00) каждых суток, делает дамп Вашей базы DBNAME и архивирует его архиватором gzip в файл-архив с именем, соответствующим текущей дате. Например, если мы делаем dump 3 января 2002 года, имя файла с архивом будет 2002-01-03.gz. Для того, чтобы получить файлы, по именам которых можно удобно узнать дату их создания, мы используем команду date, которая является стандартной для всех unix-систем. Эта команда позволяет задавать произвольный формат вывода даты, что мы и использовали — date «+%Y-%m-%d». Мы поместили эту команду в обратные одинарные кавычки (backticks), что в unix shell заставляет вставить в команду (утрируя) результат выполнения другой команды.

Сохраняем правило для cron и ждем результатов. Итак, каждый день мы будем иметь на диске заархивированную копию нашей базы данных. Можно быстро найти нужный архив по его названию и восстановить то, что испортилось, например. Кстати, если Вы хотите автоматизировать удаление старых архивов, попробуйте воспользоваться cron и командой find, которая обычно есть в unix. Запуская периодически

find ~/каталог-с-архивами -name "*.gz" -mtime +7

, Вы будете удалять архивы, которые «старше» семи дней. Прочитайте документацию по find — она доступна по команде man find в unix shell.

Если у Вас есть машина, постоянно подключенная к интернет, можно так же по cron копировать созданный Вами backup на нее. Конечно, провайдерская хостинг-машина — это очень надежная штука. Однако, как говорится, «береженого Бог бережет». Старая как мир истина в определенных условиях может и Вам помочь. Используйте для копирования на другую машину команды ftp и scp. Добавьте их запуск в cron. Если Ваша машина поддерживает соединение по протоколу ssh, используйте secure copy клиент для копирования файлов — scp. Читайте документацию по этой команде в man-странице man scp. Примерный запуск: scp 2002-01-03.gz : — закачиваем файл 2002-01-03.gz на машину your.host.ru авторизовавшись там под логином login.

Дополнительные возможности

Скрипт mysqlhotcopy, написанный на языке Perl, поможет Вам упростить создание копий баз данных и отдельных таблиц. Использование этого скрипта позволяет производить резервное копирование еще более гибко. Описание mysqlhotcopy читайте тут.

By sysadmin on | MySQL
Метки: , ,