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?

  1. _orders_new adında yeni bir tablo oluşturur, yeni şemayı burada uygular
  2. Orijinal tabloya trigger’lar ekler (INSERT, UPDATE, DELETE)
  3. Verileri küçük chunk’lar halinde kopyalar
  4. Kopyalama tamamlandığında tabloları atomik olarak yer değiştirir
  5. 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.

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir