Mysql
 sql >> Database >  >> RDS >> Mysql

i dati utf8 sembrano a posto in mysql ma sono rotti nei binari

Quando un client MySQL interagisce con il server:

  1. il server riceve qualsiasi testo semplicemente come una stringa di byte; il cliente gli avrà precedentemente detto come sarebbe stato codificato tale testo.

  2. se poi il server deve memorizzare quel testo in una tabella, deve transcodificarlo nella codifica della colonna pertinente (se diversa).

  3. se il client desidera successivamente recuperare tale testo, il server deve transcodificarlo nella codifica prevista dal client.

Se le codifiche utilizzate dal client nei passaggi 1 e 3 sono le identiche (cosa che di solito è il caso, specialmente quando il client in entrambi i casi è la stessa applicazione), quindi spesso passa inosservato se il client utilizza una codifica diversa da quella che avrebbe detto. Ad esempio, supponiamo che il client dica a MySQL che utilizzerà latin1 , ma in realtà invia i dati in utf8 :

  • La stringa 'Jazz–Man' viene inviato al server in UTF-8 come 0x4a617a7ae280934d616e .

  • MySQL, decodificando quei byte in Windows-1252, li interpreta per rappresentare la stringa 'Jazz–Man' .

  • Per memorizzare in un utf8 colonna, MySQL transcodifica la stringa nella sua codifica UTF-8 0x4a617a7ac3a2e282ace2809c4d616e . Questo può essere verificato utilizzando SELECT HEX(name) FROM lessons WHERE id=79510 .

  • Quando il client recupera il valore, MySQL pensa di volerlo in latin1 e quindi transcodifica nella codifica Windows-1252 0x4a617a7ae280934d616e .

  • Quando il client riceve quei byte, li decodifica come UTF-8 e quindi interpreta la stringa come 'Jazz–Man' .

Conclusione :il cliente non si accorge che c'è qualcosa che non va. I problemi vengono rilevati solo quando un client diverso (uno che non indica erroneamente la sua connessione UTF-8 come latin1 ) tenta di utilizzare la tabella. Nel tuo caso, ciò si è verificato quando mysqldump ha ottenuto un'esportazione dei dati; usando il --default-character-set=latin1 --skip-set-charset le opzioni hanno effettivamente forzato mysqldump a comportarsi nello stesso modo rotto della tua applicazione, quindi ha finito con i dati codificati correttamente.

Per risolvere il tuo problema in futuro, devi:

  1. Configura la tua applicazione in modo che imposti correttamente il suo set di caratteri di connessione MySQL (ad es. set encoding: utf8 in config/database.yml per binari);

  2. Ricodifica i dati nel tuo database, ad es. UPDATE lessons SET name = BINARY CONVERT(name USING latin1) (nota che questo deve essere fatto per ogni colonna di testo codificata in modo errato).

Nota anche che probabilmente vorrai eseguire queste due azioni in modo atomico, il che potrebbe richiedere qualche riflessione.