Я являюсь старшим разработчиком приложения Software-as-a-Service, используемого многими разными клиентами. Наше программное обеспечение работает на кластере серверов приложений Apache / PHP, работающих на базе MySQL. В одном конкретном экземпляре программного обеспечения код PHP для запроса списка названий категорий истекает, когда у клиента более 29 категорий . Я знаю, что это не имеет смысла; нет ничего особенного в числе 30, которое могло бы нарушить это, и другие клиенты имеют более 30 категорий, однако, проблема воспроизводима на 100%, когда эта установка имеет 30 или более категорий и исчезает, когда имеется менее 30 категорий.
Рассматриваемая таблица:
CREATE TABLE IF NOT EXISTS `categories` (
`id` int(10) unsigned NOT NULL auto_increment,
`name` varchar(64) NOT NULL,
`title` varchar(128) NOT NULL,
`parent` int(10) unsigned NOT NULL,
`keywords` varchar(255) NOT NULL,
`description` text NOT NULL,
`status` enum('Active','Inactive','_Deleted','_New') NOT NULL default 'Active',
`style` enum('_Unknown') default NULL COMMENT 'Autoenum;',
`order` smallint(5) unsigned NOT NULL,
`created_at` datetime NOT NULL,
`modified_at` datetime default NULL,
PRIMARY KEY (`id`),
KEY `name` (`name`),
KEY `parent` (`parent`),
KEY `created_at` (`created_at`),
KEY `modified_at` (`modified_at`),
KEY `status` (`status`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 COMMENT='R2' AUTO_INCREMENT=33 ;
Рассматриваемый код рекурсивно запрашивает таблицу для получения всех категорий. Выдает
SELECT * FROM `categories` WHERE `parent`=0 ORDER BY `order`,`name`
И затем повторяет этот запрос для каждой возвращаемой строки, но использует WHERE parent=$category_id
каждый раз. (Я уверен, что эта процедура может быть улучшена, но это, вероятно, другой вопрос)
Насколько я могу судить, следующий запрос зависает навсегда:
SELECT * FROM `categories` WHERE `parent`=22 ORDER BY `order`,`name`
Я могу выполнить этот запрос в клиенте MySQL на сервере совершенно нормально, и я могу выполнить его в PHPMyAdmin также без проблем.
Обратите внимание, что проблема не в конкретном запросе . Если я DELETE FROM categories WHERE id=22
тогда другой запрос, похожий на тот, что выше, будет зависать. Кроме того, запрос выше возвращает ноль строк, когда я запускаю его вручную .
Я подозревал , что таблица может быть повреждена, и я попытался REPAIR TABLE
и , OPTIMIZE TABLE
но Пустоты из них сообщили о проблемах , ни решить эту проблему. Я бросил стол и воссоздал, но проблема вернулась. Это точно такая же структура таблиц и PHP-код, которые используют другие клиенты без проблем для кого-либо еще, включая клиентов, которые имеют более 30 категорий.
Код PHP не повторяется вечно. (Это не бесконечный цикл)
Сервер MySQL работает под управлением CentOS Linux с mysqld Ver 5.0.92-community для pc-linux-gnu на i686 (MySQL Community Edition (GPL))
Загрузка на сервере MySQL низкая: средняя загрузка: 0,58, 0,75, 0,73, процессор (ы): 4,6% us, 2,9% sy, 0,0% ni, 92,2% id, 0,0% wa, 0,0% hi, 0,3% si, 0,0% ул. Незначительный своп используется (448 КБ)
Как я могу устранить эту проблему? Какие-нибудь предложения относительно того, что могло бы продолжаться?
ОБНОВЛЕНИЕ: я TRUNCE
редактировал таблицу и вставил 30 строк фиктивных данных:
INSERT INTO `categories` (`id`, `name`, `title`, `parent`, `keywords`, `description`, `status`, `style`, `order`, `created_at`, `modified_at`) VALUES
(1, 'New Category', '', 0, '', '', 'Inactive', NULL, 1, '2011-10-25 12:06:30', '2011-10-25 12:06:34'),
(2, 'New Category', '', 0, '', '', 'Inactive', NULL, 2, '2011-10-25 12:06:39', '2011-10-25 12:06:40'),
(3, 'New Category', '', 0, '', '', 'Inactive', NULL, 3, '2011-10-25 12:06:41', '2011-10-25 12:06:42'),
(4, 'New Category', '', 0, '', '', 'Inactive', NULL, 4, '2011-10-25 12:06:46', '2011-10-25 12:06:47'),
(5, 'New Category', '', 0, '', '', 'Inactive', NULL, 5, '2011-10-25 12:06:49', NULL),
(6, 'New Category', '', 0, '', '', 'Inactive', NULL, 6, '2011-10-25 12:06:51', '2011-10-25 12:06:52'),
(7, 'New Category', '', 0, '', '', 'Inactive', NULL, 7, '2011-10-25 12:06:53', '2011-10-25 12:06:54'),
(8, 'New Category', '', 0, '', '', 'Inactive', NULL, 8, '2011-10-25 12:06:56', '2011-10-25 12:06:57'),
(9, 'New Category', '', 0, '', '', 'Inactive', NULL, 9, '2011-10-25 12:06:59', '2011-10-25 12:06:59'),
(10, 'New Category', '', 0, '', '', 'Inactive', NULL, 10, '2011-10-25 12:07:01', '2011-10-25 12:07:01'),
(11, 'New Category', '', 0, '', '', 'Inactive', NULL, 11, '2011-10-25 12:07:03', '2011-10-25 12:07:03'),
(12, 'New Category', '', 0, '', '', 'Inactive', NULL, 12, '2011-10-25 12:07:05', '2011-10-25 12:07:05'),
(13, 'New Category', '', 0, '', '', 'Inactive', NULL, 13, '2011-10-25 12:07:06', '2011-10-25 12:07:07'),
(14, 'New Category', '', 0, '', '', 'Inactive', NULL, 14, '2011-10-25 12:07:08', '2011-10-25 12:07:09'),
(15, 'New Category', '', 0, '', '', 'Inactive', NULL, 15, '2011-10-25 12:07:11', '2011-10-25 12:07:12'),
(16, 'New Category', '', 0, '', '', 'Inactive', NULL, 16, '2011-10-25 12:07:13', '2011-10-25 12:07:14'),
(17, 'New Category', '', 0, '', '', 'Inactive', NULL, 17, '2011-10-25 12:09:41', '2011-10-25 12:09:42'),
(18, 'New Category', '', 0, '', '', 'Inactive', NULL, 18, '2011-10-25 12:09:47', NULL),
(19, 'New Category', '', 0, '', '', 'Inactive', NULL, 19, '2011-10-25 12:09:48', NULL),
(20, 'New Category', '', 0, '', '', 'Inactive', NULL, 20, '2011-10-25 12:09:48', NULL),
(21, 'New Category', '', 0, '', '', 'Inactive', NULL, 21, '2011-10-25 12:09:49', NULL),
(22, 'New Category', '', 0, '', '', 'Inactive', NULL, 22, '2011-10-25 12:09:50', NULL),
(23, 'New Category', '', 0, '', '', 'Inactive', NULL, 23, '2011-10-25 12:09:51', NULL),
(24, 'New Category', '', 0, '', '', 'Inactive', NULL, 24, '2011-10-25 12:09:51', NULL),
(25, 'New Category', '', 0, '', '', 'Inactive', NULL, 25, '2011-10-25 12:09:52', NULL),
(26, 'New Category', '', 0, '', '', 'Inactive', NULL, 26, '2011-10-25 12:09:53', NULL),
(27, 'New Category', '', 0, '', '', 'Inactive', NULL, 27, '2011-10-25 12:09:54', NULL),
(28, 'New Category', '', 0, '', '', 'Inactive', NULL, 28, '2011-10-25 12:09:55', NULL),
(29, 'New Category', '', 0, '', '', 'Inactive', NULL, 29, '2011-10-25 12:09:56', NULL),
(30, 'New Category', '', 0, '', '', 'Inactive', NULL, 30, '2011-10-25 12:09:57', NULL);
Родителей вообще нет , все категории находятся на высшем уровне. проблема все еще там. Следующий запрос, выполненный PHP, не выполняется:
SELECT * FROM `categories` WHERE `parent`=22 ORDER BY `order`,`name`
Вот это EXPLAIN
:
mysql> EXPLAIN SELECT * FROM `categories` WHERE `parent`=22 ORDER BY `order`,`name`;
+----+-------------+------------+------+---------------+--------+---------+-------+------+-----------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+------------+------+---------------+--------+---------+-------+------+-----------------------------+
| 1 | SIMPLE | categories | ref | parent | parent | 4 | const | 1 | Using where; Using filesort |
+----+-------------+------------+------+---------------+--------+---------+-------+------+-----------------------------+
1 row in set (0.00 sec)
ОБНОВЛЕНИЕ № 2: Теперь я попробовал все следующее:
- Я скопировал эту таблицу и данные на другой сайт с тем же программным обеспечением. Проблема не следовала за таблицей. Кажется, он ограничен этой базой данных.
- Я изменил индекс, как подсказал ответ gbn. Проблема осталась.
- Я отбросил таблицу и воссоздал ее как
InnoDB
таблицу и вставил те же 30 тестовых строк выше. Проблема осталась.
Я подозреваю, что это должно быть что-то с этой базой данных ...
ОБНОВЛЕНИЕ № 3: Я полностью удалил базу данных и воссоздал ее под новым именем, импортировав ее данные. Проблема остается.
Я обнаружил, что фактический оператор PHP, который зависает, является вызовом mysql_query()
. Заявления после этого никогда не исполняются.
Пока этот вызов зависает, MySQL выводит поток как спящий!
mysql> show full processlist;
+-------+------------------+-----------------------------+----------------------+---------+------+-------+-----------------------+
| Id | User | Host | db | Command | Time | State | Info |
+-------+------------------+-----------------------------+----------------------+---------+------+-------+-----------------------+
| 5560 | root | localhost | problem_db | Query | 0 | NULL | show full processlist |
----- many rows which have no relevancy; only rows from this customer's app are shown ------
| 16341 | shared_db | oak01.sitepalette.com:53237 | shared_db | Sleep | 308 | | NULL |
| 16342 | problem_db | oak01.sitepalette.com:60716 | problem_db | Sleep | 307 | | NULL |
| 16344 | shared_db | oak01.sitepalette.com:53241 | shared_db | Sleep | 308 | | NULL |
| 16346 | problem_db | oak01.sitepalette.com:60720 | problem_db | Sleep | 308 | | NULL |
+-------+------------------+-----------------------------+----------------------+---------+------+-------+-----------------------+
ОБНОВЛЕНИЕ № 4: Я сузил его до комбинации двух таблиц, categories
таблицы, описанной выше, и media_images
таблицы с 556 строками. Если media_images
таблица содержит менее 556 строк или categories
таблица содержит менее 30 строк, проблема устраняется. Как будто это какой-то лимит MySQL, который я бью здесь ...
ОБНОВЛЕНИЕ № 5: Я просто попытался переместить базу данных на другой сервер MySQL, и проблема исчезла ... Так что это связано с моим рабочим сервером базы данных ...
ОБНОВЛЕНИЕ № 6: Вот соответствующий код PHP, который висит каждый раз:
public function find($type,$conditions='',$order='',$limit='')
{
if($this->_link == self::AUTO_LINK)
$this->_link = DFStdLib::database_connect();
if(is_resource($this->_link))
{
$q = "SELECT ".($type==_COUNT?'COUNT(*)':'*')." FROM `{$this->_table}`";
if($conditions)
{
$q .= " WHERE $conditions";
}
if($order)
{
$q .= " ORDER BY $order";
}
if($limit)
{
$q .= " LIMIT $limit";
}
switch($type)
{
case _ALL:
DFSkel::log(DFSkel::LOG_DEBUG,"mysql_query($q,$this->_link);");
$res = @mysql_query($q,$this->_link);
DFSkel::log(DFSkel::LOG_DEBUG,"res = $res");
Этот код находится в производстве и отлично работает на всех других установках. Просто на одной установке он висит на $res = @mysql_query($q,$this->_link);
. Я знаю, потому что я вижу mysql_query
в журнале отладки, а не res =
, и когда я strace
PHP-процесс, он зависает наread(
ОБНОВЛЕНИЕ # что бы это ни было - я ненавижу это - & (# ^ & -issue! Это теперь начало происходить с двумя моими клиентами. Я только что загорелся, tcpdump
и похоже, что ответ от MySQL никогда не отправляется полностью. Кажется, что поток TCP просто зависает, прежде чем можно будет отправить полный ответ MySQL (однако я все еще изучаю)
ОБНОВЛЕНИЕ # Я-ушел-полностью-сумасшедший-но-это-работает-сейчас-вроде: Хорошо, это не имеет смысла, но я нашел решение. Если я назначу второй IP-адрес eth2
интерфейсу сервера MySQL и использую один IP-адрес для трафика NFS и второй IP-адрес для MySQL, проблема исчезнет. Как будто я как-то ... перегружаю IP-адрес, если оба трафика NFS + MySQL идут на этот IP. Но это бессмысленно, потому что вы не можете «перегружать» IP-адрес. Насыщение интерфейса конечно, но это тот же интерфейс.
Есть идеи, что, черт возьми, здесь происходит? Это, вероятно, вопрос unix.SE или ServerFault на данный момент ... (По крайней мере, сейчас это работает ...)
ОБНОВЛЕНИЕ # почему-о-почему: эта проблема все еще происходит. Это начало происходить даже с использованием двух разных IP-адресов. Я могу продолжать создавать новые частные IP-адреса, но явно что-то не так.
источник
Ответы:
Для общего описания того, что именно происходит в плане запроса, вы можете попробовать ПРОФИЛИРОВАНИЕ
Это в основном поможет вам определить, где зависание.
Конечно, это работает только если вы скомпилировали MySQL с
enable-profiling
.источник
Идеи (не уверен, относится ли это к MyISAM, я работаю с InnoDB)
Измените индекс «родитель», чтобы он был на 3 столбца: родитель, порядок, имя. Это соответствует ГДЕ .. ЗАКАЗАТЬ ПО
Удалить
SELECT *
. Возьмите только те колонки, которые вам нужны. Добавьте любые другие столбцы в индекс «родитель»Это позволит оптимизатору использовать только индекс, потому что он сейчас покрывает. В нынешнем виде вы должны прочитать всю таблицу, потому что индексы для этого запроса бесполезны.
источник
parent
индекса на(parent, order, name)
Я хотел бы проверить несколько вещей на сервере производственной базы данных
netstat | grep -i mysql | grep TIME_WAIT
skip-host-cache
иskip-name-resolve
обойти использование DNS в mysqld. Таким образом, я могу относиться к ответу @ marcioAlmada как к контрольной точке для просмотра.Если вы считаете, что ни одна из этих проверок не является полезной, пожалуйста, прокомментируйте как можно скорее и дайте мне знать, чтобы я мог удалить свой ответ.
источник
/var
есть какие-то плохие блоки (это на RAID10), но я могу легко ошибаться. Я проверю netstat, хорошая идея там! Я не использую,mysql_pconnect
но проверим сеть / DNS / и т.д.dmesg
. Если у вас нет аппаратного RAID, в этом случае проверьте программу аппаратного рейд-монитора.TIME_WAIT
соединение MySQL. Их не так много ... Таблица не перегружена.Я бы сказал, что ты ударил Шрединбаг . Вы можете попытаться выполнить
die()
запрос после или перед вашим запросом и попытаться просмотреть код, дляif statements
которого это происходит очень редко. Трудно сказать, что зависает, когда у нас нет вашего кода.РЕДАКТИРОВАТЬ: я бы сказал, что это может быть
который (я предполагаю) создает соединение каждый раз, когда вызывается функция. Это может быть проблемой. Каковы ваши max_connections в my.cnf?
источник
mysql_query()
tcpdump
в ближайшие несколько дней. Если это действительно является проблемой PHP, то я должен опубликовать новый вопрос о SO.$this->_link
на константу:self::AUTO_LINK
. 2. Даже если бы я был, этот код находится в if:,if($this->_link == self::AUTO_LINK)
и следующая строка$this->_link = DFStdLib::database_connect();
изменяет значение,$this->_link
чтобы онif
больше не запускался. Я уверен, что есть только одно соединение с базой данных на поток. (См. Список процессов)Некоторые попытки:
Брандмауэры ?? Существует ли какой-либо брандмауэр, блокирующий ваше приложение и не позволяющий ему делать какие-либо запросы к вашему производственному серверу баз данных или наоборот?
Используете ли вы имя домена в конфигурации подключения или IP-адрес? Использование доменного имени может немного замедлить взаимодействие с базой данных, и это в сочетании с коротким максимальным временем выполнения скрипта PHP приведет к вечному зависанию
Последнее предложение, по-видимому, объясняет странное поведение переменных при переключении серверов баз данных. Один может отвечать намного быстрее, чем другой, и поскольку для каждой найденной записи у вас будет вторичный запрос, этот гипотетический объяснение объясняет, почему приложение задерживается только с определенным количеством запрашиваемых результатов (> 30).
По крайней мере, мы пришли к первичному выводу. Определенно проблема не в MySQL сервере istelf. Взглянул на документацию, и, кажется, нет никаких ограничений возможностей, которые подходят для вашей конкретной ситуации, также у меня никогда не было проблем с рекурсивными таблицами и определенным количеством записей.
Надеюсь, это поможет.
источник
Вы пытались обновить команду mysql_query (), чтобы она стала родным драйвером PHP5? MySQLi :: запрос ()? Не уверен, что это что-нибудь сделает, но может стоить того.
источник