Verständnisfrage zu Zeichensätzen bei Mysql

Verständnisfrage zu Zeichensätzen bei Mysql

am 03.08.2007 12:00:41 von Daniel Maus

Moin zusammen,

ich bin gerade etwas verwirrt ob der Zeichensatzmöglichkeiten bei Mysql.
Aber vielleicht erst mal die Zustandsbeschreibung:

Gegeben sind:
A: ein Produktivsystem mit Mysql V4.0 auf einem älteren Mandrake, daß
mit UTF-8 Daten eines Apachen gefüttert wird und das die UTF-8 Daten
auch wieder für UTF-8 Webseiten rausrückt. ;-)

B: Ein Etch (stable) im Aufbau mit passenden deb Mysql V5.0 (müßte das
sein).

Ziel ist die Ablösung von A durch B.

"Was bisher geschah":
Das Mysqlverzeichnis von A wurde nach B ge"rsync"t. Nach einigen
kleineren Problemen beschwert sich jetzt auch das Checkscript von Debian
nicht mehr über die Tabellen (Merke, was 4.0 hinnimmt, muß 5.0 nicht
auch hinnehmen).
Replikation von A nach B wurde gestartet. Nach einigen kleineren
Problemen läuft das jetzt auch.
Da mein Workflow bislang ja UTF-8 basiert ist, stellt sich mir die
Frage, B Mysql "komplett" auf UTF-8 umzustellen. Ich weiß, daß viele der
Meinung sind, daß man das nicht machen sollte, wenns nicht unbedingt
nötigt ist, aber viele Tabellen können prinzipiell mal mit Zeilen
gefüllt werden, die aus den unterschiedlichsten Sprachräumen kommen.

Nun gut. Ich stellte fest, daß die Binaryversion wohl immer erst mal mit
latin1 als Serverzeichensatz startet (hier mal die relevanten Variablen).
| character_set_client | latin1 |
| character_set_connection | latin1 |
| character_set_database | latin1 |
| character_set_filesystem | binary |
| character_set_results | latin1 |
| character_set_server | latin1 |
| character_set_system | utf8 |
| character_sets_dir | /usr/share/mysql/charsets/ |
| collation_connection | latin1_swedish_ci |
| collation_database | latin1_swedish_ci |
| collation_server | latin1_swedish_ci |


Die Webanwendung läuft auch auf B einwandfrei. Umlaute werden korrekt
wiedergegeben. Auch wenn ich direkt mit dem Mysqlclient die Tabelle
abfrage, paßt das.

Aber warum? Stelle ich den Client auf UTF-8 um, dann werden die Umlaute
versägt, respektive ich bekomme die 2-ByteKodierung als 2 Zeichen
angezeigt. Aber wenn ich das richtig gelesen habe, bedeutet doch
"Charakter-set-results" bei mir, daß das Ergebnis nach latin1
umgewandelt wird, bevor es an den Client geschickt wird.


Und wenn die Daten sowieso schon als UTF-8 in der DB liegen, wieso
bekomme ich sie als latin1 korrekt und als UTF-8 falsch bzw. "roh"
angezeigt. Irgendwie fehlt mir hier noch was zum Verständnis (Wink mit
dem Zaunpfahl). ;-)

Aufgefallen ist mir das ganze übrigens, als ich mit Phpmyadmin (Oh Gott,
ich habe das böse Wort gesagt) auf B auf Mysql zugegriffen habe und mir
PMA sagt, der Serverzeichensatz wäre utf-8, er mir aber die Inhalte auch
nur "roh" anzeigt. Füge ich via PMA eine Zeile mit Umlauten ein, dann
dreht sich das im mysqlclient übrigens um.

So, viel geschrieben. Vielleicht kann mir einer auf die Sprünge helfen,
die Doku konnte es bislang nicht.

