Sono lo sviluppatore senior di un'applicazione Software-as-a-Service utilizzata da molti clienti diversi. Il nostro software funziona su un cluster di server applicazioni Apache / PHP, alimentato da un back-end MySQL. In una particolare istanza del software, il codice PHP per interrogare l'elenco dei nomi delle categorie è scaduto quando il cliente ha più di 29 categorie . So che questo non ha senso; non c'è nulla di speciale nel numero 30 che potrebbe spezzare questo e altri clienti hanno più di 30 categorie, tuttavia, il problema è riproducibile al 100% quando questa installazione ha 30 o più categorie e scompare quando ci sono meno di 30 categorie.
La tabella in questione è:
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 ;
Il codice in questione interroga ricorsivamente la tabella per recuperare tutte le categorie. Emette a
SELECT * FROM `categories` WHERE `parent`=0 ORDER BY `order`,`name`
Quindi ripete questa query per ogni riga restituita, ma utilizzando WHERE parent=$category_idogni volta. (Sono sicuro che questa procedura potrebbe essere migliorata, ma questa è probabilmente un'altra domanda)
Per quanto ne so, la seguente query è sospesa per sempre:
SELECT * FROM `categories` WHERE `parent`=22 ORDER BY `order`,`name`
Posso eseguire perfettamente questa query nel client mysql sul server e posso eseguirla anche in PHPMyAdmin senza problemi.
Si noti che non è quella query specifica che è il problema. Se ho DELETE FROM categories WHERE id=22quindi una query diversa simile a quella sopra si bloccherà. Inoltre, la query sopra restituisce zero righe quando la eseguo manualmente .
Sospettavo che la tabella può essere danneggiato, e ho provato REPAIR TABLEe OPTIMIZE TABLEma inferiore di questi segnalato problemi né risolto il problema. Ho lasciato cadere il tavolo e ricreato, ma il problema è tornato. Questa è esattamente la stessa struttura della tabella e il codice PHP che altri clienti utilizzano senza problemi per nessun altro, inclusi i clienti che hanno più di 30 categorie.
Il codice PHP non ricorre per sempre. (Questo non è un ciclo infinito)
Il server MySQL esegue CentOS Linux con mysqld Ver 5.0.92-community per pc-linux-gnu su i686 (MySQL Community Edition (GPL))
Il carico sul server MySQL è basso: media del carico: 0,58, 0,75, 0,73, Cpu: 4,6% us, 2,9% sy, 0,0% ni, 92,2% id, 0,0% wa, 0,0% hi, 0,3% si, 0,0% st. Swap trascurabile in uso (448k)
Come posso risolvere questo problema? Qualche suggerimento su cosa potrebbe succedere?
AGGIORNAMENTO: ho modificato TRUNCEla tabella e inserito 30 righe di dati fittizi:
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);
Nessun genitore , tutte le categorie sono ai massimi livelli. il problema è ancora lì. La seguente query, eseguita da PHP, non riesce:
SELECT * FROM `categories` WHERE `parent`=22 ORDER BY `order`,`name`
Ecco il 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)
AGGIORNAMENTO # 2: ora ho provato tutti i seguenti:
- Ho copiato questa tabella e i dati in un sito diverso con lo stesso software. Il problema non ha seguito la tabella. Sembra essere limitato a questo unico database.
- Ho cambiato l'indice come suggerito dalla risposta di gbn. Il problema è rimasto.
- Ho lasciato cadere la tabella e ricreato come
InnoDBtabella e ho inserito le stesse 30 righe di test sopra. Il problema è rimasto.
Sospetto che debba essere qualcosa con questo database ...
AGGIORNAMENTO # 3: Ho completamente eliminato il database e ricreato con un nuovo nome, importando i suoi dati. Il problema rimane.
Ho scoperto che l'effettiva dichiarazione PHP che si blocca è una chiamata a mysql_query(). Le dichiarazioni dopo questa non vengono mai eseguite.
Mentre quella chiamata si blocca, MySQL elenca il thread come inattivo!
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 |
+-------+------------------+-----------------------------+----------------------+---------+------+-------+-----------------------+
AGGIORNAMENTO # 4: l' ho ridotto alla combinazione di due tabelle, la categoriestabella sopra dettagliata e una media_imagestabella con 556 righe. Se la media_imagestabella contiene meno di 556 righe o la categoriestabella contiene meno di 30 righe, il problema scompare. È come se fosse una sorta di limite di MySQL che sto colpendo qui ...
AGGIORNAMENTO # 5: Ho appena provato a spostare il database su un altro server MySQL del tutto e il problema è andato via ... Quindi è legato al mio server di database di produzione ...
AGGIORNAMENTO N. 6: Ecco il codice PHP pertinente che si blocca ogni volta:
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");
Questo codice è in produzione e funziona perfettamente su tutte le altre installazioni. Solo su una installazione, si blocca a $res = @mysql_query($q,$this->_link);. Lo so perché vedo il mysql_querylog di debug e non il res =, e quando ho straceil processo PHP, è bloccatoread(
AGGIORNAMENTO # qualunque sia-odio-odio-questo- & (# ^ & -issue! Questo è ora iniziato a accadere a due miei clienti. Ho appena fatto fuoco tcpdumpe sembra che la risposta di MySQL non sia mai stata inviata completamente. Il flusso TCP sembra bloccarsi prima di poter inviare l'intera risposta MySQL. (Sto ancora indagando)
AGGIORNAMENTO # Sono-completamente-impazzito-ma-funziona-ora-un-tipo: ok, questo non ha senso, ma ho trovato una soluzione. Se assegno un secondo indirizzo IP all'interfaccia del server MySQL eth2e utilizzo un IP per il traffico NFS e il secondo IP per MySQL, il problema scompare. È come se in qualche modo ... sovraccaricassi l'indirizzo IP se sia il traffico NFS + MySQL sia quello IP. Ma questo ha senso zero perché non è possibile "sovraccaricare" un indirizzo IP. Saturando sicuramente un'interfaccia, ma è la stessa interfaccia.
Hai idea di cosa diavolo sta succedendo qui? Questa è probabilmente una domanda unix.SE o ServerFault a questo punto ... (Almeno ora funziona ...)
AGGIORNAMENTO # why-oh-why: il problema persiste. Ha iniziato a succedere anche usando due IP diversi. Posso continuare a creare nuovi IP privati, ma chiaramente qualcosa non va.