MySQL veritabanı yönetimi, sunucuya bir kere kurup unutulacak bir iş değil; günlük aldığınız kararları doğrudan etkileyen bir üretim sorumluluğu. Yanlış kurulmuş bir indeks, bir e-ticaret sitesinde sepet sayfasının yanıt süresini 200 milisaniyeden 4 saniyeye çıkarabilir. Bu rehberde MySQL'i kurulumdan sorgu yazmaya, indekslemeden yedeklemeye kadar pratik komutlarla ele alıyoruz; teorik anlatım değil, terminalde doğrudan çalıştırabileceğiniz adımlar var.
Yazı boyunca mysql komut satırı istemcisini temel alıyoruz, çünkü grafik arayüzler her sunucuda bulunmayabilir ama komut satırı her yerde çalışır. phpMyAdmin veya MySQL Workbench gibi araçlar aynı işlemleri görsel olarak yaptırır, ama altta dönen SQL'i bilmek hata ayıklarken işinize yarar.
MySQL kurulumu nasıl yapılır
Ubuntu ve Debian tabanlı sistemlerde MySQL Server'ı paket yöneticisiyle kurmak birkaç dakika sürer. Kurulumdan hemen sonra güvenlik betiğini çalıştırmak, root parolasını belirlemek ve anonim kullanıcıları temizlemek gerekir.
sudo apt update
sudo apt install mysql-server
sudo mysql_secure_installation
mysql_secure_installation sırayla root parolasını, uzaktan root girişinin kapatılmasını ve test veritabanının silinmesini sorar. Bu sorulara varsayılan olarak "evet" demek, kurulumun ilk günden güvenli başlamasını sağlar. Kurulum bitince servisin çalıştığını kontrol edin:
sudo systemctl status mysql
mysql -u root -p
RHEL veya CentOS türevlerinde paket adı mysql-server yerine bazen mariadb-server olarak karşınıza çıkar; MariaDB, MySQL'in çatallanmış bir sürümüdür ve komut sözdizimi neredeyse birebir aynıdır.
Veritabanı ve tablo nasıl oluşturulur
MySQL'de her şey bir veritabanı içindeki tablolarda tutulur. Önce veritabanını, sonra içindeki tabloları oluşturursunuz; her sütun için bir veri türü belirtmek zorunludur.

CREATE DATABASE magaza;
USE magaza;
CREATE TABLE urunler (
id INT AUTO_INCREMENT PRIMARY KEY,
ad VARCHAR(120) NOT NULL,
fiyat DECIMAL(10,2) NOT NULL,
stok INT DEFAULT 0,
eklenme_tarihi DATETIME DEFAULT CURRENT_TIMESTAMP
);
AUTO_INCREMENT PRIMARY KEY her yeni satıra otomatik ve benzersiz bir numara atar; bunu elle yönetmeye kalkışmak eş zamanlı yazmalarda çakışmaya yol açar. Parasal değerler için DECIMAL(10,2), FLOAT'a tercih edilmelidir, çünkü kayan noktalı sayılar yuvarlama hatası üretebilir. Bir faturada 19.99 TL'nin 19.988 gibi görünmesi güven kırar.
Sütun türü seçimi depolama alanını da etkiler. VARCHAR(120) yalnızca girilen karakter kadar yer kaplarken, gereksiz yere TEXT kullanmak indeks performansını düşürür. Doğru tür seçimi, tabloyu sonradan değiştirmek zorunda kalmamak için baştan düşünülmesi gereken bir karardır.
CRUD komutlarıyla veri işlemek
Bir tabloya kayıt eklemek, okumak, güncellemek ve silmek MySQL'in günlük işidir. Her işlemin kendi komutu var ve bunları ezbere bilmek sorun giderirken zaman kazandırır.
-- Ekleme
INSERT INTO urunler (ad, fiyat, stok) VALUES ('Klavye', 349.90, 25);
-- Okuma
SELECT ad, fiyat FROM urunler WHERE stok > 0 ORDER BY fiyat DESC;
-- Güncelleme
UPDATE urunler SET stok = stok - 1 WHERE id = 4;
-- Silme
DELETE FROM urunler WHERE stok = 0 AND eklenme_tarihi < '2024-01-01';
UPDATE ve DELETE komutlarında WHERE koşulunu unutmak tablodaki tüm satırları etkiler; MySQL'e yeni başlayanların en sık düştüğü tuzak budur. Üretim veritabanında bu komutları çalıştırmadan önce aynı sorguyu SELECT ile deneyip hangi satırların etkileneceğini görmek iyi bir alışkanlıktır.
Filtreleme ve sıralama WHERE ve ORDER BY ile yapılır. LIMIT 10 gibi bir ek, büyük tablolarda yalnızca ilk 10 sonucu getirerek hem ağ trafiğini hem de sunucu yükünü azaltır.
JOIN ile tablolar arası ilişki kurmak
İlişkisel veritabanlarının gücü, verileri tek bir devasa tabloda değil, birbirine bağlı küçük tablolarda tutmaktan gelir. siparisler tablosu, musteriler tablosuna bir musteri_id yabancı anahtarıyla bağlanabilir.