Hauptfrage wäre für mich, was für einen Sinn es machen würde MYSQL
durchweg auf UTF-8 umzustellen. Soweit ich das verstanden habe, hätte
ich nur den Vorteil, die Kollation entsprechend setzen zu können.
Und ich müßte dann die Daten wohl komplett dumpen, Server umstellen und
wieder importieren, oder?
Wobei sich dann die Frage stellt, ob die Replikation sauber weiterlaufen
würde und ich nicht nachher einen Mischmasch bei den Zeichenkodierungen
hätte.

Danke
Daniel

Re: Verständnisfrage zu Zeichensätzen bei Mysql

am 03.08.2007 13:04:04 von Axel Schwenke

Daniel Maus wrote:
>
> ich bin gerade etwas verwirrt ob der Zeichensatzmöglichkeiten bei Mysql.
> Aber vielleicht erst mal die Zustandsbeschreibung:
>
> Gegeben sind:
> A: ein Produktivsystem mit Mysql V4.0 auf einem älteren Mandrake, daß
> mit UTF-8 Daten eines Apachen gefüttert wird und das die UTF-8 Daten
> auch wieder für UTF-8 Webseiten rausrückt. ;-)

Tja, schon kaputt. MySQL 4.0 kann kein utf8. Vermutlich enthalten
die Tabellen also utf8 Daten obwohl sie als latin1 deklariert sind.
MySQL4.0 stört das nicht, weil es Daten prinzipiell *immer* so
rausrückt, wie es sie bekommen hat. Auch String-Literale, bspw. für
.... WHERE foo='bär' ...
werden 1:1 mit dem Tabelleninhalt verglichen.

> B: Ein Etch (stable) im Aufbau mit passenden deb Mysql V5.0 (müßte das
> sein).
>
> Ziel ist die Ablösung von A durch B.

Seit 4.1 beachtet MySQL für jedes String-Objekt die "Encoding"
Eigenschaft und kennt i.d.R. auch noch verschiedene Collations pro
Encoding. Wenn also deine Daten in der Tabelle als latin1 deklariert
sind, dein Client aber sagt, "vergleiche mal mit _utf8'bär'" dann
konvertiert MySQL das ganze Gedöns, bis es vergleichbar ist und
findet den Bären in den Daten nicht (weil der als "b\xC3\xA4r"
in der Tabelle steht und nicht als "b\xE4r").

> Die Webanwendung läuft auch auf B einwandfrei. Umlaute werden korrekt
> wiedergegeben. Auch wenn ich direkt mit dem Mysqlclient die Tabelle
> abfrage, paßt das.
>
> Aber warum?

Weil sowohl die Daten als latin1 deklariert sind als auch der Client
behauptet, latin1 zu sprechen. Unter der Haube versteht der Client
aber offensichtlich doch utf8 und zeigt es dann richtig an.
Du kannst ja spaßeshalber mal ein non-utf8 xterm starten und darin
den Kommandozeilenclient. Dann wirst du sehen.

> Stelle ich den Client auf UTF-8 um, dann werden die Umlaute
> versägt, respektive ich bekomme die 2-ByteKodierung als 2 Zeichen
> angezeigt. Aber wenn ich das richtig gelesen habe, bedeutet doch
> "Charakter-set-results" bei mir, daß das Ergebnis nach latin1
> umgewandelt wird, bevor es an den Client geschickt wird.

Jep. Und da es laut Deklaration ja bereits latin1 ist, wird gar nix
konvertiert und es geht doch. Zweimal falsch gibt wieder richtig.
Oder anders gesagt: 5.0 verhält sich hier genauso transparent wie 4.0
weil Transport-Encoding und Storage-Encoding gleich sind.

> So, viel geschrieben. Vielleicht kann mir einer auf die Sprünge helfen,
> die Doku konnte es bislang nicht.

Die Lösung ist einfach: bring die Deklaration deiner Daten in
Übereinstimmung mit dem Inhalt. Da anscheinend eine Menge Leute utf8
in MySQL4.0 gespeichert haben, erklärt das auch das Handbuch:
http://dev.mysql.com/doc/refman/4.1/en/charset-upgrading.htm l

> Hauptfrage wäre für mich, was für einen Sinn es machen würde MYSQL
> durchweg auf UTF-8 umzustellen.

