Ich bin der leitende Entwickler einer Software-as-a-Service-Anwendung, die von vielen verschiedenen Kunden verwendet wird. Unsere Software läuft auf einem Cluster von Apache / PHP-Anwendungsservern, die von einem MySQL-Backend unterstützt werden. In einer bestimmten Instanz der Software läuft der PHP-Code zum Abfragen der Liste der Kategorienamen ab, wenn der Kunde mehr als 29 Kategorien hat . Ich weiß, das macht keinen Sinn. Es gibt nichts Besonderes an der Nummer 30, das dies zerstören würde, und andere Kunden haben viel mehr als 30 Kategorien. Das Problem ist jedoch zu 100% reproduzierbar, wenn diese eine Installation 30 oder mehr Kategorien hat und verschwindet, wenn es weniger als 30 Kategorien gibt.
Die fragliche Tabelle lautet:
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 ;
Der betreffende Code fragt die Tabelle rekursiv ab, um alle Kategorien abzurufen. Es gibt a
SELECT * FROM `categories` WHERE `parent`=0 ORDER BY `order`,`name`
Anschließend wird diese Abfrage für jede zurückgegebene Zeile wiederholt, jedoch WHERE parent=$category_id
jedes Mal. (Ich bin sicher, dass dieses Verfahren verbessert werden könnte, aber das ist wahrscheinlich eine andere Frage)
Soweit ich das beurteilen kann, hängt die folgende Abfrage für immer:
SELECT * FROM `categories` WHERE `parent`=22 ORDER BY `order`,`name`
Ich kann diese Abfrage im MySQL-Client auf dem Server einwandfrei ausführen und sie auch ohne Probleme in PHPMyAdmin ausführen.
Beachten Sie, dass nicht diese spezifische Abfrage das Problem ist. Wenn ich DELETE FROM categories WHERE id=22
dann eine andere Abfrage ähnlich der oben genannten hängen werde. Außerdem gibt die obige Abfrage null Zeilen zurück, wenn ich sie manuell ausführe .
Ich vermutete , dass die Tabelle möglicherweise beschädigt, und ich versuchte , REPAIR TABLE
und OPTIMIZE TABLE
doch nether davon berichteten Probleme noch gelöst das Problem. Ich ließ den Tisch fallen und stellte ihn neu her, aber das Problem kehrte zurück. Dies ist genau die gleiche Tabellenstruktur und der gleiche PHP-Code, den andere Kunden ohne Probleme für andere verwenden, einschließlich Kunden mit weit mehr als 30 Kategorien.
Der PHP-Code wird nicht für immer wiederholt. (Dies ist keine Endlosschleife)
Auf dem MySQL-Server wird CentOS Linux mit mysqld Ver 5.0.92-community für pc-linux-gnu auf i686 (MySQL Community Edition (GPL)) ausgeführt.
Die Last auf dem MySQL-Server ist gering: Lastdurchschnitt: 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% hi, 0,3% si, 0,0% st. Vernachlässigbarer Swap wird verwendet (448k)
Wie kann ich dieses Problem beheben? Irgendwelche Vorschläge, was los sein könnte?
UPDATE: Ich habe TRUNCE
die Tabelle bearbeitet und 30 Zeilen Dummy-Daten eingefügt:
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);
Überhaupt keine Eltern , alle Kategorien sind auf der obersten Ebene. Problem ist immer noch da. Die folgende von PHP ausgeführte Abfrage schlägt fehl:
SELECT * FROM `categories` WHERE `parent`=22 ORDER BY `order`,`name`
Hier ist das 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)
UPDATE 2: Ich habe jetzt Folgendes versucht:
- Ich habe diese Tabelle und Daten mit derselben Software auf eine andere Site kopiert. Das Problem folgte nicht der Tabelle. Es scheint auf diese eine Datenbank beschränkt zu sein.
- Ich habe den Index geändert, wie in der Antwort von gbn vorgeschlagen. Das Problem blieb bestehen.
- Ich habe die Tabelle gelöscht und als
InnoDB
Tabelle neu erstellt und die gleichen 30 Testzeilen oben eingefügt. Das Problem blieb bestehen.
Ich vermute, es muss etwas mit dieser Datenbank sein ...
UPDATE 3: Ich habe die Datenbank vollständig gelöscht und unter einem neuen Namen neu erstellt und ihre Daten importiert. Das Problem bleibt bestehen.
Ich habe festgestellt, dass die eigentliche PHP-Anweisung, die hängt, ein Aufruf von ist mysql_query()
. Anweisungen danach werden nie ausgeführt.
Während dieser Aufruf hängt, listet MySQL den Thread als schlafend auf!
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 |
+-------+------------------+-----------------------------+----------------------+---------+------+-------+-----------------------+
UPDATE 4: Ich habe es auf die Kombination von zwei Tabellen, der oben beschriebenen categories
Tabelle und einer media_images
Tabelle mit 556 Zeilen eingegrenzt. Wenn die media_images
Tabelle weniger als 556 Zeilen categories
enthält oder die Tabelle weniger als 30 Zeilen enthält, verschwindet das Problem. Es ist, als wäre es eine Art MySQL-Limit, das ich hier erreiche ...
UPDATE Nr. 5: Ich habe gerade versucht, die Datenbank auf einen anderen MySQL-Server zu verschieben, und das Problem ist behoben ... Es hängt also mit meinem Produktionsdatenbankserver zusammen ...
UPDATE 6: Hier ist der relevante PHP-Code, der jedes Mal hängt:
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");
Dieser Code ist in der Produktion und arbeitet fein auf allen anderen installiert. Nur bei einer Installation hängt es an $res = @mysql_query($q,$this->_link);
. Ich weiß, weil ich das mysql_query
im Debug-Protokoll sehe und nicht das res =
, und wenn ich strace
den PHP-Prozess mache, hängt es daranread(
UPDATE # was auch immer-es-ist-ich-hasse-das- & (# ^ & -ausgabe! Dies ist jetzt bei zwei meiner Kunden passiert . Ich habe gerade gefeuert tcpdump
und es sieht so aus, als würde die Antwort von MySQL nie vollständig gesendet. Der TCP-Stream scheint nur zu hängen, bevor die vollständige MySQL-Antwort gesendet werden kann (ich untersuche dies jedoch noch).
UPDATE # Ich-bin-völlig-verrückt-geworden-aber-es-funktioniert-jetzt-irgendwie: Ok, das macht keinen Sinn, aber ich habe eine Lösung gefunden. Wenn ich der eth2
Schnittstelle des MySQL-Servers eine zweite IP-Adresse zuweise und eine IP für den NFS-Verkehr und die zweite IP für MySQL verwende, verschwindet das Problem. Es ist, als würde ich irgendwie ... die IP-Adresse überladen, wenn beide NFS + MySQL-Daten auf diese IP gehen. Das macht aber keinen Sinn, weil Sie eine IP-Adresse nicht "überladen" können. Eine Schnittstelle sicher sättigen, aber es ist die gleiche Schnittstelle.
Irgendeine Idee, was zum Teufel hier los ist? Dies ist wahrscheinlich eine Unix.SE- oder ServerFault-Frage zu diesem Zeitpunkt ... (Zumindest funktioniert es jetzt ...)
UPDATE # warum-oh-warum: Dieses Problem tritt immer noch auf. Es beginnt sogar mit zwei verschiedenen IPs. Ich kann weiterhin neue private IPs erstellen, aber offensichtlich stimmt etwas nicht.