SELECT musteriler.ad, siparisler.tutar, siparisler.tarih
FROM siparisler
JOIN musteriler ON siparisler.musteri_id = musteriler.id
WHERE siparisler.tarih >= '2026-01-01';
Bu sorgu iki ayrı tablodaki verileri tek bir sonuç kümesinde birleştirir. INNER JOIN yalnızca her iki tabloda da eşleşen kayıtları getirirken, LEFT JOIN sol tablodaki tüm kayıtları, sağda eşleşme olmasa bile döndürür. Hangi JOIN türünü kullanacağınız, eşleşmeyen kayıtları da görmek isteyip istemediğinize bağlıdır.
Yabancı anahtar tanımlamak veri tutarlılığını veritabanı seviyesinde garanti eder:
ALTER TABLE siparisler
ADD CONSTRAINT fk_musteri
FOREIGN KEY (musteri_id) REFERENCES musteriler(id);
Bu kısıtlama sayesinde, var olmayan bir müşteri kimliğine sipariş eklemeye çalıştığınızda MySQL işlemi reddeder. Hata, uygulama katmanına sızmadan veritabanında yakalanır.
İndeksleme ve performans
Bir tablo büyüdükçe, WHERE koşulunda sık kullanılan sütunlarda indeks olmaması sorguları yavaşlatır. İndeks, kitabın arkasındaki dizin gibi çalışır; MySQL tüm satırları tek tek taramak yerine doğrudan aradığı yere gider.

CREATE INDEX idx_urun_ad ON urunler(ad);
EXPLAIN SELECT * FROM urunler WHERE ad = 'Klavye';
EXPLAIN komutu sorgunun nasıl çalıştırılacağını gösterir. type sütununda ALL görürseniz MySQL tüm tabloyu taramak zorunda kalıyor demektir; bu genellikle eksik bir indeks işaretidir. Ancak her sütuna indeks eklemek çözüm değildir. Her yazma işleminde MySQL tüm indeksleri de güncellemek zorunda kalır, bu da INSERT ve UPDATE performansını düşürür.
Pratikte en çok fayda, WHERE, JOIN ve ORDER BY içinde sık geçen sütunlara indeks koymaktan gelir. Nadiren sorgulanan bir sütuna indeks eklemek depolama alanı tüketmekten başka bir şey yapmaz.
Yetkilendirme ve yedekleme
Her uygulamanın veritabanına root ile bağlanması, tek bir sızıntıda tüm veritabanını riske atar. Bunun yerine uygulamaya özel, sınırlı yetkili bir kullanıcı tanımlamak en az ayrıcalık ilkesinin pratik uygulamasıdır.
CREATE USER 'magaza_app'@'localhost' IDENTIFIED BY 'guclu-bir-parola';
GRANT SELECT, INSERT, UPDATE, DELETE ON magaza.* TO 'magaza_app'@'localhost';
FLUSH PRIVILEGES;
Bu kullanıcı yalnızca magaza veritabanında CRUD işlemleri yapabilir; sunucu yapılandırmasını değiştiremez veya başka veritabanlarına dokunamaz. Yetkileri düzenli aralıklarla gözden geçirmek, artık kullanılmayan hesapların birikmesini önler.
Yedekleme tarafında mysqldump, tek bir komutla tüm veritabanının SQL çıktısını alır:
mysqldump -u root -p magaza > magaza_yedek_$(date +%Y%m%d).sql
Bu yedeği başka bir sunucuya kopyalamadan veya en azından farklı bir diske taşımadan bırakmak, disk arızası senaryosunda işe yaramaz. Aldığınız yedeği ara sıra gerçekten geri yükleyip test etmek, felaket anında yedeğin bozuk çıkmasını önleyen tek yöntemdir.
Sağlam bir MySQL yedekleme rutini şu üç noktayı karşılamalı:
- Yedek dosyası, üretim sunucusundan farklı bir konumda saklanmalı
mysqldumpişlemicronile otomatik ve düzenli çalıştırılmalı- Alınan yedek, ayda en az bir kez gerçekten geri yüklenerek test edilmeli
Sık karşılaşılan hatalar
ERROR 1045 (28000): Access denied for user hatası çoğunlukla yanlış parola veya kullanıcının o host'tan bağlanma yetkisi olmamasından kaynaklanır. 'magaza_app'@'localhost' yalnızca yerelden bağlanmaya izin verir; uzak bir sunucudan bağlanmak istiyorsanız kullanıcıyı 'magaza_app'@'%' şeklinde tanımlamanız gerekir.
ERROR 1146 (42S02): Table doesn't exist hatası genelde USE magaza; komutunu unutup yanlış veritabanında sorgu çalıştırmaktan gelir. Bağlantı kurduktan sonra hangi veritabanında olduğunuzu SELECT DATABASE(); ile kontrol etmek bu karışıklıkları baştan önler.
Karakter seti uyuşmazlıkları da yaygındır; Türkçe karakterlerin bozuk görünmesi genellikle tablo utf8mb4 yerine eski latin1 ile oluşturulduğunda ortaya çıkar. Yeni tablo oluştururken CHARACTER SET utf8mb4 COLLATE utf8mb4_turkish_ci eklemek bu sorunu baştan çözer. Var olan bir tabloyu sonradan dönüştürmek isterseniz ALTER TABLE tablo_adi CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_turkish_ci; komutu, mevcut veriyi koruyarak karakter setini günceller. MySQL'in resmi dokümantasyonu, karakter seti ve indeksleme konularında ayrıntılı referans sağlar: MySQL 8.0 Reference Manual.
Henüz yorum yok.
Sohbete katıl. Yorumlar yayınlanmadan önce moderasyondan geçer.