Keinen. Insbesondere weil da das Wort "durchweg" steht. Wenn du sagst
"eine bestimmte Tabelle" oder "alle Tabellen in einer bestimmten
Datenbank" - dann sieht das schon anders aus.

Das hat übrigens keinen technischen Hintergrund, sondern einen mensch-
lichen. Wenn alle DBAs perfekt wären, würden sie vor jedem CREATE TABLE
genau nachdenken, welches Encoding (und welche Collation) sie denn für
welche Spalte brauchen und würden das auch explizit hinschreiben. Dann
wäre es egal, was als Default eingestellt ist.

> Wobei sich dann die Frage stellt, ob die Replikation sauber weiterlaufen
> würde und ich nicht nachher einen Mischmasch bei den Zeichenkodierungen
> hätte.

Da wird es haken. Weil dein 4.0 Master ja behauptet, er würde latin1
anliefern (aber in Wirklichkeit utf8). Der Slave wird also das
vermeintliche latin1 in utf8 umwandeln und damit kaputt machen.


XL

Re: Verständnisfrage zu Zeichensätzen bei Mysql

am 03.08.2007 15:24:52 von Daniel Maus

> Tja, schon kaputt. MySQL 4.0 kann kein utf8. Vermutlich enthalten
> die Tabellen also utf8 Daten obwohl sie als latin1 deklariert sind.
> MySQL4.0 stört das nicht, weil es Daten prinzipiell *immer* so
> rausrückt, wie es sie bekommen hat. Auch String-Literale, bspw. für
> ... WHERE foo='bär' ...
> werden 1:1 mit dem Tabelleninhalt verglichen.
Kopfkratz... wieso kann ich dann im mysqlclient auf _A_ nach Umlauten
suchen (... like '%ü%') und es werden Zeilen zurückgegeben, die dann
aber "roh" zurückkommen?

> Die Lösung ist einfach: bring die Deklaration deiner Daten in
> Übereinstimmung mit dem Inhalt. Da anscheinend eine Menge Leute utf8
> in MySQL4.0 gespeichert haben, erklärt das auch das Handbuch:
> http://dev.mysql.com/doc/refman/4.1/en/charset-upgrading.htm l
OK, wühle ich mich mal durch


>> Hauptfrage wäre für mich, was für einen Sinn es machen würde MYSQL
>> durchweg auf UTF-8 umzustellen.
>
> Keinen. Insbesondere weil da das Wort "durchweg" steht. Wenn du sagst
> "eine bestimmte Tabelle" oder "alle Tabellen in einer bestimmten
> Datenbank" - dann sieht das schon anders aus.
Naja, sagen wir mal so. Ca. 80% der Spalten werden durch Eingaben
erzeugt und müssen also potentiell "UTF-8 ready" sein. Bleiben noch ein
paar Spalten als keys und flags über. Bei letzteren beiden macht es per
se natürlich keinen Sinn, aber wenn man mal vom erhöhten Speicherbedarf
absieht, sehe ich da auch kein Problem oder übersehe ich da was? Anders
formuliert. Wenn ich alles in einem Encoding habe, _glaube ich_ eine
Fehlerquelle weniger zu haben, als wenn ich mit mehreren Encodings
gleichzeitig zu gange bin. Aber ich lasse mich auch da gerne eines
Besseren belehren. ;-)


> Da wird es haken. Weil dein 4.0 Master ja behauptet, er würde latin1
> anliefern (aber in Wirklichkeit utf8). Der Slave wird also das
> vermeintliche latin1 in utf8 umwandeln und damit kaputt machen.
Sowas ähnlich habe ich schon vermutet.

Eine Ungewissheit bleibt aber noch. Meine Daten liegen ja als UTF-8 in
der DB, völlig unabhängig davon, welches Encoding Mysql jetzt vermutet
(oder weiß). Sehe ich das richtig, das letztlich die Konversion bein
Transfer zum Client, die Mysql aufgrund des (vermuteten) Encodings auf
Server- und Clientseite macht, der Grund dafür ist, daß die Encodings
korrekt angegeben werden müssen?

