MySQL Semi-Sync Replication Yapılandırması

Selamlar, bu yazımızda MySQL'in semi-sync replication modunu konuşacağız. Açıkçası replikasyon konusu ilk başta kafa karıştırıcı geliyor; çünkü 'asenkron' ve 'senkron' arasında üçüncü bir şık var ve adı 'yarı senkron'. Tecrübeyle sabittir ki bu mod tam ortada bir tradeoff sunuyor: kabul edilebilir gecikme karşılığında ciddi bir veri kaybı koruması. Lafı uzatmadan başlayalım.

Semi-sync nedir, ne işe yarar?

MySQL'de varsayılan replikasyon asenkrondur. Yani primary (kaynak) bir transaction'i commit eder, OK döner ve bu olayı replikalara fırlatır. Replika henüz duymadan primary çakılırsa o transaction'in son birkaç saniyesi kaybolur. Bence bu, prod ortamlarda gözardı edilen riskli bir varsayım.

Semi-sync (yarı senkron) replication ise primary'ye 'commit'i kullanıcıya OK olarak dönmeden önce en az bir replikadan acknowledgement (ACK) bekle' der. Replika binlog event'i kendi relay log'una yazıp ACK gönderene kadar primary müşteriye cevap vermez. Sonuç: ACK gelmiş bir transaction kesin olarak en az iki sunucudadır.

Plugin'leri kurmak

MySQL 8.0.26 ve sonrasında plugin adları rpl_semi_sync_source ve rpl_semi_sync_replica olarak değişti; daha eski sürümlerde hala master/slave terminolojisi geçer. Burada yeni isimlere göre gideceğim.

Önce primary'de:

INSTALL PLUGIN rpl_semi_sync_source SONAME 'semisync_source.so';
SHOW PLUGINS WHERE Name LIKE '%semi%';

Sonra replikada:

INSTALL PLUGIN rpl_semi_sync_replica SONAME 'semisync_replica.so';

Plugin'ler ACTIVE göründüyse iş yarısını bitti sayılır. Asıl iş aktivasyon ve timeout ayarında.

Aktivasyon ve timeout

Primary tarafında semi-sync'i açıyoruz ve bir timeout veriyoruz:

SET GLOBAL rpl_semi_sync_source_enabled = ON;
SET GLOBAL rpl_semi_sync_source_timeout = 10000;

Buradaki 10000 milisaniyedir, yani 10 saniye. Replikadan da semi-sync'i etkinleştirmek ve IO thread'i yeniden başlatmak gerek:

SET GLOBAL rpl_semi_sync_replica_enabled = ON;
STOP REPLICA IO_THREAD;
START REPLICA IO_THREAD;

IO thread'i restart etmeyi unutursanız semi-sync 'açık görünür ama çalışmaz' moduna takılır, sonra status'a bakıp 'neden ON yazıyor da hiçbir şey beklemiyor' diye saç baş yolarsınız. Ben de ilk denememde bu adımı atlamıştım.

Ayarları kalıcı yapmak için my.cnf:

[mysqld]
plugin-load-add              = semisync_source.so
rpl_semi_sync_source_enabled = ON
rpl_semi_sync_source_timeout = 10000

Replika sunucularında ise semisync_replica.so ve rpl_semi_sync_replica_enabled satırlarını eklersiniz.

Ack timeout ve async'e düşme davranışı

Burası işin kritik yeri. Primary, replikanın ACK'ini sonsuza kadar beklemez. rpl_semi_sync_source_timeout süresi dolduğunda otomatik olarak asenkron moda düşer ve transaction'lar normal şekilde akmaya devam eder. Yani bir replika çöktüğünde primary'niz kilitlenmez. Bunu duyup rahatlayanları görüyorum, ama dikkat: o ana kadar yazılan transaction'lar artık ACK garantisi olmadan commit oluyor demektir.

Replika geri geldiğinde ve aradaki gecikmeyi kapattığında semi-sync otomatik olarak yeniden devreye alınır. Bunu izlemek için:

SHOW STATUS LIKE 'Rpl_semi_sync_source_status';
SHOW STATUS LIKE 'Rpl_semi_sync_source_clients';
SHOW STATUS LIKE 'Rpl_semi_sync_source_no_tx';

no_tx değeri timeout sonrası async'e düşen transaction sayısını verir; izlenmesi gereken metriklerden biri budur.

Dayanıklılık tradeoff'u ve lossless mode

Peki ne kazandık ne kaybettik? Kazanç net: ACK almış her transaction en az iki diskte var. Kayıp: her commit ekstra bir round-trip beklediği için latency artar. Bu artış genelde 1-5 ms arası ama yüksek QPS'li sistemlerde toplam tüketim hissedilir.

rpl_semi_sync_source_wait_point değişkeniyle bekleme noktasını seçebilirsiniz. Varsayılan AFTER_SYNC modu (lossless mode olarak da bilinir) primary'nin storage engine commit'ini ACK gelmeden tetiklememesini sağlar. Yani failover anında 'phantom read' yaşanmaz; replikaya geçen okuyucu, primary'nin onayladığı ama replikaya gitmemiş bir satırı asla görmez.

AFTER_COMMIT ise commit'i hızlandırır ama crash anında 'primary'nin gösterdiği ama replikada olmayan' satırlar mümkün hale gelir. Bana sorarsanız AFTER_SYNC dururken AFTER_COMMIT tercih etmek için çok iyi bir nedeniniz olmalı.

Sık karşılaşılan hatalar

  • Replika IO thread'i restart etmemek: Plugin yüklü, değişken ON, ama replika ACK göndermez. Status'a bakınca Rpl_semi_sync_replica_status OFF görünür; bu en sık atlanan adım.
  • rpl_semi_sync_source_timeout değerini çok kısa tutmak: 1-2 saniye gibi düşük değerler, kısa ağ gecikmesi durumlarında bile primary'yi hemen async'e düşürür. 10 saniye makul başlangıç.
  • Tek replikaya güvenmek: Tek replikanız varsa ve o çakarsa otomatik async'e düşersiniz. Gerçek dayanıklılık istiyorsanız en az iki replika ve rpl_semi_sync_source_wait_for_replica_count = 2 düşünün.
  • no_tx metriğini izlememek: Async'e düşmeleri görmüyorsanız semi-sync'in size verdiği güveni hak etmiyor olabilirsiniz; sessiz sessiz async modda çalışıyor olabilir.

Kapanış

Semi-sync replication, full senkron'un latency cezasını ödemeden asenkron'un veri kaybı riskini azaltan pragmatik bir orta yol. Bence çoğu OLTP sistemi için doğru başlangıç noktası budur; Group Replication'a geçmek için somut bir gerekçeniz yoksa semi-sync + iyi izleme yeterli oluyor. Umarım faydalı bir içerik olmuştur, bir sonraki yazıda görüşmek üzere.