Khi máy khách MySQL tương tác với máy chủ:
-
máy chủ nhận bất kỳ văn bản nào chỉ đơn thuần là một chuỗi byte; trước đó khách hàng sẽ cho nó biết cách mã hóa văn bản như vậy.
-
nếu sau đó máy chủ phải lưu trữ văn bản đó trong một bảng, thì nó phải chuyển mã nó sang mã hóa của cột có liên quan (nếu khác).
-
nếu sau đó máy khách muốn truy xuất văn bản như vậy, máy chủ phải chuyển mã nó sang mã hóa mà máy khách mong đợi.
Nếu các mã hóa được khách hàng sử dụng ở bước 1 và 3 là giống nhau (điều này thường xảy ra, đặc biệt là khi ứng dụng khách trong cả hai trường hợp là cùng một ứng dụng), sau đó thường không được chú ý nếu khách hàng đang sử dụng một mã hóa khác với mã hóa mà nó đã nói. Ví dụ:giả sử máy khách nói với MySQL rằng nó sẽ sử dụng latin1
, nhưng thực sự gửi dữ liệu trong utf8
:
-
Chuỗi
'Jazz–Man'
được gửi đến máy chủ trong UTF-8 dưới dạng0x4a617a7ae280934d616e
. -
MySQL, giải mã các byte đó trong Windows-1252, hiểu chúng để đại diện cho chuỗi
'Jazz–Man'
. -
Để lưu trữ trong
utf8
cột, MySQL chuyển mã chuỗi sang mã hóa UTF-8 của nó0x4a617a7ac3a2e282ace2809c4d616e
. Điều này có thể được xác minh bằng cách sử dụngSELECT HEX(name) FROM lessons WHERE id=79510
. -
Khi máy khách truy xuất giá trị, MySQL nghĩ rằng nó muốn nó trong
latin1
và do đó chuyển mã sang mã hóa Windows-12520x4a617a7ae280934d616e
. -
Khi máy khách nhận được các byte đó, nó sẽ giải mã chúng thành UTF-8 và do đó hiểu chuỗi là
'Jazz–Man'
.
Kết luận :khách hàng không nhận ra bất cứ điều gì là sai. Sự cố chỉ được phát hiện khi một ứng dụng khách khác (ứng dụng không ghi sai kết nối UTF-8 của nó là latin1
) cố gắng sử dụng bảng. Trong trường hợp của bạn, điều này xảy ra khi mysqldump lấy được dữ liệu xuất; sử dụng --default-character-set=latin1 --skip-set-charset
các tùy chọn đã buộc mysqldump hoạt động theo cách bị hỏng giống như ứng dụng của bạn một cách hiệu quả, do đó, nó kết thúc với dữ liệu được mã hóa chính xác.
Để khắc phục sự cố của bạn trong tương lai, bạn phải:
-
Định cấu hình ứng dụng của bạn để ứng dụng đặt chính xác bộ ký tự kết nối MySQL (ví dụ:đặt
encoding: utf8
trongconfig/database.yml
cho Rails); -
Mã hóa dữ liệu trong cơ sở dữ liệu của bạn, ví dụ:
UPDATE lessons SET name = BINARY CONVERT(name USING latin1)
(lưu ý rằng điều này phải được thực hiện cho mọi cột văn bản được mã hóa sai).
Cũng lưu ý rằng bạn có thể sẽ muốn thực hiện hai hành động này một cách nguyên tử, điều này có thể cần một chút suy nghĩ.