Danke
Daniel

Re: Verständnisfrage zu Zeichensätzen bei Mysql

am 04.08.2007 12:35:19 von Axel Schwenke

Daniel Maus wrote:
>> Tja, schon kaputt. MySQL 4.0 kann kein utf8. Vermutlich enthalten
>> die Tabellen also utf8 Daten obwohl sie als latin1 deklariert sind.
>> MySQL4.0 stört das nicht, weil es Daten prinzipiell *immer* so
>> rausrückt, wie es sie bekommen hat. Auch String-Literale, bspw. für
>> ... WHERE foo='bär' ...
>> werden 1:1 mit dem Tabelleninhalt verglichen.

> Kopfkratz... wieso kann ich dann im mysqlclient auf _A_ nach Umlauten
> suchen (... like '%ü%') und es werden Zeilen zurückgegeben, die dann
> aber "roh" zurückkommen?

Kannst du das genauer erklären?
Du suchst nach latin1 "ü" und es werden Zeilen mit utf8 "ü" gefunden?

Kann eigentlich nur passieren, wenn utf8 Sequenzen ein latin1 "ü" =
0xFC enthalten. Das ist aber frühestens mit 5-Byte Unicode erlaubt
(das erste Zeichen einer Fünfersequenz könnte 1111 1000 sein).
Aber OK, da das ohnehin handgedrechseltes utf8 ist, kann es womög-
lich auch illegale Sequenzen enthalten.

>>> Hauptfrage wäre für mich, was für einen Sinn es machen würde MYSQL
>>> durchweg auf UTF-8 umzustellen.
>>
>> Keinen. Insbesondere weil da das Wort "durchweg" steht. Wenn du sagst
>> "eine bestimmte Tabelle" oder "alle Tabellen in einer bestimmten
>> Datenbank" - dann sieht das schon anders aus.

> Naja, sagen wir mal so. Ca. 80% der Spalten werden durch Eingaben
> erzeugt und müssen also potentiell "UTF-8 ready" sein.

Ja. "potentiell"
Nur: wie oft kommt das vor, daß ein User da tatsächlich ein Zeichen
eingint, das außerhalb latin1 liegt? Und ist das auch erwünscht?

> Wenn ich alles in einem Encoding habe, _glaube ich_ eine
> Fehlerquelle weniger zu haben, als wenn ich mit mehreren Encodings
> gleichzeitig zu gange bin. Aber ich lasse mich auch da gerne eines
> Besseren belehren. ;-)

Für deine Anwendung ist das Storage-Encoding doch vollkommen
transparent. Die sagt MySQL einmal "ich spreche utf8" und dann
bekommt sie auch alles als utf8. Und falls irgendwas nicht in utf8
abgespeichert ist, konvertiert MySQL das automatisch.

> Eine Ungewissheit bleibt aber noch. Meine Daten liegen ja als UTF-8 in
> der DB, völlig unabhängig davon, welches Encoding Mysql jetzt vermutet
> (oder weiß).

Das Encoding (genauer gesagt: die gewünschte Collation und damit
implizit auch das Encoding) ist ein extra Attribut jeder string-
artigen Spalte, das MySQL ab 4.1 pflegt. Als du die Daten aus dem
4.0 nach 5.0 übernommen hast, wurden alle Spalten mit dem aktuellen
Wert von character_set_server initialisiert.

> Sehe ich das richtig, das letztlich die Konversion bein
> Transfer zum Client, die Mysql aufgrund des (vermuteten) Encodings auf
> Server- und Clientseite macht, der Grund dafür ist, daß die Encodings
> korrekt angegeben werden müssen?

Ab 4.1 unterscheidet MySQL zwischen Storage- und Transfer-Encoding.
Aufgrund dieser beiden Angaben wird konvertiert. Keiner dieser Werte
wird "geraten". Wenn eine Angabe fe lt, wird der jeweilige Default
genommen. Das ist normalerweise latin1.


XL