2015-11-07 25 views
8

Questo è un aggiornamento a questa domanda, in cui stavo facendo il casting in giro cercando di capire cosa diavolo stava succedendo:MySQL non sta selezionando in modo corretto le righe (a volte)

MySQL sometimes erroneously returns 0 for count(*)

ho finito per accettare un rispondi lì perché ha risposto alla domanda che ho posto ("perché potrebbe accadere") anche se non ha risposto alla domanda che volevo davvero sapere ("perché mi sta succedendo"). Ma Sono riuscito a restringere un po 'la questione, e penso di poter dire definitivamente che qualcosa non va in un modo che non capisco e che non ho mai visto prima.

Il problema è stato molto difficile da eseguire il debug perché, per ragioni che non comprendono la mia comprensione, l'accesso al database lo risolve automagicamente. Tuttavia, oggi sono riuscito ad attivare lo stato problematico mentre avevo una sessione MySQL aperta in un terminale. Ecco alcune domande e le risposte successive tratte da quella seduta:

In primo luogo, questo è il mio layout di tabella:

mysql> describe forum_posts;              
+-----------+------------+------+-----+---------+----------------+ 
| Field  | Type  | Null | Key | Default | Extra   | 
+-----------+------------+------+-----+---------+----------------+ 
| post_id | int(11) | NO | PRI | NULL | auto_increment | 
| thread_id | int(11) | YES | MUL | NULL |    | 
| forum_id | int(11) | YES | MUL | NULL |    | 
| user_id | int(11) | YES | MUL | NULL |    | 
| moderator | tinyint(1) | NO |  | 0  |    | 
| message | mediumtext | YES | MUL | NULL |    | 
| date  | int(11) | NO | MUL | NULL |    | 
| edited | int(11) | YES |  | NULL |    | 
| deleted | tinyint(1) | YES | MUL | 0  |    | 
| bbcode | tinyint(1) | NO |  | 1  |    | 
+-----------+------------+------+-----+---------+----------------+ 
10 rows in set (0.00 sec) 

Ora, consente di guardare quanti posti ci sono in un dato thread del forum:

mysql> SELECT count(post_id) as num FROM `forum_posts` where thread_id=5243; 
+-----+ 
| num | 
+-----+ 
| 195 | 
+-----+ 
1 row in set (0.00 sec) 

OK, ma voglio solo post del forum che non hanno il set deleted bandiera:

mysql> SELECT count(post_id) as num FROM `forum_posts` where thread_id=5243 and deleted=0; 
+-----+ 
| num | 
+-----+ 
| 0 | 
+-----+ 
1 row in set (0.06 sec) 

mysql> select post_id,deleted from forum_posts where thread_id=5243 and deleted=0; 
Empty set (0.06 sec) 

OK, lascia semplicemente doppio-make-sicuri che essi non sono in realtà tutti cancellati:

mysql> select post_id,deleted from forum_posts where thread_id=5243; 
+---------+---------+ 
| post_id | deleted | 
+---------+---------+ 
| 104081 |  0 | 
| 104082 |  0 | 

[snip] 

| 121162 |  0 | 
| 121594 |  0 | 
+---------+---------+ 
195 rows in set (0.00 sec) 

ogni riga nella tabella ha 'cancellato' impostato a 0, ma l'aggiunta di and deleted=0 alla query non produce risultati . Fino a quando non apro una nuova sessione accedendo nuovamente a MySQL da una finestra di terminale, dopo di che posso ancora una volta selezionare correttamente le righe dove "cancellato" è 0.

Cosa diavolo?


AGGIORNAMENTI:

@ miken32 nei commenti qui sotto mi suggerì di provare un EXPLAIN SELECT ..., quindi:

mysql> explain select post_id,deleted from forum_posts where thread_id='5243' and deleted=0; 
+----+-------------+-------------+-------------+-------------------+-------------------+---------+------+------+--------------------------------------------------------------+ 
| id | select_type | table  | type  | possible_keys  | key    | key_len | ref | rows | Extra              | 
+----+-------------+-------------+-------------+-------------------+-------------------+---------+------+------+--------------------------------------------------------------+ 
| 1 | SIMPLE  | forum_posts | index_merge | thread_id,deleted | thread_id,deleted | 5,2  | NULL | 97 | Using intersect(thread_id,deleted); Using where; Using index | 
+----+-------------+-------------+-------------+-------------------+-------------------+---------+------+------+--------------------------------------------------------------+ 
1 row in set (0.00 sec) 
+5

Il tuo server è stato recentemente posseduto da demoni, fantasmi o altre creature ultraterrene? – Sumurai8

+0

Non a mia conoscenza. E non sembra nemmeno essere specifico per la piattaforma. Ho riprodotto questo errore sia sul mio server di produzione su cui è in esecuzione Debian Wheezy, sia sul mio laptop locale con una copia del codice e del database (come copiato tramite mysqldump) su cui è in esecuzione Debian sid. – Mala

+1

Prova il tuo dump con http://sqlfiddle.com – lad2025

risposta

3

Sulla base del commento che usando FORZA KEY altera il risultato della query , è molto probabile che abbiamo a che fare con il bug di merge optimizer. SPIEGARE della query originale mostra che l'ottimizzazione viene eseguita selezionando dalla chiave eliminata, quindi dalla chiave post_id, quindi unendo i risultati. Quando forziamo per bypassare quel codice, il problema scompare.

I passi dal punto:

  • provarlo sugli stessi dati con la più recente versione 5.6 di MySQL
  • se il problema riproduce, cercare di isolare al più minimale banco di prova, visita http://bugs.mysql.com/ e segnala il bug
+0

Per quanto riguarda la mia applicazione, è una soluzione ragionevole solo per usare 'force key' in produzione? O è solo "mascherare" quello che potrebbe essere un problema potenzialmente molto peggiore che non ho ancora scoperto? – Mala

+0

Se si tratta effettivamente di un bug di merge optimizer, potrebbe potenzialmente influire su altre query che hanno colpito lo stesso percorso di codice. Quindi lavorerei per risolverlo solo per questo motivo. Inoltre è un servizio alla comunità MySQL, aiuta a migliorare MySQL. –

+0

Oh, non sto suggerendo di non segnalarlo, cercherò di capire esattamente come attivarlo (ho fatto qualche progresso anche su questo fronte). Mi chiedo solo se, nel prossimo futuro, dovrei rimuovere completamente i codepath che sembrano esacerbare il problema e forzare semplicemente le chiavi sulle query interessate – Mala

0

Esorcina demoni e fantasmi!Aggiungi questo indice per evitare bug di "unione":

INDEX(deleted, thread_id) and DROP the key on just deleted 

Un indice su una bandiera è quasi sempre inutile. Questa volta era peggio che inutile.

Questo sarà più economico, più veloce e più sicuro di FORCE INDEX.