Domanda

Avevo tutte le mie tabelle in myISAM ma il blocco a livello di tabella stava iniziando a uccidermi quando avevo lunghi lavori di aggiornamento. Ho convertito le mie tabelle primarie in InnoDB e ora molte delle mie query impiegano più di 1 minuto per completarsi dove erano quasi istantanee su myISAM. Di solito sono bloccati nel passaggio Risultato dell'ordinamento . Ho fatto qualcosa di sbagliato?

Ad esempio:

SELECT * FROM `metaward_achiever` 
 INNER JOIN `metaward_alias` ON (`metaward_achiever`.`alias_id` = `metaward_alias`.`id`) 
 WHERE `metaward_achiever`.`award_id` = 1507  
 ORDER BY `metaward_achiever`.`modified` DESC 
 LIMIT 100  

Ora impiega circa 90 secondi. Ecco la descrizione:

+----+-------------+-------------------+--------+-------------------------------------------------------+----------------------------+---------+---------------------------------+-------+-----------------------------+
| id | select_type | table             | type   | possible_keys                                         | key                        | key_len | ref                             | rows  | Extra                       |
+----+-------------+-------------------+--------+-------------------------------------------------------+----------------------------+---------+---------------------------------+-------+-----------------------------+
|  1 | SIMPLE      | metaward_achiever | ref    | metaward_achiever_award_id,metaward_achiever_alias_id | metaward_achiever_award_id | 4       | const                           | 66424 | Using where; Using filesort | 
|  1 | SIMPLE      | metaward_alias    | eq_ref | PRIMARY                                               | PRIMARY                    | 4       | paul.metaward_achiever.alias_id |     1 |                             | 
+----+-------------+-------------------+--------+-------------------------------------------------------+----------------------------+---------+---------------------------------+-------+-----------------------------+

Sembra che ora MOLTE delle mie query rimangano bloccate nel " Risultato dell'ordinamento " passaggio:

mysql> show processlist;
+--------+------+-----------+------+---------+------+----------------+------------------------------------------------------------------------------------------------------+
| Id     | User | Host      | db   | Command | Time | State          | Info                                                                                                 |
+--------+------+-----------+------+---------+------+----------------+------------------------------------------------------------------------------------------------------+
| 460568 | paul | localhost | paul | Query   |    0 | NULL           | show processlist                                                                                     | 
| 460638 | paul | localhost | paul | Query   |    0 | Sorting result | SELECT `metaward_achiever`.`id`, `metaward_achiever`.`modified`, `metaward_achiever`.`created`, `met | 
| 460710 | paul | localhost | paul | Query   |   79 | Sending data   | SELECT `metaward_achiever`.`id`, `metaward_achiever`.`modified`, `metaward_achiever`.`created`, `met | 
| 460722 | paul | localhost | paul | Query   |   49 | Updating       | UPDATE `metaward_alias` SET `modified` = '2009-09-15 12:43:50', `created` = '2009-08-24 11:55:24', ` | 
| 460732 | paul | localhost | paul | Query   |   25 | Sorting result | SELECT `metaward_achiever`.`id`, `metaward_achiever`.`modified`, `metaward_achiever`.`created`, `met | 
+--------+------+-----------+------+---------+------+----------------+------------------------------------------------------------------------------------------------------+
5 rows in set (0.00 sec)

Qual è il motivo per cui quel semplice aggiornamento è bloccato per 49 secondi?

Se aiuta, ecco gli schemi:

| metaward_alias | CREATE TABLE `metaward_alias` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `modified` datetime NOT NULL,
  `created` datetime NOT NULL,
  `string_id` varchar(255) DEFAULT NULL,
  `shortname` varchar(100) NOT NULL,
  `remote_image` varchar(500) DEFAULT NULL,
  `image` varchar(100) NOT NULL,
  `user_id` int(11) DEFAULT NULL,
  `type_id` int(11) NOT NULL,
  `md5` varchar(32) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `string_id` (`string_id`),
  KEY `metaward_alias_user_id` (`user_id`),
  KEY `metaward_alias_type_id` (`type_id`)
) ENGINE=InnoDB AUTO_INCREMENT=858381 DEFAULT CHARSET=utf8 | 

| metaward_award | CREATE TABLE `metaward_award` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `modified` datetime NOT NULL,
  `created` datetime NOT NULL,
  `string_id` varchar(20) NOT NULL,
  `owner_id` int(11) NOT NULL,
  `name` varchar(100) NOT NULL,
  `description` longtext NOT NULL,
  `owner_points` int(11) NOT NULL,
  `url` varchar(500) NOT NULL,
  `remote_image` varchar(500) DEFAULT NULL,
  `image` varchar(100) NOT NULL,
  `parent_award_id` int(11) DEFAULT NULL,
  `slug` varchar(110) NOT NULL,
  `true_points` double DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `string_id` (`string_id`),
  KEY `metaward_award_owner_id` (`owner_id`),
  KEY `metaward_award_parent_award_id` (`parent_award_id`),
  KEY `metaward_award_slug` (`slug`),
  KEY `metaward_award_name` (`name`)
) ENGINE=InnoDB AUTO_INCREMENT=122176 DEFAULT CHARSET=utf8 | 

