Я є старшим розробником програми, що використовується в програмі "Програмне забезпечення як послуга", яку використовують багато різних клієнтів. Наше програмне забезпечення працює на кластері серверів додатків 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-спільнотою для pc-linux-gnu на i686 (MySQL Community Edition (GPL))
Навантаження на сервер MySQL низька: середнє завантаження: 0,58, 0,75, 0,73, Cpu (s): 4,6% us, 2,9% sy, 0,0% ni, 92,2% id, 0,0% wa, 0,0% привіт, 0,3% si, 0,0% ст. Використовується незначний своп (448k)
Як я можу вирішити цю проблему? Будь-які пропозиції щодо того, що може статися?
ОНОВЛЕННЯ: я 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
в журналі налагодження, а не the 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-адреси, але явно щось не так.