İletişime geç

MySQL'de latin5'ten utf8mb4'e kayıpsız geçiş

On yıllık bir veritabanında Türkçe karakterleri bozmadan karakter setini değiştirmek kulağa tek satırlık bir iş gibi geliyor. Değil. Önce verinin gerçekte ne olduğunu anlamak gerekiyor.

MySQL'de latin5'ten utf8mb4'e kayıpsız geçiş Veritabanı

01Problem

Bu blogun eski sürümünde tablolar latin5 karakter setiyle oluşturulmuştu, ama PHP tarafındaki bağlantı yıllar içinde utf8mb4 olarak güncellenmişti. Sonuç tanıdık: bazı başlıklar düzgün, bazılarında ı gibi bozuk karakterler, emojiler ise hiç kaydedilmiyor.

İlk refleks şu komutu çalıştırmak olur:

sql
ALTER TABLE iq_page
  CONVERT TO CHARACTER SET utf8mb4
  COLLATE utf8mb4_tr_0900_ai_ci;

Veri gerçekten latin5 ise bu komut doğru çalışır. Ama yıllarca yanlış bağlantı ayarıyla yazılmış bir tabloda verinin bir kısmı zaten UTF-8 baytları olarak duruyor olabilir. O durumda bu komut, bozuk veriyi bir kez daha kodlar ve geri dönüşü zorlaşır.

02Teşhis: bayt bayt bakmak

Ekranda gördüğünüz karaktere değil, diskteki bayta bakın. Türkçe karakter içerdiğini bildiğiniz birkaç satırı HEX() ile inceleyin ve aşağıdaki tabloyla karşılaştırın:

Karakterlatin5UTF-8Çift kodlanınca
ıFDC4 B1ı
ğF0C4 9FÄ + kontrol karakteri
çE7C3 A7ç

latin5 bir sütunda C4B1 görüyorsanız, o satır latin5 değil; latin5 kılığına girmiş UTF-8'dir.

03İki senaryo, iki yöntem

Veri gerçekten latin5 ise yukarıdaki CONVERT TO komutu yeterlidir. MySQL her baytı doğru UTF-8 karşılığına çevirir.

Veri çift kodlanmışsa MySQL'e "bu baytlara dokunma, sadece etiketini değiştir" demek gerekir. Bunun yolu sütunu önce ikili (binary) türe, sonra hedef karakter setine çevirmektir:

sql
-- 1) Etiketi kaldır: baytlar olduğu gibi kalır
ALTER TABLE iq_page
  MODIFY baslik VARBINARY(1020),
  MODIFY icerik MEDIUMBLOB;

-- 2) Doğru etiketi ver: baytlar artık UTF-8 olarak okunur
ALTER TABLE iq_page
  MODIFY baslik VARCHAR(255) CHARACTER SET utf8mb4 NOT NULL,
  MODIFY icerik MEDIUMTEXT CHARACTER SET utf8mb4 NOT NULL;
dikkatAynı tabloda iki tür veri karışıksa tek bir ALTER yetmez. Satırları önce HEX() ile sınıflandırıp yeni bir tabloya ayrı ayrı taşımak daha güvenlidir.

04Collation tuzakları

MySQL 8 için Türkçe sıralama kurallarını en doğru uygulayan collation utf8mb4_tr_0900_ai_ci'dir. ı/I ve i/İ çiftlerini doğru eşler, sıralamayı alfabeye göre yapar.

Ancak URL ve slug gibi benzersiz sütunlarda büyük-küçük harf duyarsız bir collation sürpriz çakışmalara yol açabilir. Bu sütunlar için ascii_bin ya da utf8mb4_bin kullanmak, eşitliğin her zaman bayt bayt kontrol edilmesini sağlar.

05Kontrol listesi

  1. Tam yedek alın ve geri yüklemeyi bir kez deneyin.
  2. Her metin sütunu için HEX() örneklemesi yapın; latin5 ve çift kodlanmış satırları sayın.
  3. Senaryoya göre CONVERT TO ya da binary üzerinden dönüşümü seçin.
  4. Tablo varsayılanını da güncelleyin: DEFAULT CHARACTER SET utf8mb4.
  5. Bağlantıda utf8mb4 kullanıldığını uygulama tarafında doğrulayın.
  6. Rastgele 50 satırı gözle kontrol edin. Bir emoji ekleyip kaydedin.