| metaward_achiever | CREATE TABLE `metaward_achiever` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `modified` datetime NOT NULL,
  `created` datetime NOT NULL,
  `award_id` int(11) NOT NULL,
  `alias_id` int(11) NOT NULL,
  `count` int(11) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `metaward_achiever_award_id` (`award_id`),
  KEY `metaward_achiever_alias_id` (`alias_id`)
) ENGINE=InnoDB AUTO_INCREMENT=77175366 DEFAULT CHARSET=utf8 | 

E questi nel mio my.cnf

innodb_file_per_table
innodb_buffer_pool_size = 2048M
innodb_additional_mem_pool_size = 16M
innodb_flush_method=O_DIRECT
È stato utile?

Soluzione

Questo è un set di risultati di grandi dimensioni (66.424 righe) che MySQL deve ordinare manualmente. Prova ad aggiungere un indice a metaward_achiever.modified.

Esiste una limitazione con MySQL 4.x che consente a MySQL di utilizzare solo un indice per tabella. Poiché utilizza l'indice sulla colonna metaward_achiever.award_id per la selezione WHERE, non può utilizzare l'indice su metaward_achiever.modified per l'ordinamento. Spero che tu stia usando MySQL 5.x, che potrebbe aver migliorato questo.

Puoi vederlo spiegando questa query semplificata:

SELECT * FROM `metaward_achiever` 
 WHERE `metaward_achiever`.`award_id` = 1507  
 ORDER BY `metaward_achiever`.`modified` DESC 
 LIMIT 100

Se puoi ottenerlo utilizzando gli indici sia per la selezione WHERE che per l'ordinamento, allora sei pronto.

Puoi anche creare un indice composto con metaward_achiever.award_id e metaward_achiever. Se MySQL non lo utilizza, puoi suggerirlo o rimuovere quello solo su award_id.

In alternativa, se riesci a sbarazzarti di metaward_achiever.id e rendere metaward_achiever.award_id la tua chiave primaria e aggiungere una chiave su metaward_achiever.modified, o meglio rendere metaward_achiever.award_id combinato con metaward.modified la tua chiave primaria, allora ' starò davvero bene.

Puoi provare a ottimizzare l'ordinamento dei file modificando le impostazioni. Sfortunatamente, non ne ho esperienza, poiché il nostro DBA gestisce la configurazione, ma potresti voler dare un'occhiata a questo fantastico blog: http://www.mysqlperformanceblog.com/

Ecco un articolo su filesort in particolare: http://s.petrunia.net/blog/?p=24

Altri suggerimenti

Come scritto in Dovresti passare da MyISAM a Innodb? (che è piuttosto recente):

  

Innodb ha bisogno dell'ottimizzazione Come nota finale sulla migrazione da MyISAM a Innodb, dovrei menzionare l'ottimizzazione di Innodb. Innodb deve essere ottimizzato. Veramente. MyISAM per molte applicazioni può funzionare bene con i valori predefiniti. Ho visto centinaia di database GB eseguiti con MyISAM con impostazioni predefinite e ha funzionato ragionevolmente. Innodb ha bisogno di risorse e non funzionerà molto bene con le impostazioni predefinite. L'ottimizzazione di MyISAM dalle impostazioni predefinite raramente fornisce un guadagno di oltre 2-3 volte mentre può essere fino a 10-50 volte per le tabelle Innodb, in particolare per i carichi di lavoro intensivi di scrittura. Controlla qui per i dettagli.

Quindi, riguardo alle impostazioni di MySQL Innodb, l'autore ha scritto in Nozioni di base sull'ottimizzazione delle prestazioni di Innodb :

  

I più importanti sono:

     

innodb_buffer_pool_size 70-80% di   la memoria è una scommessa sicura. L'ho impostato su 12G   su scatola da 16 GB.

     

AGGIORNAMENTO : se stai cercando di più   dettagli, consulta la guida dettagliata su    ottimizzazione del pool di buffer innodb

     

innodb_log_file_size & # 8211; Questo dipende da   la tua velocità di recupero richiede solo 256M   sembra essere un buon equilibrio tra   tempo di recupero ragionevole e buono   prestazioni

     

