> CREATE TABLE IF NOT EXISTS `users_rights` (
`id` int(10) unsigned NOT NULL,
`user_id` int(10) unsigned NOT NULL,
`rights` int(10) unsigned NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
* где table
— имя таблицы (в примере users_rights
); field1, field2
— имя полей (в примере создается 3 поля — id, user_id, rights
); options1, options2
— параметры поля (в примере int(10) unsigned NOT NULL
); table options
— общие параметры таблицы (в примере ENGINE=InnoDB DEFAULT CHARSET=utf8
).
Использование запросов в PHP
Подключаемся к базе данных:
mysql_connect ("localhost", "login", "password") or die ("MySQL connect error");
mysql_select_db ("db_name");
mysql_query("SET NAMES "utf8"");
* где подключение выполняется к базе на локальном сервере (localhost
); учетные данные для подключения — login
и password
(соответственно, логин и пароль); в качестве базы используется db_name
; используемая кодировка UTF-8
.
Также можно создать постоянное подключение:
mysql_pconnect ("localhost", "login", "password") or die ("MySQL connect error");
* однако есть вероятность достигнуть максимально разрешенного лимита хостинга. Данным способом стоит пользоваться на собственных серверах, где мы сами можем контролировать ситуацию.
Завершить подключение:
* в PHP выполняется автоматически, кроме постоянных подключений (mysql_pconnect).
Запрос к MySQL (Mariadb) в PHP делается функцией mysql_query(), а извлечение данных из запроса — mysql_fetch_array():
$result = mysql_query("SELECT * FROM users");
while ($mass = mysql_fetch_array($result)) {
echo $mass . "
";
}
* в данном примере выполнен запрос к таблице users
. Результат запроса помещен в переменную $result
. Далее используется цикл while
, каждая итерация которого извлекает массив данных и помещает его в переменную $mass
— в каждой итерации мы работаем с одной строкой базы данных.
Используемая функция mysql_fetch_array() возвращает ассоциативный массив, с которым удобно работать, но есть еще альтернатива — mysql_fetch_row(), которая возвращает обычный нумерованный массив.
Экранирование
При необходимости включения в строку запроса спецсимвола, например, %, необходимо использовать экранирование с помощью символа обратного слэша — \
Например:
* если выполнить такой запрос без экранирования, знак %, будет восприниматься как любое количество символов после 100.
На этом все. Если Вам нужно помочь с выполнением запроса, пишите мне на почту
Содержание статьи
1.
Самые простые MySQL запросы
2.
Простые SELECT (выбрать) запросы
3.
Простые INSERT (новая запись) запросы
4.
Простые UPDATE (перезаписать, дописать) запросы
5.
Простые DELETE (удалить запись) запросы
6.
Простые DROP (удалить таблицу) запросы
7.
Сложные MySQL запросы
8.
MySQL запросы и переменные PHP
1. Самые простые SQL запросы
1. Выведет список ВСЕХ баз.
SHOW databases;
2. Выведет список ВСЕХ таблиц в Базе Данных base_name.
SHOW tables in base_name;
2. Простые SELECT (выбрать) запросы к базе данных MySQL
SELECT
– запрос, который выбирает уже существующие данные из БД. Для выбора можно указывать определённые параметры выбора. Например, суть запроса русским языком звучит так - ВЫБРАТЬ такие-то колонки ИЗ такой-то таблицы ГДЕ параметр такой-то колонки равен значению.1. Выбирает ВСЕ данные в таблице tbl_name.
SELECT * FROM tbl_name;
2. Выведет количество записей в таблице tbl_name.
SELECT count(*) FROM tbl_name;
3. Выбирает (SELECT) из(FROM) таблицы tbl_name лимит (LIMIT) 3 записи, начиная с 2.
SELECT * FROM tbl_name LIMIT 2,3;
4. Выбирает (SELECT) ВСЕ (*) записи из (FROM) таблицы tbl_name и сортирует их (ORDER BY) по полю id по порядку.
SELECT * FROM tbl_name ORDER BY id;
5. Выбирает (SELECT) ВСЕ записи из (FROM) таблицы tbl_name и сортирует их (ORDER BY) по полю id в ОБРАТНОМ порядке.
SELECT * FROM tbl_name ORDER BY id DESC;
6. Выбирает (SELECT
) ВСЕ (*) записи из (FROM
) таблицы users
и сортирует их (ORDER BY
) по полю id
в порядке возрастания, лимит (LIMIT
) первые 5 записей.
SELECT * FROM users ORDER BY id LIMIT 5;
7. Выбирает все записи из таблицы users
, где поле fname
соответствует значению Gena
.
SELECT * FROM users WHERE fname="Gena";
8. Выбирает все записи из таблицы users
, где значение поля fname
начинается с Ge
.
SELECT * FROM users WHERE fname LIKE "Ge%";
9. Выбирает все записи из таблицы users
, где fname
заканчивается на na
, и упорядочивает записи в порядке возрастания значения id
.
SELECT * FROM users WHERE fname LIKE "%na" ORDER BY id;
10. Выбирает все данные из колонок fname
, lname
из таблице users
.
SELECT fname, lname FROM users;
11.
Допустим у Вас в таблице пользовательских данных есть страна. Так вот если Вы хотите вывести ТОЛЬКО список встречающихся значений (чтобы, например, Россия не выводилось 20 раз, а только один), то используем DISTINCT. Выведет, из массы повторяющихся значений Россия, Украина, Беларусь. Таким образом, из таблицы users
колонки country
будут выведены ВСЕ УНИКАЛЬНЫЕ значения
SELECT DISTINCT country FROM users;
12. Выбирает ВСЕ данные строк из таблицы users
где age
имеет значения 18,19 и 21.
SELECT * FROM users WHERE age IN (18,19,21);
13.
Выбирает МАКСИМАЛЬНОЕ значение age
в таблице users
. То есть если у Вас в таблице самое большее значение age
(с англ. возраст) равно 55, то результатом запроса будет 55.
SELECT max(age) FROM users;
14. Выберет данные из таблицы users
по полям name
и age
ГДЕ age
принимает самое маленькое значение.
SELECT name, min(age) FROM users;
15. Выберет данные из таблицы users
по полю name
ГДЕ id
НЕ РАВЕН 2.
SELECT name FROM users WHERE id!="2";
3. Простые INSERT (новая запись) запросы
INSERT
– запрос, который позволяет ПЕРВОНАЧАЛЬНО вставить запись в БД. То есть создаёт НОВУЮ запись (строчку) в БД.1.
Делает новую запись в таблице users
, в поле name
вставляет Сергей, а в поле age
вставляет 25. Таким образом, в таблицу дописывается новая строки с данными значениями. Если колонок больше, то они оставшиеся останутся либо пустыми, либо с установленными по умолчанию значениями.
INSERT INTO users (name, age) VALUES ("Сергей", "25");
4. Простые UPDATE запросы к базе данных MySQL
UPDATE
– запрос, который позволяет ПЕРЕЗАПИСАТЬ значения полей или ДОПИСАТЬ что-то в уже существующей строке в БД. Например, есть готовая строка, но в ней нужно перезаписать параметр возраста, так как он изменился со временем. 1. В таблице users
age
становится 18.
UPDATE users SET age = "18" WHERE id = "3";
2.
Всё то же самое, что и в первом запросе, просто показан синтаксис запроса, где перезаписываются два поля и более.
В таблице users
ГДЕ id равно 3 значение поля age
становится 18, а country
Россия.
UPDATE users SET age = "18", country = "Россия" WHERE id = "3";
5. Простые DELETE (удалить запись) запросы к базе данных MySQL
DELETE
– запрос, который удаляет строку из таблицы.1. Удаляет строку из таблицы users
ГДЕ id
равен 10.
DELETE FROM users WHERE id = "10";
6. Простые DROP (удалить таблицу) запросы к базе данных MySQL
DROP
– запрос, который удаляет таблицу.1. Удаляет целиком таблицу tbl_name
.
DROP TABLE tbl_name;
7. Сложные запросы к базе данных MySQL
Любопытные запросы, которые могут пригодиться даже опытным пользователямSELECT id,name,country FROM users,admins WHERE TO_DAYS(NOW()) - TO_DAYS(registration_date) <= 14 AND activation != "0" ORDER BY registration_date DESC;
Данный сложный запрос ВЫБИРАЕТ колонки id,name,country
В ТАБЛИЦАХ users,admins
ГДЕ registration_date
(дата) не старше 14
дней И activation
НЕ РАВНО 0
, СОРТИРОВАТЬ по registration_date
в обратном порядке (новое в начале).
UPDATE users SET age = "18+" WHERE age = (SELECT age FROM users WHERE male = "man");
Выше указан пример так называемого запроса в запросе
в SQL. Обновить возраст среди пользователей на 18+, где пол - мужской. Подобные варианты запроса не рекомендую. По личному опыту скажу, лучше создать несколько отдельных - они будут прорабатываться быстрее.
8. Запросы к базе данных MySQL и PHP
В MySQL запросы в PHP странице можно вставлять переменные в качестве сравниваемых и тп значений. Пара примеров1. Выбирает все записи из таблицы users
, где поле fname
соответствует значению переменной $name
.
SELECT * FROM users WHERE fname="$name";
2. В таблице users
ГДЕ id равно 3 значение поля age
изменяется на значение переменной $age.
UPDATE users SET age = "$age" WHERE id = "3";
Внимание!
Если Вам интересен какой-либо ещё пример, то пишите вопрос в комментарии!
В данной статье мы рассмотрим способы обращения к таблицам баз данный MySQL с помощью языка запросов SQL. SQL - это аббревиатура, которая так и "раскрывается" - структуризированный язык запросов.
В языке PHP для это цели существует целый ряд функций с префиксом "mysql". Нам для рассмотрения запросов понадобится не так много из них. Функция, без которой в языке PHP выполнение SQL-запросов было бы просто невозможным:
Resource mysql_query(запрос)
Данная функция посылает запрос к базе данных и возвращает в случае успешного обращения идентификатор ресурса.
Для того чтобы подключиться к базе данных MySQL необходимо выполнить следующую последовательность:
$host="localhost"; // имя хоста (уточняется у провайдера)
$database="db_name"; // имя базы данных, которую вы должны создать
$user="user_name"; // заданное вами имя пользователя, либо определенное провайдером
$pswd="your_pass"; // заданный вами пароль
$dbh = mysql_connect($host, $user, $pswd) or die("Не могу соединиться с MySQL.");
mysql_select_db($database) or die("Не могу подключиться к базе.");
Итак mysql_connect()
- функция для подключения к серверу MySQL на Вашем хостинге.
А mysql_select_db()
выбирает базу данных на сервере для подключения.
Иными словами подключаемся к серверу, выбираем базу и начинаем работать.
Функция die() вызывается в случае ошибки и выводит в окно браузера сообщение, которое вы указали.
Для завершения работы с базами данных используется функция:
Mysql_close($dbh);
Здесь $dbh
- дескриптор, которые при соединении возвратила функция mysql_connect
.
Закончив стартовый обзор, начнем рассмотрение собственно SQL-запросов.
Для этого прежде всего вам необходимо создать базу данных с определенном именем. А в ней создать таблицу, тоже с конкретным именем. В наших примерах будем обращаться к таблице my_sql_table
. Чтобы создать эту таблицу давайте выполним в phpmyadmin нашего localhost следующий запрос:
CREATE TABLE `my_sql_table` (`id` INT NOT NULL , // идентификатор будущих записей таблицы
`firstname` VARCHAR(50) NOT NULL , // текстовое поле VARCHAR
`surname` VARCHAR(50) NOT NULL , // max длиной 50 символов
PRIMARY KEY (`id`) // первичный ключ - идентификатор id);
Итак таблица создана. Выполним первый запрос, который сразу оформим в виде PHP-кода:
\n";
echo "Имя: ".$row["firstname"]."
\n";
echo "Фамилия: ".$row["surname"]."
\n";
}
?>
Разберем PHP-код файла firstsql.php
. Начнем с собственно запроса к таблицам базы данных (БД).
$query = "SELECT * FROM `my_sql_table`";
Данный запрос можно расшифровать так: выбрать из таблицы my_sql_table
БД все записи из всех полей. Таким образом знак *
после слова SELECT означает "выбрать абсолютно все". Итак, запрос сформирован. Теперь его надо выполнить:
$res = mysql_query($query);
В случае успешного выполнения запроса функция mysql_query()
вернет нам идентификатор ресурса $res
.
Его мы должны передать в качестве параметра в функцию mysql_fetch_array()
. Название этой функции говорит само за себя. Т.е. она формирует и выдает массив по выборке из таблицы БД. В случае нашей таблицы массив будет состоять из числа элементов, равных количествам записей (строк) в таблице и содержать значения id, firstname, surname
для каждой строки таблицы. Следовательно, следующий код:
While($row = mysql_fetch_array($res))
{
echo "Номер: ".$row["id"]."
\n";
echo "Имя:".$row["firstname"]."
\n";
echo "Фамилия:".$row["surname"]."
\n";
}
можно прокомментировать так: пока введенная нами переменная $row получает не нулевые результаты работы функции mysql_fetch_row
следует выдать в броузер значение полей $row["id"], $row["firstname"], $row["surname"]
с помощью echo
.
Если запрос выполнить так:
$query = "SELECT firstname FROM `my_sql_table`";
то это будет означать, что из всех строк выбирается только значения поля firstname.
Следовательно предыдущий код следует переписать как:
$res = mysql_query($query);
while($row = mysql_fetch_array($res))
{
echo "Имя:".$row["firstname"]."
\n";
}
Если Вы хотите выбрать строки таблицы с конкретным значением id
где фамилия (surname) будет Петров
, то запрос перепишется следующим образом:
$query = "SELECT id FROM `my_sql_table` where surname="Петров"";
А вот если потребуется узнать фамилию того, кто находится под номером, к примеру, 5, то запрос будет таким:
$query = "SELECT surname FROM `my_sql_table` where id=5";
В этом случае Вы знаете, что результатом запроса будет всего одна строка из таблицы. Т.е. нет смысла организовывать цикл с использованием while
. И обработка запроса будет следующей
$res = mysql_query($query);
$row = mysql_fetch_row($res);
echo "Фамилия пятого человека в списке: ".$row."\n";
Здесь вместо mysql_fetch_array() мы применили mysql_fetch_row()
. Т.е. получить значение поля (или полей) конкретной строки. Поскольку поле у нас было одно - surname - мы можем обратиться к единственному элементу массива $row как $row;
.
Итак, рассмотрим наиболее типичные примеры запросов MySQL. Рассмотрение проведем на базе таблицы my_sql_table
:
1. Добавим в таблицу my_sql_table поле middle_name (отчество) после surname
:
$query = "ALTER TABLE `my_sql_table` ADD `middle_name`
VARCHAR(50) NOT NULL AFTER `surname`";
2. Теперь удалим поле surname из таблицы my_sql_table:
$query = "ALTER TABLE `my_sql_table` DROP `surname`";
3. Удаляем записи из таблицы my_sql_table с фамилией Сидоров:
$query = "DELETE FROM `my_sql_table` where surname="Сидоров"";
4. Помимо знаков равенства, также "больше" или "меньше", в языке MySQL запросов существует понятие "похоже на
". Выберем записи из таблицы my_sql_table, где в фамилии встречается "дор
" :
$query = "SELECT * FROM `my_sql_table` where surname like "%дор%"";
Здесь наличие "%
" в начале и конце "дор" и означает, что запрос будет искать именно "дор", причем не важно в начале, конце, или середине фамилии он находится. Рассмотрим следующий пример
5. Выберем записи из таблицы my_sql_table с фамилией, которая начинается на П
. Обратите внимание на расположение "%
":
$query = "SELECT * FROM `my_sql_table` where surname like "П%"";
6. Вычислим максимальное значение id
:
$query = "SELECT MAX(id) FROM `my_sql_table`";
7. Вычислим количество полей в my_sql_table с фамилией, которая начинается на П
.
$query = "SELECT COUNT(*) FROM `my_sql_table` where surname like "П%"";
8. Удаление таблицы my_sql_table:
$query = "DROP TABLE `my_sql_table`";
Для запросов 1-3 на языке PHP достаточно просто выполнить запрос:
Mysql_query($query);
Мы рассмотрели наиболее характерные примеры запросов. Полагаю, с их помощью, следуя элементарной логике, Вы сможете выполнять более сложные запросы к созданным Вами таблицам баз данных MySQL.
Есть еще вопросы или что-то непонятно - добро пожаловать на наш
|
|
|
Как оптимизировать MySQL запросы?
Для обычного, не особо посещаемого сайта, нет большой разницы, оптимизированы MySQL запросы к базе или нет. А вот для рабочих серверов под большой нагрузкой разница между правильным и неправильным SQL является огромной, и во время выполнения они могут значительно влиять на поведение и надежность сервисов. В этой статье я рассмотрю, как писать быстрые запросы и факторы, делающие их медленными.
Почему MySQL?
Сегодня идет много разговоров о Dig Data и других новых технологиях. NoSQL и облачные решения это супер, но много популярного софта (такого как WordPress, phpBB, Drupal) до сих пор работает на MySQL. Миграция на новейшие решения может вылиться не только в изменении конфигурации на серверах. К тому же, эффективность MySQL до сих пор на уровне, особенно версия Percona.
Не делайте распространенную ошибку, выбрасывая все больше и больше железа на решение проблемы медленных запросов и высокой нагрузки серверов - лучше обратиться к истокам проблем. Увеличение мощности процессоров и жестких дисков и добавление оперативной памяти это также определенный вид оптимизации, однако, это не то, о чем мы будем говорить в данной статье. Также, оптимизируя сайт и решая проблему железом, нагрузка будет расти только в геометрической прогрессии. Поэтому это лишь краткосрочное решение.
Хорошее понимание SQL это важнейший инструмент для веб-разработчика, именно он позволит эффективно оптимизировать и использовать реляционные базы данных. В этой статье мы сфокусируемся на популярной открытой базе данных, часто используется в связке с PHP, и это MySQL.
Для кого эта статья?
Для веб-разработчиков, архитекторов и разработчиков баз данных и системных администраторов, хорошо знакомых с MySQL. Если раньше вы не использовали MySQL, эта статья может не принести вам пользы, но я все равно буду стараться быть как можно более информативным и полезным даже для новичков в MySQL.
Сначала бэкап
Я рекомендую делать следующие шаги на базе MySQL, с которой вы работаете, однако не забудьте сделать резервную копию. Если у вас нет базы данных, с которой вы можете работать, я буду предоставлять примеры для создания собственной базы данных, где это будет уместно.
Делать бэкапы MySQL просто, используя утилиту mysqldump:
$ mysqldump myTab > myTab-backup.sql
Вы можете узнать больше о mysqldump
.
Что делает запрос медленным?
Вот общий список факторов, влияющих на скорость выполнения запросов и нагрузки сервера:
- индексы таблиц;
- условие WHERE(и использования внутренних функций MySQL, например, таких как IF или DATE);
- сортировка по ORDER BY;
- частое повторение одинаковых запросов;
- тип механизма хранения данных (InnoDB, MyISAM, Memory, Blackhole);
- не использование версии Percona;
- конфигурации сервера (my.cnf / my.ini);
- большие выдачи данных (более 1000 строк);
- нестойкое соединение;
- распределенная или кластерная конфигурация;
- слабое проектирование таблиц.
Далее мы обратимся ко всем этим проблемам. Также, установите Percona , если вы еще не используете эту встроенную замену стандартному MySQL - это придаст сильное увеличение мощности базы данных.
Что такое индексы?
Индексы используются в MySQL для поиска строк с указанными значениями колонок, например, с командой WHERE. Без индексов, MySQL должна, начиная с первой строки, прочитать всю таблицу в поисках релевантных значений. Чем больше таблица, тем больше затрат.
Если таблица имеет индексы на колонках, которые будут использованы в запросе, MySQL быстро найдет расположения необходимой информации без просмотра всей таблицы. Это гораздо быстрее, чем последовательный поиск в каждой строке.
Нестойкое соединение?
Когда ваше приложение подключается к базе данных и настроено устойчивое соединение, оно будет использоваться каждый раз без надобности каждый раз открывать новое соединение. Это оптимальное решение для рабочей среды.
Уменьшаем частое повторение одинаковых запросов
Наиболее быстрый и эффективный способ, который я нашел для этого - это создание хранилища запросов и результатов их выполнения с помощью Memcached или Redis.
С Memcache вы можете легко положить в кэш результат выполнения вашего запроса, например, следующим образом:
connect("localhost",11211);
$cacheResult = $cache->get("key-name");
if($cacheResult){
//не нуждаемся в запросе
$result = $cacheResult;
} else {
//запускаем ваш запрос
$mysqli = mysqli("p:localhost","username","password","table"); //добавляйте p: для договременного хранения
$sql = "SELECT * FROM posts
LEFT JOIN userInfo using (UID) WHERE posts.post_type = "post" || posts.post_type = "article"
ORDER BY column LIMIT 50";
$result = $mysqli->query($sql);
$memc->set("key-name", $result->fetch_array(), MEMCACHE_COMPRESSED,86400);
}
//Пароль $cacheResult в шаблон
$template->assign("posts", $cacheResult);
?>
Теперь тяжелый запрос, использующий LEFT JOIN, будет выполняться только раз за каждые 86 400 секунд (то есть раз в сутки), что значительно уменьшит нагрузку MySQL сервера, оставив ресурсы для других соединений.
Примечание: Допишите p: в начале аргумента хоста MySQLi для создания постоянного соединения.
Распределенная или кластерная конфигурация
Когда данных становится все больше, и скорость вашего сервиса идет под уклон, паника может овладеть вами. Быстрым решением может стать распределения ресурсов (sharding). Однако я не рекомендую делать это, если вы не обладаете хорошим опытом, поскольку распределение по своей сути делает структуры данных сложнейшими.
Слабое проектирование таблиц
Создание схем баз данных не является сложной работой, если следовать таким золотым правилам, как работа с ограничениями и знание того, что будет эффективным. Например, хранение изображений в ячейках типа BLOB очень смущает - лучше храните путь к файлу в ячейке VARCHAR, это является гораздо лучшим решением.
Обеспечение правильного проектирования для нужного использования является первостепенным в создании вашего приложения. Храните различные данные в различных таблицах (например, категории и статьи) и убедитесь, что отношения к другу (many to one) и один ко многим (one to many) могут быть легко связаны с идентификаторами (ID). Использование FOREIGN KEY в MySQL идеально подходит для хранения каскадных данных в таблицах.
При создании таблицы помните следующее:
- Создавайте эффективные таблицы для решения ваших задач, а не заполняйте таблицы лишними данными и связями.
- Не ожидайте от MySQL выполнения вашей бизнес логики или програмности - данные должны быть готовы к вставке строки вашей скриптовым языком. Например, если вам нужно отсортировать список в случайном порядке, сделайте это в массиве PHP, не используя ORDER BY из арсенала MySQL.
- Используйте индексные типы UNIQUE для уникальных наборов данных и применяйте ON DUPLICATE KEY UPDATE, чтобы хранить дату обновленной, например, для того, чтобы знать, когда строка была в последний раз изменена.
- Используйте тип данных INT для сохранения целых чисел. Если вы не укажете размер типа данных, MySQL сделает это за вас.
Основы оптимизации
Для эффективной оптимизации мы должны применять три подхода к вашему приложению:
- Анализ (логирование медленных запросов, изучение системы, анализ запросов и проектирование базы данных)
- Требования к исполнению (сколько пользователей)
- Ограничения технологий (скорость железа, неправильное использование MySQL)
Анализ может быть сделан несколькими путями. Сначала мы рассмотрим наиболее очевидные способы, чтобы заглянуть под капот вашей MySQL, в котором выполняются запросы. Самый первый инструмент оптимизации в вашем арсенале это EXPLAIN. Если добавить этот оператор перед вашим запросом по SELECT, результат запроса будет таким:
Колонки, вы видите, сохраняют важную информацию о запросе. Колонки, на которые вы должны обратить наибольшее внимание это possible_keys и Extra.
Колонка possible_keys покажет индексы, в которые MySQL имел доступ, чтобы выполнить запрос. Иногда нужно назначить индексы, чтобы запрос выполнялся быстрее. Колонка Extra покажет, были ли использованы дополнительные WHEREили ORDER BY. Наиболее важно обратить внимание, есть ли Using Filesort в выводе.
Что делает Using Filesort, указано в справке MySQL:
MySQL должен выполнить дополнительный проход, чтобы понять, как вернуть строки в отсортированном виде. Это сортировка происходит проходом по всем строкам в соответствии с типом объединения и сохраняет ключ к сортировке и указатель на строку для всех строк, совпадающих с условным выражением WHERE. Ключи сортируются и строки возвращаются в нужном порядке.
Лишний проход замедлит ваше приложение, этого нужно избегать, чего бы это ни стоило. Другой критический результат Extra, который мы должны избегать - это Using temporary. Он говорит о том, что MySQL пришлось создать временную таблицу для выполнения запроса. Очевидно, это ужасное использования MySQL. В таком случае результат запроса должен быть сохранен в Redis или Memcache и не выполняться пользователями лишний раз.
Чтобы избежать проблемы с Using Filesort мы должны увериться, что MySQL использует INDEX. Сейчас указано несколько ключей в possible_keys, из которых можно выбирать, но MySQL может выбрать только один индекс для финального запроса. Также индексы могут быть составлены из нескольких колонок, также вы можете ввести подсказки (хинты) для оптимизатора MySQL, указывая на индексы, что вы создали.
Хинтинг индексов
Оптимизатор MySQL будет использовать статистику, основанную на запросах таблиц, чтобы выбрать лучший индекс для выполнения запроса. Он действует достаточно просто, основываясь на встроенной статистической логике, поэтому имея несколько вариантов, не всегда делает правильный выбор без помощи хинтинга. Чтобы убедиться, что был использован правильный (или неправильный) ключ, воспользуйтесь ключевым словам FORCE INDEX, USE INDEX и IGNORE INDEX в вашем запросе. Вы можете прочитать больше о хинтинге индексов в справке MySQL
.
Чтобы вывести ключи таблицы, используйте команду SHOW INDEX. Вы можете задать несколько хинтов для использования оптимизатором.
В дополнение к EXPLAIN существует ключевое слово DESCRIBE. Вместе с DESCRIBE можно просматривать информацию из таблицы следующим образом:
Добавляем индекс
Для добавления индексов в MySQL надо использовать синтаксис CREATE INDEX. Есть несколько видов индексов. FULLTEXT Применяется для полнотекстового поиска, а UNIQUE - для хранения уникальных данных.
Чтобы добавить индекс в вашу таблицу, используйте следующий синтаксис:
Mysql> CREATE INDEX idx_bookname ON `books` (bookname(10));
Это создаст индекс на таблице books, которая будет использовать первые 10 букв из колонки, которая хранит названия книг и имеет тип varchar.
В этом случае, любой поиск с запросом WHERE на название книги с совпадением до 10 символов будет давать такой же результат, как и просмотр всей таблицы от начала до конца.
Композитные индексы
Индексы имеют большое влияние на скорость выполнения запросов. Только назначения главного уникального ключа недостаточно - композитные ключи являются реальной областью применения в настройке MySQL, что иногда требует некоторых A/B проверок с использованием EXPLAIN.
Например, если нам нужно ссылаться на две колонки в условии выражения WHERE, композитный ключ будет идеальным решением.
Mysql> CREATE INDEX idx_composite ON users (username, active);
Как только мы создали ключ на основе колонки username, в котором хранится имя пользователя и колонки active типа ENUM, определяющий, активен ли его аккаунт. Теперь все оптимизировано для запроса, который будет использовать WHERE для поиска валидного имени пользователя с активным аккаунтом (active = 1).
Насколько быстра ваша MySQL?
Включим профилирование, чтобы подробнее рассмотреть MySQL запросы. Это можно сделать, выполнив команду set profiling=1, после чего для просмотра результата надо выполнить show profiles.
Если вы используете PDO, выполните следующий код:
$db->query("set profiling=1");
$db->query("select headline, body, tags from posts");
$rs = $db->query("show profiles");
$db->query("set profiling=0"); // отключить профилирование после выполнения запроса
$records = $rs->fetchAll(PDO::FETCH_ASSOC); // получить результаты профилирования
$errmsg = $rs->errorInfo(); //Отлавливаем некоторые ошибки здесь
То же самое можно сделать с помощью mysqli:
$db = new mysqli($host,$username,$password,$dbname);
$db->query("set profiling=1");
$db->query("select headline, body, tags from posts");
if ($result = $db->query("SHOW profiles", MYSQLI_USE_RESULT)) {
while ($row = $result->fetch_row()) {
var_dump($row);
}
$result->close();
}
if ($result = $db->query("show profile for query 1", MYSQLI_USE_RESULT)) {
while ($row = $result->fetch_row()) {
var_dump($row);
}
$result->close();
}
$db->query("set profiling=0");
Это вернет вам профилированные данные, содержащие время выполнения запроса во втором элементе ассоциативного массива.
Array(3) {
=> string(1) "1"
=> string(10) "0.00024300"
=> string(17) "select headline, body, tags from posts"
}
Этот запрос выполнялся 0.00024300 секунд. Это довольно быстро, поэтому не будем беспокоиться. Но когда числа становятся большими, мы должны смотреть глубже.
Перейдите к вашему приложению, чтобы потренироваться на рабочем примере. Проверьте константу DEBUG в конфигурации вашей базы данных, а затем начните изучать систему, включив вывод результатов профилирования с помощью функций var_dump или print_r. Так вы сможете переходить со страницы на страницу в вашем приложении, получив удобное профилирование системы.
Полный аудит работы базы вашего сайта
Чтобы сделать полный аудит ваших запросов, включите логирование. Некоторые разработчики сайтов переживают по поводу того, что логирование сильно влияет на выполнение и дополнительно замедляет запросы. Однако, практика показывает, что разница незначительна.
Чтобы включить логирование в MySQL 5.1.6 используйте глобальную переменную log_slow_queries, также вы можете отметить файл для логирования с помощью переменной slow_query_log_file. Это можно сделать, выполнив следующий запрос:
Set global log_slow_queries = 1;
set global slow_query_log_file = /dev/slow_query.log;
Также это можно указать в файлах конфигурации /etc/my.cnf или my.ini вашего сервера.
После внесения изменений не забудьте перезагрузить MySQL сервер необходимой командой, например service mysql restart, если вы используете Linux.
В версиях MySQL после 5.6.1 переменная log_slow_queries обозначена как устаревшая и вместо нее используется slow_query_log. Также для более удобного дебаггинга можно включить вывод в таблице, задав переменной log_output значение TABLE, однако эта функция доступна только с MySQL 5.6.1.
Log_output = TABLE;
log_queries_not_using_indexes = 1;
long_query_time = 1;
Переменная long_query_time определяет количество секунд, после которых выполнение запроса считается медленным. Значение это 10, а минимум это 0. Также можно указать миллисекунды, используя дробь; сейчас я указал одну секунду. И теперь каждый запрос, который будет выполняться дольше 1 секунды, записывается в логи в таблице.
Логирование будет вестись в таблицах mysql.slow_log и mysql.general_log вашей MySQL базы данных. Чтобы выключить логирование, измените log_output на NONE.
Логирование на рабочем сервере
На рабочем сервере, который обслуживает клиентов, лучше применять логирование только на короткий период и для мониторинга нагрузки, чтобы не создавать лишней нагрузки. Если ваш сервис перегружен и необходимо безотлагательное вмешательство, попробуйте выделить проблему, выполнив SHOW PROCESSLIST, или обратитесь к таблице information_schema.PROCESSLIST, выполнив SELECT * FROM information_schema.PROCESSLIST;.
Логирование всех запросов на рабочем сервере может дать вам много информации и стать хорошим средством для исследовательских целей при проверке проекта, однако логи за большие периоды не дадут вам много полезной информации по сравнению с логами за период до 48 часов (старайтесь отслеживать пиковые нагрузки, чтобы иметь шанс лучше исследовать выполнение запросов).
Примечание: если у вас сайт, переживающей волны трафика и временами почти без него, как, например, спортивный сайт в не сезон, тогда используйте эту информацию для построения и изучения логирования.
Логирование множества запросов
Важно знать не только о запросах, которые выполняются дольше секунду, также необходимо иметь в виду запросы, выполняемые сотни раз. Даже если запросы выполняются быстро, в нагруженной системе они могут оттянуть все ресурсы на себя.
Вот почему всегда нужно быть настороже после внесения изменений в живом проекте - это наиболее критическое время для работы любой базы данных.
Горячий и холодный кэш
Количество запросов и нагрузка сервера имеет сильное влияние на исполнение, также может повлиять на время выполнения запросов. При разработке вы должны взять за правило, что выполнение каждого запроса должно быть не более доли миллисекунды (0.0xx или быстрее) на свободном сервере.
Применение Memcache имеет сильный эффект на нагрузку серверов, освободит ресурсы, которые выполняют запросы. Убедитесь, что вы используете Memcached эффективно и протестовали ваше приложение с горячим кэшем (подгруженными данным) и с холодным кэшем.
Чтобы избежать запуска на рабочем сервере с пустым кэшем, хорошей идеей будет скрипт, который соберет весь необходимый кэш перед запуском сервера, чтобы большой наплыв клиентов не снизил время загрузки системы.
Исправление медленных запросов
Теперь, когда логирование настроено, вы могли найти несколько медленных запросов на вашем сайте. Давайте исправим их! Для примера я покажу несколько распространенных проблем, вы можете встретить и логику их исправления.
Если вы пока не нашли медленного запроса, проверьте настройки long_query_time, если вы пользуетесь этим методом логирования. Иначе, проверив все ваши запросы профилирования (set profiling=1), составьте список запросов, отнимают больше времени, чем доля миллисекунд (0.000x секунд) и начнем из них.
Распространенные проблемы
Вот шесть самых распространенных проблем, которые я находил, оптимизируя MySQL запросы:
ORDER BY и filesort
Предотвращение filesort иногда невозможно из-за выражения ORDER BY. Для оптимизации сохраните результат в Memcache, или выполните сортировку в логике вашего приложения.
Использование ORDER BY вместе с WHERE и LEFT JOIN
ORDER BY очень замедляет выполнение запросов. Если это возможно, старайтесь не использовать ORDER BY. Если же вам необходима сортировка, то используйте сортировку по индексам.
Применение ORDER BY по временным колонками
Просто не делайте этого. Если вам нужно объединить результаты, сделайте это в логике вашего приложения; не используйте фильтрацию или сортировку во временной таблице запроса MySQL. Это требует много ресурсов.
Игнорирование индекса FULLTEXT
Использование LIKE это самый лучший способ сделать полнотекстовый поиск медленным.
Беспричинный выбор большого количества строк
Забыв о LIMIT в вашем запросе можно сильно увеличить время выполнения выборки из базы данных в зависимости от размера таблиц.
Чрезмерное использование JOIN вместо создания композитных таблиц или представления
Когда в одном запросе вы пользуетесь больше чем тремя-четырьмя операторами LEFT JOIN, спросите себя: все ли здесь верно? Продолжайте, если у вас есть на то веская причина, например - запрос используется не часто для вывода в панели администратора, или результат вывода может быть сохранен в кэше. Если же вам нужно выполнять запрос с большим количеством операций объединения таблиц, тогда лучше задуматься о создании композитных таблиц из необходимых столбиков или использовать представления.
Итак
Мы обсудили основы оптимизации и инструменты, необходимые для работы. Мы изучили систему, применяя профилирования и оператор EXPLAIN, чтобы увидеть, что происходит с базой данных, и понять, как можно улучшить структуру.
Также мы посмотрели на несколько примеров и классических ловушек, в которые вы можете попасть, используя MySQL. Используя хинтинг индексов, мы можем увериться в том, что MySQL выберет необходимые индексы, особенно при нескольких выборках в одной таблице. Чтобы продолжить изучение темы, я советую вам посмотреть в сторону Percona project.
Сообщить об опечатке
Текст, который будет отправлен нашим редакторам: