İçeriğe geç
Can Uğurlu
Geri dön

MySQL'de utf8 değil utf8mb4 kullanın

MySQL’in utf8 karakter kümesi UTF-8 değil. Gerçek adı utf8mb3, karakter başına en fazla üç bayt kullanıyor ve BMP dışındaki hiçbir karakteri saklayamıyor. Emoji bunun en sık çarpılan yeri, çünkü emoji dört bayt istiyor.

Semptom iki türlü: ya insert Incorrect string value hatasıyla patlıyor, ya da hiç hata vermeden alanı emoji’nin geldiği noktada sessizce kesiyor.

Hata mesajı

SQLSTATE[HY000]: General error: 1366 Incorrect string value: '\xF0\x9F\x98\x80' for column 'comment' at row 1

\xF0\x9F\x98\x80 bir emoji’nin UTF-8 bayt dizisi, dört bayt uzunluğunda. utf8mb3 üç bayta kadar destekliyor, dördüncü baytı hiç göremiyor.

İkinci semptom: sessiz kesilme

Hata her zaman patlamıyor. sql_mode içinde STRICT_TRANS_TABLES kapalıysa MySQL hata vermek yerine bir uyarı yazıyor ve alanı geçerli baytların bittiği noktada kesiyor. Sorgu başarıyla döner, satır kaydedilir, ama emoji’den sonraki her karakter kayıptır.

SHOW WARNINGS;
-- Warning | 1366 | Incorrect string value: '\xF0\x9F\x98\x80' for column 'comment' at row 1

Bu ikinci semptom daha tehlikeli çünkü kimseye bir şey söylemiyor. Uygulama katmanı 200 döner, kullanıcı kaydın gittiğini düşünür, veritabanında yarım bir metin durur.

Dördü birden utf8mb4e çevirin

utf8mb4e geçmek tek satırla bitmiyor. Dört katman var, biri eksik kalırsa hata duruyor: veritabanı, tablo, sütun, bağlantı.

ALTER DATABASE mydb CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
ALTER TABLE comments CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
ALTER TABLE comments MODIFY body TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

CONVERT TO CHARACTER SET tablodaki mevcut sütunları çeviriyor, ama sonradan eklenen ya da elle farklı ayarlanmış bir sütun varsa onun üzerinden atlanıyor. Her sütunu tek tek kontrol edin.

Bağlantı katmanı en çok unutulan yer. Sunucu ve tablo utf8mb4 olsa bile PHP bağlantısı utf8 ile açılırsa MySQL client bu bağlantı üzerinden gelen veriyi yine üç baytlık kümeye göre yorumluyor.

Dördünü de tek tek kontrol edin

Tahmin etmeyin, sorgulayın:

SHOW VARIABLES LIKE 'character_set_%';
SHOW VARIABLES LIKE 'collation_%';
SHOW CREATE TABLE comments;

İlk iki komut sunucu ve bağlantı seviyesindeki charset’i gösteriyor, character_set_client, character_set_connection ve character_set_database satırlarının hepsi utf8mb4 olmalı. SHOW CREATE TABLE ise tablonun ve her sütunun gerçekte hangi charset’te olduğunu gösteriyor; tablo utf8mb4 görünüp bir sütun hâlâ utf8 olması yaygın bir durum, özellikle eski migration’larla büyümüş tablolarda.

Laravel bağlantısı

// config/database.php
'mysql' => [
    'charset' => 'utf8mb4',
    'collation' => 'utf8mb4_unicode_ci',
],

Bu iki satır olmadan Eloquent bağlantısı sunucunun varsayılan charset’iyle açılır. Sunucu varsayılanı zaten utf8mb4 değilse (eski kurulan sunucularda genelde değildir) uygulama tarafında hâlâ kesiliyor.

utf8mb4_unicode_ci mi utf8mb4_0900_ai_ci mi

utf8mb4_general_ci dil kurallarına en kayıtsız seçenek, artık önerilmiyor. İki gerçekçi aday var:

Türkçe verinin ı ve İ sıralaması ikisinde de doğru çıkmıyor. ı/i ayrımını sıralamada doğru istiyorsanız utf8mb4_turkish_ci kullanın. Aksi hâlde WHERE ve ORDER BY sonuçlarında i ile ı aynı harf gibi davranıyor.

İndeks uzunluğu tarihi bir engeldi

utf8mb4ten kaçınmanın sebebi yıllar önce indeks anahtar uzunluğuydu. Eski InnoDB row format’larında indeks anahtarı 767 bayt ile sınırlıydı. utf8mb3 karakter başına üç bayt, utf8mb4 dört bayt kullandığı için aynı VARCHAR(255) sütunu utf8mb4te indekslenemeyecek kadar uzun bayt dizisine çıkabiliyordu.

Güncel innodb_large_prefix ve DYNAMIC/COMPRESSED row format bu sınırı kaldırdı. Bu hatayı hâlâ alıyorsanız sebep neredeyse her zaman eski bir row format ayarı, utf8mb4in kendisi değil.

Özet

Yeni projede baştan utf8mb4 ve utf8mb4_unicode_ci (Türkçe sıralama gerekiyorsa utf8mb4_turkish_ci) kullanın. Mevcut projede veritabanı, tablo, sütun ve bağlantının dördünü birden çevirin, biri eksik kalırsa emoji hâlâ patlar.


Bu yazıyı paylaş:

Önceki Yazı
Shopify webhook'ta HMAC doğrulaması nasıl yapılır
Sonraki Yazı
GitHub Actions'ta cache ile build süresi