innodb_log_buffer_size = 4M 4M è buono   per la maggior parte dei casi, a meno che tu non ti stia riferendo   grandi BLOB in Innodb in questo caso   aumentalo un po '.

     

innodb_flush_log_at_trx_commit = 2 Se non sei preoccupato per ACID e puoi perdere transazioni per l'ultimo o due secondi in caso di crash del sistema operativo completo di impostare questo valore. Può avere effetti drammatici soprattutto su molte transazioni di scrittura brevi.

     

innodb_thread_concurrency = 8 Anche con le attuali correzioni di scalabilità di Innodb che hanno una concorrenza limitata aiuta. Il numero effettivo può essere superiore o inferiore a seconda dell'applicazione e il valore predefinito 8 è l'inizio decente

     

innodb_flush_method = O_DIRECT Evita il doppio buffering e riduce la pressione di scambio, nella maggior parte dei casi questa impostazione migliora le prestazioni. Tuttavia, fai attenzione se non è stato eseguito il backup della batteria della cache RAID come quando la scrittura di IO potrebbe risentirne.

     

innodb_file_per_table & # 8211; Se non hai troppe tabelle usa questa opzione, quindi non avrai una crescita incontrollata del tablespace principale innodb che non puoi richiedere. Questa opzione è stata aggiunta in MySQL 4.1 e ora è abbastanza stabile da usare.

     

Controlla anche se la tua applicazione può essere eseguita in modalità di isolamento READ-COMMITED & # 8211; se lo fa & # 8211; impostarlo come predefinito come transazione-isolamento = READ-COMMITTED. Questa opzione offre alcuni vantaggi in termini di prestazioni, specialmente nel blocco in 5.0 e ancora di più con MySQL 5.1 e la replica a livello di riga.

Solo per la cronaca, le persone dietro mysqlperformanceblog.com hanno eseguito un benchmark comparando Falcon, MyISAM e InnoDB. Il benchmark doveva davvero evidenziare Falcon, tranne che era InnoDB a vincere la giornata, superando sia Falcon che MyISAM nelle query al secondo per quasi tutti i test: InnoDB vs MyISAM vs Falcon benchmarks & # 8211; parte 1 .

Suppongo che probabilmente non hai configurato le impostazioni di InnoDB oltre le impostazioni predefinite. Dovresti fare un rapido google per configurare le opzioni di InnoDB.

Quello che mi ha causato immediatamente i problemi di prestazioni più evidenti è stato innodb_buffer_pool_size . Questo dovrebbe essere impostato sul 50-80% della memoria della macchina. Di default sono spesso solo pochi MB. Avvialo via e dovresti vedere un notevole aumento delle prestazioni.

Dai un'occhiata anche a innodb_additional_mem_pool_size .

Inizia qui , ma anche google around per " innodb performance tuning " ;.

Il Query Optimizer di MySQL non è buono, dalla mia memoria. Prova una sottoselezione invece di un join diretto.

SELECT * FROM (SELECT * FROM `metaward_achiever` 
               WHERE `metaward_achiever`.`award_id` = 1507) a
INNER JOIN `metaward_alias` ON (a.`alias_id` = `metaward_alias`.`id`) 
ORDER BY a.`modified` DESC 
LIMIT 100

O qualcosa del genere (sintassi non testata sopra).

L'ordinamento è qualcosa fatto dal server di database, non dal motore di archiviazione, in MySQL.

Se in entrambi i casi, il motore non è stato in grado di fornire i risultati in forma già ordinata (dipende dall'indice utilizzato), il server deve ordinarli.

L'unico motivo per cui MyISAM / InnoDB potrebbe essere diverso è che l'ordine in cui le righe ritornano potrebbe influenzare il modo in cui i dati sono già ordinati - MyISAM potrebbe restituire i dati in " più ordinati " in alcuni casi (e viceversa).

Tuttavia, l'ordinamento di 60k righe non richiederà molto tempo poiché si tratta di un set di dati molto piccolo. Sei sicuro di avere il buffer di ordinamento impostato abbastanza grande?

L'uso di un filesort su disco () invece di uno in memoria è molto più lento. Tuttavia, il motore non dovrebbe fare alcuna differenza. filesort non è una funzione del motore, ma una funzione principale di MySQL. filesort, in effetti, fa schifo in molti modi ma normalmente non è così lento.

Prova ad aggiungere una chiave per i campi: metaward_achiever . alias_id , metaward_achiever . award_id e metaward_achiever . modificato questo aiuterà molto. E non usare le chiavi sui campi varchar aumenterà il tempo per inserimenti e aggiornamenti. Inoltre sembra che tu abbia 77 milioni di record nella tabella dei risultati, potresti voler preoccuparti delle ottimizzazioni innodb. Ci sono molti bravi tutor su come impostare i limiti di memoria per questo.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top