Büyük Tablolarda Güvenli ALTER TABLE Yöntemleri
Production ortamında büyük bir tabloya ALTER TABLE çalıştırdığınız ve tablonun saatlerce kilitlendiğini fark ettiğiniz o korkunç anı yaşadıysanız, bu yazı tam size göre. MySQL ve MariaDB’de büyük tablolarda şema değişikliği yapmak, yanlış yaklaşıldığında hem uzun süreli downtime’a hem de veri tutarsızlıklarına yol açabilir. Bu rehberde, milyonlarca satırlık tablolarda güvenli ALTER TABLE yapmanın yöntemlerini, araçlarını ve gerçek dünya senaryolarını ele alacağız.
Sorunun Kaynağı: ALTER TABLE Neden Tehlikeli?
MySQL ve MariaDB’de geleneksel ALTER TABLE işlemi, arka planda şu adımları izler:
- Orijinal tabloyu kilitler (tablo seviyesinde write lock)
- Geçici bir tablo oluşturur
- Tüm verileri yeni şemaya göre geçici tabloya kopyalar
- Orijinal tabloyu siler, geçici tabloyu orijinal adıyla yeniden adlandırır
- Kilidi açar
Bu süreç, 10 milyon satırlık bir tablo için saatler sürebilir. Bu süre boyunca tabloya yazma işlemi yapılamaz, uygulama hata verir ve kullanıcılarınız etkilenir.
-- Kötü örnek: Production'da doğrudan çalıştırılmamalı
ALTER TABLE orders ADD COLUMN discount_code VARCHAR(50);
-- Bu komut 50 milyon satırlık bir tabloda saatlerce sürebilir
-- ve tablo tamamen kilitlenir
Online DDL: MySQL 5.6+ ve MariaDB’nin Katkısı
MySQL 5.6 ile birlikte gelen Online DDL özelliği, bazı ALTER TABLE işlemlerini tablo kilidi olmadan yapabilmeyi sağlar. MariaDB ise buna ek olarak kendi optimizasyonlarını getirmiştir.
-- ALGORITHM ve LOCK parametreleriyle ALTER TABLE
ALTER TABLE orders
ADD COLUMN discount_code VARCHAR(50),
ALGORITHM=INPLACE,
LOCK=NONE;
ALGORITHM parametresi:
- COPY: Eski yöntem, geçici tablo oluşturur, yavaş ve kilitler
- INPLACE: Tablo yerinde değiştirilir, genellikle daha hızlı
- INSTANT: MariaDB 10.3+ ve MySQL 8.0+, anlık değişiklik (yalnızca metadata)
LOCK parametresi:
- NONE: Okuma ve yazma işlemine izin ver, kilitleme
- SHARED: Okumaya izin ver, yazmayı engelle
- EXCLUSIVE: Hem okuma hem yazmayı engelle
- DEFAULT: Mümkün olan en az kısıtlayıcı kilit seçeneğini uygula
INSTANT Algorithm: Gerçek Anlamda Anlık Değişiklik
MariaDB 10.3.2 ve MySQL 8.0 ile gelen INSTANT algoritması, bazı işlemler için metadata değişikliğiyle yetinerek tabloyu kopyalamadan şema değişikliği yapabilir.
-- MariaDB 10.3+ / MySQL 8.0+
-- Tablo verisi hiç kopyalanmaz, saniyeler içinde tamamlanır
ALTER TABLE orders
ADD COLUMN notes TEXT,
ALGORITHM=INSTANT;
-- InnoDB tablolarında kolon ekleme için ideal
ALTER TABLE products
ADD COLUMN meta_json JSON DEFAULT NULL,
ALGORITHM=INSTANT;
INSTANT algoritması şu işlemler için desteklenir:
- Tablonun sonuna kolon ekleme
- Kolon varsayılan değerini değiştirme
- ENUM ve SET listesine değer ekleme
- NULL kısıtlamasını kaldırma veya ekleme (bazı durumlarda)
pt-online-schema-change: Percona’nın Kurtarıcı Aracı
Production sistemlerde en yaygın kullanılan güvenli ALTER TABLE yöntemi pt-online-schema-change (pt-osc) aracıdır. Percona Toolkit’in bir parçası olan bu araç, değişikliği arka planda gerçekleştirir ve uygulama çalışmaya devam eder.
Kurulum
# Ubuntu/Debian
apt-get install percona-toolkit
# RHEL/CentOS/Rocky Linux
yum install percona-toolkit
# Ya da doğrudan indirme
wget https://downloads.percona.com/downloads/percona-toolkit/3.5.7/binary/tarball/percona-toolkit-3.5.7_x86_64.tar.gz
tar -xzf percona-toolkit-3.5.7_x86_64.tar.gz
Temel Kullanım
# Temel syntax
pt-online-schema-change
--host=localhost
--user=dbadmin
--password=secretpass
--alter="ADD COLUMN discount_code VARCHAR(50)"
D=production_db,t=orders
--execute
# Dry-run ile önce ne yapacağını gör
pt-online-schema-change
--host=localhost
--user=dbadmin
--password=secretpass
--alter="ADD COLUMN discount_code VARCHAR(50)"
D=production_db,t=orders
--dry-run
pt-osc Nasıl Çalışır?
_orders_newadında yeni bir tablo oluşturur, yeni şemayı burada uygular- Orijinal tabloya trigger’lar ekler (INSERT, UPDATE, DELETE)
- Verileri küçük chunk’lar halinde kopyalar
- Kopyalama tamamlandığında tabloları atomik olarak yer değiştirir
- Trigger’ları temizler
Gerçek Dünya Senaryosu: E-Ticaret Sipariş Tablosu
# 50 milyon satırlık orders tablosuna index ekleme
# --chunk-size ile yük kontrolü
# --max-load ile sunucu yükü sınırı
# --critical-load ile emergency durdurma
pt-online-schema-change
--host=db-master.example.com
--port=3306
--user=pt_osc_user
--password='$3cureP@ss'
--alter="ADD INDEX idx_customer_status (customer_id, status)"
D=ecommerce,t=orders
--chunk-size=5000
--chunk-time=0.5
--max-load="Threads_running=50"
--critical-load="Threads_running=100"
--sleep=0.5
--progress=time,30
--execute 2>&1 | tee /var/log/pt-osc-orders-$(date +%Y%m%d).log
Önemli parametreler:
- –chunk-size: Her seferinde kaç satır kopyalanacağı (varsayılan: 1000)
- –chunk-time: Her chunk için hedef süre (saniye)
- –max-load: Bu değerin üzerindeyse işlemi yavaşlat
- –critical-load: Bu değerin üzerindeyse işlemi tamamen durdur
- –sleep: Chunk’lar arasındaki bekleme süresi
- –progress: İlerleme raporu aralığı
gh-ost: GitHub’ın Trigger-Free Çözümü
gh-ost (GitHub’s Online Schema Transmogrifier), pt-osc’un trigger tabanlı yaklaşımının aksine MySQL binary log’larını dinleyerek çalışır. Bu sayede trigger overhead’inden kaçınır.
# gh-ost kurulumu
wget https://github.com/github/gh-ost/releases/download/v1.1.6/gh-ost_1.1.6_linux_amd64.tar.gz
tar -xzf gh-ost_1.1.6_linux_amd64.tar.gz
mv gh-ost /usr/local/bin/
# Temel kullanım
gh-ost
--host="db-master.example.com"
--port=3306
--user="ghost_user"
--password="ghostpass"
--database="ecommerce"
--table="orders"
--alter="ADD COLUMN loyalty_points INT DEFAULT 0"
--allow-on-master
--execute
# Replica üzerinden okuyarak master'a yazan yaklaşım (önerilen)
gh-ost
--host="db-replica.example.com"
--port=3306
--user="ghost_user"
--password="ghostpass"
--database="ecommerce"
--table="orders"
--alter="DROP COLUMN legacy_field"
--assume-master-host="db-master.example.com:3306"
--execute
gh-ost’un Üstünlükleri
- Trigger kullanmaz, dolayısıyla trigger overhead’i yoktur
- Binary log’dan okuyarak replikasyon gecikme etkisini minimize eder
- Interactive mod ile işlemi duraklatma/hızlandırma imkanı sunar
- Gerçek zamanlı throttle kontrolü
# gh-ost çalışırken socket üzerinden kontrol
# İşlemi duraklatmak
echo "throttle" | nc -U /tmp/gh-ost.orders.sock
# İşlemi devam ettirmek
echo "no-throttle" | nc -U /tmp/gh-ost.orders.sock
# İlerleme durumu
echo "status" | nc -U /tmp/gh-ost.orders.sock
Partition’lı Tablolarda Güvenli ALTER TABLE
Büyük tablolarla çalışırken tablo partitioning hem performansı artırır hem de ALTER TABLE işlemlerini kolaylaştırır. Belirli bir partition üzerinde değişiklik yaparken diğer partition’lar etkilenmez.
-- Mevcut büyük tabloyu partition'lara bölme
ALTER TABLE orders
PARTITION BY RANGE (YEAR(created_at)) (
PARTITION p2021 VALUES LESS THAN (2022),
PARTITION p2022 VALUES LESS THAN (2023),
PARTITION p2023 VALUES LESS THAN (2024),
PARTITION p2024 VALUES LESS THAN (2025),
PARTITION p_future VALUES LESS THAN MAXVALUE
);
-- Tek bir partition'ı rebuild etme (diğerleri etkilenmez)
ALTER TABLE orders REBUILD PARTITION p2021;
-- Yeni partition ekleme (anlık işlem)
ALTER TABLE orders REORGANIZE PARTITION p_future INTO (
PARTITION p2025 VALUES LESS THAN (2026),
PARTITION p_future VALUES LESS THAN MAXVALUE
);
Güvenlik Kontrolleri ve Hazırlık Aşaması
Büyük tabloda ALTER TABLE yapmadan önce mutlaka yapılması gereken kontroller vardır.
Ön Kontrol Script’i
#!/bin/bash
# pre-alter-check.sh
# Büyük tablo alter öncesi hazırlık kontrolü
DB_HOST="localhost"
DB_USER="dbadmin"
DB_PASS="secretpass"
DATABASE="ecommerce"
TABLE="orders"
echo "=== ALTER TABLE Ön Kontrol Raporu ==="
echo "Tarih: $(date)"
echo ""
# Tablo boyutunu kontrol et
echo "--- Tablo Boyutu ---"
mysql -h $DB_HOST -u $DB_USER -p$DB_PASS $DATABASE -e "
SELECT
table_name,
table_rows,
ROUND(data_length / 1024 / 1024, 2) AS data_mb,
ROUND(index_length / 1024 / 1024, 2) AS index_mb,
ROUND((data_length + index_length) / 1024 / 1024, 2) AS total_mb
FROM information_schema.tables
WHERE table_schema = '$DATABASE' AND table_name = '$TABLE';
"
# Aktif işlemleri kontrol et
echo ""
echo "--- Aktif İşlemler ---"
mysql -h $DB_HOST -u $DB_USER -p$DB_PASS -e "
SHOW PROCESSLIST;
" | grep -v "Sleep"
# Disk alanı kontrolü
echo ""
echo "--- Disk Alanı (MySQL datadir) ---"
df -h /var/lib/mysql
# Replikasyon lag kontrolü
echo ""
echo "--- Replikasyon Durumu ---"
mysql -h $DB_HOST -u $DB_USER -p$DB_PASS -e "SHOW SLAVE STATUSG" 2>/dev/null | grep -E "Seconds_Behind_Master|Slave_IO_Running|Slave_SQL_Running"
echo ""
echo "Kontrol tamamlandı."
Replikasyon Gecikme Kontrolüyle pt-osc
# Replikasyon gecikmesi belirli bir threshold'u geçerse otomatik durdur
pt-online-schema-change
--host=db-master.example.com
--user=dbadmin
--password=secretpass
--alter="ADD INDEX idx_created_at (created_at)"
D=ecommerce,t=orders
--max-lag=5
--check-slave-lag=db-replica.example.com
--chunk-size=2000
--max-load="Threads_running=40"
--execute
Geri Alma Planı: Bir Şeyler Ters Giderse
Her ALTER TABLE işlemi öncesinde bir rollback planı olmalıdır.
-- Önce tablonun backup'ını al
-- Sadece şema için:
mysqldump --no-data -u dbadmin -p ecommerce orders > orders_schema_backup_$(date +%Y%m%d).sql
-- Tüm tablo için (küçük tablolarda):
mysqldump -u dbadmin -p ecommerce orders > orders_full_backup_$(date +%Y%m%d).sql
-- Büyük tablolar için sadece şema değişikliğini kaydet
-- ve pt-osc'un oluşturduğu _orders_new tablosunu silmeyi unutma
pt-osc veya gh-ost kullandığınızda araç başarısız olursa, geçici tablo (_orders_new veya _orders_ghc) silinmez. Bir sonraki denemeden önce bunları temizlemeniz gerekir.
-- pt-osc artık tablosunu temizle
DROP TABLE IF EXISTS _orders_new;
DROP TABLE IF EXISTS _orders_old;
-- Trigger'ları temizle (pt-osc başarısız olursa kalabilir)
DROP TRIGGER IF EXISTS pt_osc_ecommerce_orders_ins;
DROP TRIGGER IF EXISTS pt_osc_ecommerce_orders_upd;
DROP TRIGGER IF EXISTS pt_osc_ecommerce_orders_del;
Zaman Planlaması ve Maintenance Window
Tüm güvenli yöntemlere rağmen, çok kritik değişiklikler için maintenance window planlamak hala iyi bir pratiktir.
#!/bin/bash
# scheduled-alter.sh
# Belirli bir saatte ALTER TABLE çalıştır
TARGET_HOUR=02 # Gece 02:00
CURRENT_HOUR=$(date +%H)
if [ "$CURRENT_HOUR" -eq "$TARGET_HOUR" ]; then
echo "$(date): ALTER TABLE işlemi başlatılıyor..." >> /var/log/alter-table.log
pt-online-schema-change
--host=localhost
--user=dbadmin
--password=secretpass
--alter="ADD COLUMN updated_by INT DEFAULT NULL"
D=ecommerce,t=orders
--chunk-size=3000
--max-load="Threads_running=30"
--execute >> /var/log/alter-table.log 2>&1
echo "$(date): İşlem tamamlandı, çıkış kodu: $?" >> /var/log/alter-table.log
else
echo "Henüz zaman değil. Hedef saat: $TARGET_HOUR, Şu an: $CURRENT_HOUR"
fi
Hangi Yöntemi Ne Zaman Kullanmalısınız?
Kullanılacak yöntemi seçerken şu kriterleri göz önünde bulundurun:
- ALGORITHM=INSTANT kullanın: Tablo sonuna kolon ekleme, varsayılan değer değiştirme, ENUM değeri ekleme gibi basit işlemlerde. MariaDB 10.3+ veya MySQL 8.0+ gerektirir.
- ALGORITHM=INPLACE, LOCK=NONE kullanın: Online DDL destekleyen işlemler için, küçük-orta ölçekli tablolarda (1-5 milyon satır).
- pt-online-schema-change kullanın: Büyük tablolarda (5 milyon satır üzeri), replikasyon ortamında, trigger kullanımı sorun değilse. Olgun ve iyi test edilmiş bir araçtır.
- gh-ost kullanın: Trigger overhead’ini önlemek istiyorsanız, binary log erişiminiz varsa, işlem sırasında interaktif kontrol istiyorsanız ve özellikle yoğun yazma trafiği olan sistemlerde.
- Manuel backup + geleneksel ALTER: Downtime kabul edilebiliyorsa, tablonun tam yedeği alındıysa ve değişiklik çok karmaşıksa.
İzleme ve Doğrulama
ALTER TABLE sonrasında mutlaka doğrulama yapın:
-- Şema değişikliğini doğrula
DESCRIBE orders;
SHOW CREATE TABLE ordersG
-- Index'in gerçekten kullanıldığını kontrol et
EXPLAIN SELECT * FROM orders WHERE customer_id = 12345 AND status = 'active';
-- Tablo istatistiklerini güncelle
ANALYZE TABLE orders;
OPTIMIZE TABLE orders;
-- Replikasyon tutarlılığını kontrol et (pt-table-checksum ile)
pt-table-checksum
--host=db-master.example.com
--user=dbadmin
--password=secretpass
--databases=ecommerce
--tables=orders
Sonuç
Büyük tablolarda ALTER TABLE yapmak, doğru araç ve yöntem kullanıldığında artık göründüğü kadar tehlikeli değil. Özetlemek gerekirse:
- Önce mutlaka bir dry-run veya test ortamında deneyin
- Disk alanı kontrolü yapmayı atlamamak, geçici tablo için orijinal tablo kadar yer gerekebilir
- pt-osc veya gh-ost, production sistemler için neredeyse her zaman ilk tercihiniz olmalı
- MariaDB 10.3+ veya MySQL 8.0 kullanıyorsanız INSTANT algoritmasını değerlendirin
- Replikasyon lag’ini işlem boyunca izleyin
- Her işlem için rollback planı hazırda tutun
Production sistemlerde aceleci davranmak, birkaç saatlik dikkatli planlama yapılmamasının bedelini saatler süren kesinti ve veri sorunlarıyla ödemeye yol açabilir. Büyük tablo değişikliklerini bir süreç olarak ele alın, gerekli zamanı ayırın ve her adımı belgelendirin.
