MySQL ve MariaDB’de Karakter Seti ve Collation Yönetimi: utf8mb4 Geçişi ve Encoding Sorunlarını Çözme
Veritabanı yönetiminde en çok baş ağrıtan konulardan biri karakter seti sorunlarıdır. Türkçe karakterlerin bozuk görünmesi, emoji’lerin kaydedilememesi ya da farklı uygulamalar arasında veri taşırken yaşanan encoding karmaşası… Bunları yaşadıysanız doğru yerdesiniz. Bu yazıda MySQL ve MariaDB’de karakter seti yönetimini, özellikle utf8mb4 geçişini ve encoding sorunlarını pratik bir bakış açısıyla ele alacağız.
Karakter Seti ve Collation Nedir?
Önce temel kavramları netleştirelim. Karakter seti (character set), veritabanında hangi karakterlerin saklanabileceğini ve bunların nasıl kodlanacağını belirler. Collation ise bu karakterlerin sıralanma ve karşılaştırma kurallarını tanımlar.
Örneğin utf8_general_ci collation’ında “ci” büyük/küçük harf duyarsız (case-insensitive) anlamına gelir. “türkçe” ile “TÜRKÇE” bu collation ile arama yapıldığında eşit kabul edilir. Ama Türkçe için önemli olan nokta, “i” ve “İ” karakterlerinin doğru karşılaştırılmasıdır. Yanlış collation seçimi Türkçe büyük/küçük harf dönüşümlerinde ciddi sorunlar yaratır.
MySQL’in “utf8” Tuzağı
Burada pek çok sysadmin’in düştüğü klasik bir tuzak var. MySQL’de utf8 olarak geçen karakter seti aslında gerçek UTF-8 değildir. MySQL’in utf8 implementasyonu karakter başına maksimum 3 byte kullanır. Oysa gerçek UTF-8 standardı karakter başına 4 byte’a kadar çıkabilir.
Bu ne anlama geliyor? 4 byte gerektiren karakterler, yani emoji’ler (😀, ❤️) ve bazı özel Çince-Japonca karakterler MySQL’in utf8 kolonlarına kaydedilemez. Uygulama bu karakterleri kaydetmeye çalıştığında ya sessizce kesilir ya da hata alırsınız.
utf8mb4 tam olarak bu problemi çözmek için geliştirilmiştir. “mb4” kısmı “multi-byte 4” anlamına gelir ve gerçek 4 byte UTF-8 desteği sunar. MySQL 5.5.3 ve üzeri, MariaDB 5.5 ve üzeri sürümlerde mevcuttur.
Mevcut Durumu Kontrol Etmek
Geçişe başlamadan önce sisteminizin mevcut durumunu tam olarak anlamalısınız. Aşağıdaki sorgular size kapsamlı bir tablo sunacak:
mysql -u root -p -e "SHOW VARIABLES LIKE 'character%'; SHOW VARIABLES LIKE 'collation%';"
Bu komutun çıktısında şunlara bakın:
- character_set_client: İstemcinin MySQL’e gönderdiği verinin kodlaması
- character_set_connection: Bağlantı katmanında kullanılan kodlama
- character_set_database: Varsayılan veritabanının karakter seti
- character_set_results: MySQL’in istemciye döndürdüğü sonuçların kodlaması
- character_set_server: Sunucu düzeyinde varsayılan karakter seti
- collation_connection: Bağlantı collation’ı
- collation_database: Veritabanı collation’ı
- collation_server: Sunucu düzeyi collation
Belirli bir veritabanındaki tablo ve kolon bilgilerini görmek için:
mysql -u root -p your_database -e "
SELECT
TABLE_NAME,
TABLE_COLLATION
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = 'your_database';
SELECT
TABLE_NAME,
COLUMN_NAME,
CHARACTER_SET_NAME,
COLLATION_NAME,
COLUMN_TYPE
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = 'your_database'
AND CHARACTER_SET_NAME IS NOT NULL
ORDER BY TABLE_NAME, COLUMN_NAME;
"
MySQL/MariaDB Yapılandırma Dosyasını Güncellemek
Sunucu düzeyinde utf8mb4 varsayılan yapmak için /etc/mysql/mysql.conf.d/mysqld.cnf veya /etc/my.cnf dosyanızı düzenlemeniz gerekir. Dağıtıma göre konum değişebilir:
# Yapılandırma dosyasını bulmak için:
mysql --help | grep "Default options" -A 1
# Ya da:
find /etc -name "my.cnf" -o -name "mysqld.cnf" 2>/dev/null
Yapılandırma dosyasına eklenecek ayarlar:
# /etc/mysql/mysql.conf.d/mysqld.cnf veya /etc/my.cnf
[mysqld]
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci
character-set-filesystem = binary
skip-character-set-client-handshake
[client]
default-character-set = utf8mb4
[mysql]
default-character-set = utf8mb4
[mysqldump]
default-character-set = utf8mb4
skip-character-set-client-handshake direktifi önemlidir; istemcinin farklı bir karakter seti talep etmesini engelleyerek sunucu ayarını zorlar.
Yapılandırmayı uygulamak için servisi yeniden başlatın:
# systemd kullanan sistemler için:
sudo systemctl restart mysql
# veya MariaDB için:
sudo systemctl restart mariadb
# Değişikliği doğrulayın:
mysql -u root -p -e "SHOW VARIABLES LIKE 'character_set_server'; SHOW VARIABLES LIKE 'collation_server';"
Mevcut Veritabanı ve Tabloları Dönüştürmek
Sunucu yapılandırmasını değiştirmek yeni oluşturulacak veritabanlarını etkiler; mevcut olanları değiştirmez. Bunun için her veritabanı ve tablo üzerinde dönüşüm yapmanız gerekir.
Önce yedek alın! Bu adımı atlamayın:
# Tam yedek
mysqldump -u root -p
--default-character-set=utf8mb4
--hex-blob
--single-transaction
--routines
--triggers
--all-databases > /backup/full_backup_$(date +%Y%m%d_%H%M%S).sql
# Sadece belirli bir veritabanı
mysqldump -u root -p
--default-character-set=utf8mb4
--hex-blob
--single-transaction
your_database > /backup/your_database_$(date +%Y%m%d_%H%M%S).sql
Şimdi dönüşüm için bir script hazırlayalım. Bu script hem veritabanını hem de tüm tablolarını dönüştürür:
#!/bin/bash
# utf8mb4_migration.sh
# Kullanim: ./utf8mb4_migration.sh veritabani_adi
DB_NAME="$1"
DB_USER="root"
DB_PASS="sifreniz"
if [ -z "$DB_NAME" ]; then
echo "Kullanim: $0 <veritabani_adi>"
exit 1
fi
echo "=== $DB_NAME icin utf8mb4 gecisi basliyor ==="
# Veritabanini donustur
mysql -u "$DB_USER" -p"$DB_PASS" -e
"ALTER DATABASE `$DB_NAME` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"
echo "Veritabani karakter seti guncellendi."
# Tum tablolari donustur
TABLES=$(mysql -u "$DB_USER" -p"$DB_PASS" -N -e
"SELECT TABLE_NAME FROM information_schema.TABLES WHERE TABLE_SCHEMA='$DB_NAME';")
for TABLE in $TABLES; do
echo "Tablo donusturuluyor: $TABLE"
mysql -u "$DB_USER" -p"$DB_PASS" "$DB_NAME" -e
"ALTER TABLE `$TABLE` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"
if [ $? -eq 0 ]; then
echo " OK: $TABLE"
else
echo " HATA: $TABLE donusturulemedi!"
fi
done
echo "=== Gecis tamamlandi ==="
# Sonucu dogrula
mysql -u "$DB_USER" -p"$DB_PASS" -e
"SELECT TABLE_NAME, TABLE_COLLATION FROM information_schema.TABLES WHERE TABLE_SCHEMA='$DB_NAME';"
Script’i çalıştırın:
chmod +x utf8mb4_migration.sh
./utf8mb4_migration.sh your_database
Collation Seçimi: unicode_ci mi, general_ci mi?
utf8mb4 geçişinde sık sorulan sorulardan biri collation seçimidir.
- utf8mb4_general_ci: Daha hızlıdır ancak bazı Unicode kurallarını basitleştirir. Türkçe “ı” ve “i” gibi özel karakterlerde beklenmedik sonuçlar verebilir.
- utf8mb4_unicode_ci: Unicode standartlarına tam uyum sağlar. Türkçe dahil pek çok dil için daha doğru sıralama ve karşılaştırma yapar.
- utf8mb4_turkish_ci: Türkçeye özgü sıralama kuralları içerir. “ç, ğ, ı, ö, ş, ü” gibi karakterlerin Türkçe alfabetik sıralamasında işlenmesini sağlar.
- utf8mb4_0900_ai_ci: MySQL 8.0 ile gelen daha modern bir collation. “ai” accent-insensitive, “ci” case-insensitive demektir.
Türkçe içerik yoğun uygulamalar için utf8mb4_turkish_ci ya da utf8mb4_unicode_ci önerilir. Ancak collation değiştirme indeks yeniden oluşturmayı tetikler; büyük tablolarda bu işlem zaman alabilir.
Gerçek Dünya Senaryosu: PHP Uygulaması ile Bağlantı
Bir PHP uygulamasının MySQL’e bağlanırken encoding sorunuyla karşılaştığınızı düşünelim. Uygulama Türkçe karakterleri bozuk kaydediyor. Yapılandırma tarafını düzelttiniz ama sorun devam ediyor. Problem büyük ihtimalle uygulama bağlantı ayarlarındadır.
PDO ile bağlantı için:
# php_config_test.php - test dosyasi
php -r "
$pdo = new PDO(
'mysql:host=localhost;dbname=test_db;charset=utf8mb4',
'kullanici',
'sifre',
[
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci',
PDO::MYSQL_ATTR_CHARSET => 'utf8mb4'
]
);
$stmt = $pdo->query('SHOW VARIABLES LIKE "character_set%"');
while ($row = $stmt->fetch()) {
echo $row[0] . ' = ' . $row[1] . PHP_EOL;
}
echo 'Baglanti basarili!' . PHP_EOL;
"
mysqli kullanan eski uygulamalar için yapılandırma dosyasına şu satırlar eklenebilir:
# Baglanti acildiktan hemen sonra calistirilmasi gereken komutlar
# mysqli_set_charset($conn, 'utf8mb4');
# ya da sorgu olarak:
# SET NAMES 'utf8mb4' COLLATE 'utf8mb4_unicode_ci';
# Bunu shell uzerinden test edebilirsiniz:
mysql -u kullanici -p test_db -e "
SET NAMES 'utf8mb4';
INSERT INTO test_tablo (metin) VALUES ('Merhaba Dünya 😀 Türkçe: şğıöüç');
SELECT * FROM test_tablo ORDER BY id DESC LIMIT 1;
"
Encoding Sorunlarını Teşhis Etmek
Bozuk veri mi var yoksa sadece görüntüleme sorunu mu? Bunu ayırt etmek önemlidir.
# Hex ciktisiyla gercek byte degerlerini gormek
mysql -u root -p your_database -e "
SELECT
metin_kolonu,
HEX(metin_kolonu) as hex_deger,
CHAR_LENGTH(metin_kolonu) as karakter_sayisi,
LENGTH(metin_kolonu) as byte_sayisi
FROM test_tablo
WHERE id = 1;
"
Eğer bir karakter UTF-8’de doğru saklanıyorsa, CHAR_LENGTH ile LENGTH değerleri farklı olabilir (çünkü bazı karakterler 2-4 byte tutar). Ama eğer çift kodlama (double encoding) sorunu varsa, byte sayısı beklenenden çok daha fazla çıkar.
Çift kodlama tespiti ve düzeltmesi gerçek dünyada sık karşılaşılan bir senaryodur. Veriler önce latin1 olarak okunup sonra utf8 olarak kaydedildiyse bu sorun oluşur:
# Cift kodlanmis veriyi duzeltmek icin:
mysql -u root -p your_database -e "
UPDATE kullanicilar
SET ad = CONVERT(BINARY CONVERT(ad USING latin1) USING utf8mb4)
WHERE ad RLIKE '[\xC3\xC4\xC5\xC6\xC7\xC8\xC9]';
"
Bu sorguyu çalıştırmadan önce mutlaka test edin ve sadece etkilenen satırlara uygulayın.
MariaDB’ye Özgü Notlar
MariaDB büyük ölçüde MySQL ile uyumludur ancak birkaç farklılık var. MariaDB 10.2 ve üzerinde utf8mb4 varsayılan olarak daha iyi desteklenir. MariaDB 10.6 ile birlikte utf8 ve utf8mb4 birleştirilmiş; yani utf8 artık gerçek utf8mb4 anlamına geliyor.
# MariaDB versiyonunu kontrol edin
mysql -u root -p -e "SELECT VERSION();"
# MariaDB 10.6+ icin karakter seti durumu
mysql -u root -p -e "SHOW VARIABLES LIKE 'character_set%'; SHOW VARIABLES LIKE 'version%';"
# MariaDB icin my.cnf ornegi
# /etc/mysql/mariadb.conf.d/50-server.cnf
cat << 'EOF' | sudo tee -a /etc/mysql/mariadb.conf.d/50-server.cnf
[mysqld]
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci
[client-server]
default-character-set = utf8mb4
EOF
sudo systemctl restart mariadb
İndeks Uzunluğu Sorunları
utf8mb4 geçişinin en can sıkıcı taraflarından biri indeks uzunluğu limitidir. MySQL 5.6 ve altında, InnoDB varsayılan olarak satır başına maksimum 767 byte indeks uzunluğuna izin verir. utf8 ile VARCHAR(255) bir kolonu indeksleyebilirsiniz (255 x 3 = 765 byte). Ama utf8mb4 ile bu 255 x 4 = 1020 byte eder ve limite takılırsınız.
# Cozum 1: innodb_large_prefix aktif etmek (MySQL 5.6-5.7)
# my.cnf dosyasina ekleyin:
# innodb_large_prefix = ON
# innodb_file_format = Barracuda
# innodb_file_per_table = ON
# Cozum 2: Indeks uzunlugunu kisaltmak
mysql -u root -p your_database -e "
ALTER TABLE kullanicilar
DROP INDEX email_idx;
ALTER TABLE kullanicilar
ADD INDEX email_idx (email(191));
"
# 191 * 4 = 764 byte, limitin altinda
# Cozum 3: MySQL 5.7+ ve MariaDB 10.3+ icin dinamik row format
mysql -u root -p your_database -e "
ALTER TABLE kullanicilar
ROW_FORMAT=DYNAMIC;
"
Sonuç
utf8mb4 geçişi kulağa karmaşık gelse de sistematik bir yaklaşımla sorunsuz tamamlanabilir. Özetlemek gerekirse:
- MySQL’in
utf8‘i gerçek UTF-8 değildir;utf8mb4kullanın - Geçiş öncesi mutlaka yedek alın
- Önce sunucu yapılandırmasını güncelleyin
- Mevcut veritabanı, tablo ve kolonları dönüştürün
- Uygulama bağlantı ayarlarını gözden geçirin
- Türkçe projeler için
utf8mb4_turkish_ciya dautf8mb4_unicode_citercih edin - İndeks uzunluğu sorunlarına hazırlıklı olun
Bu adımları takip ettiğinizde bozuk Türkçe karakterler, kayıp emoji’ler ve encoding uyumsuzlukları tarih olacak. Üretim ortamında bu işlemleri yapmadan önce mutlaka test ortamında deneyin ve bakım penceresi planlayın. Büyük tablolarda CONVERT TO CHARACTER SET işlemi tablo kilidine neden olabileceğinden, yoğun saatlerde çalıştırmaktan kaçının.
