Metin2 sunucusunda oyuncu verisini gerçekte yöneten katman, çoğu kişinin sandığı gibi oyun çekirdeği değil, metin2 db cache sürecidir. Bir karakteri hareket ettirdiğinizde, bir eşya bastığınızda ya da görev tamamladığınızda bu işlemlerin hiçbiri doğrudan MySQL'e gitmez. Önce bellekte tutulan bir önbellek katmanından geçer. Bu yazıda db sürecinin oyuncu verisini nasıl tuttuğunu, ne zaman diske yazdığını ve bu mimarinin neden bu kadar yaygın kullanıldığını adım adım anlatıyorum.
Çok süreçli Metin2 mimarisi
Klasik bir Metin2 sunucusu tek bir program değildir. En az üç ayrı süreçten oluşur ve her biri farklı bir görevi üstlenir:
- auth — Giriş ve hesap doğrulama yapan süreç. Kullanıcı adı ve şifreyi kontrol eder, oturum anahtarı üretir.
- db — Tüm MySQL trafiğinin tek kapısı. Oyuncu verisini belleğe alır, önbellekler ve periyodik olarak veritabanına yazar.
- game / core — Oyunun kendisi. Hareket, dövüş, görev, NPC ve harita mantığı burada çalışır. Büyük sunucularda birden fazla
core(kanal) bulunur.
Önemli nokta şu: oyun çekirdekleri MySQL ile hiçbir zaman doğrudan konuşmaz. Tüm veri okuma ve yazma istekleri db sürecine özel bir TCP protokolüyle gönderilir. db süreci bu isteklere cevap verir, gerektiğinde MySQL'i sorgular, çoğu zaman ise zaten bellekte tuttuğu kopyadan cevap döner.
db cache neden var?
Bu ek katman ilk bakışta gereksiz karmaşıklık gibi görünebilir. Oysa amacı çok somut: MySQL'i sürekli sorgudan korumak. Yüzlerce oyuncunun aynı anda saniyede onlarca kez değiştirdiği konum, can, tecrübe ve envanter verisi düşünüldüğünde, her değişikliği anında veritabanına yazmak diskin ve InnoDB'nin altından kalkamayacağı bir yük olurdu.
db cache bu sorunu şöyle çözer:
- Oyuncu giriş yaptığında tüm verisi (karakter, envanter, görev, affect, kasa) bir kez MySQL'den okunur ve bellekte tutulur.
- Oyun sırasındaki tüm değişiklikler önce bu bellek kopyası üzerinde yapılır — diske gitmez.
- Veritabanına yazma işlemi gruplanır ve belirli aralıklarla, ayrıca oyuncu çıkışında yapılır.
Sonuç: MySQL'e giden yazma sayısı binlerce kat azalır, oyun içi işlemler ise bellek hızında yanıt verir.
Oyuncu verisi belleğe nasıl yüklenir?
Bir oyuncu karakter seçtiğinde, db süreci o karakterin tüm verisini ilgili tablolardan toplar. Metin2'de oyuncu verisi tek tabloda durmaz; mantıksal olarak bölünmüştür:
player— temel karakter (seviye, tecrübe, konum, statlar)item— envanter, kasa ve donanım eşyalarıquest— görev durumu ve bayraklaraffect— aktif buff/debuff etkilerisafeboxvemall— depo ve mağaza kasası
Bu satırlar bellekte karaktere bağlı bir önbellek nesnesinde birleştirilir. Oyuncu çevrimiçi kaldığı sürece bu nesne yaşar; çekirdek her veri sorgusunda MySQL yerine bu nesneye danışır.
Veri ne zaman diske yazılır?
Kayıt mantığı bu mimarinin en kritik kısmıdır, çünkü oyuncu kayıplarının (item dupe, rollback, kaybolan eşya) çoğu burada doğar. Veri üç durumda MySQL'e yazılır:
- Periyodik kayıt: db süreci belirli aralıklarla bellekteki "kirli" (değişmiş) kayıtları tarar ve MySQL'e
UPDATEolarak yazar. Bu aralık yapılandırmaya göre değişir; tipik olarak birkaç saniyelik bir gecikme penceresidir. - Çıkışta kayıt: Oyuncu oyundan çıktığında veya bağlantı koptuğunda, o karakterin tüm verisi anında diske yazılır ve önbellekten düşürülür.
- Önemli olaylarda anlık kayıt: Para basımı, kasa işlemi gibi kritik işlemlerde veri beklemeden yazılabilir.
İşte bir sunucu beklenmedik şekilde çökerse (kill -9, donanım arızası, OOM) son periyodik kayıttan sonraki tüm değişiklikler kaybolur — bu yüzden oyuncular "rollback" yaşar. Mimarinin hız avantajı, aynı zamanda onun en büyük risk noktasıdır.
Çekirdek ile db arasındaki iletişim
Oyun çekirdeği ile db arasındaki konuşma, paket tabanlı özel bir protokolle yürür. Bir çekirdek "şu oyuncunun verisini yükle" ya da "bu eşyayı güncelle" şeklinde bir paket gönderir, db süreci kuyruğuna alır, işler ve cevabı geri yollar. Bu yapı, birden fazla core aynı oyuncu havuzunu paylaştığında tutarlılığı tek bir yetkili kaynaktan (db) sağlar.
Yapılandırma tarafında bağlantı genellikle şu satırlarla kurulur:
PLAYER_SQL: localhost player root sifre
COMMON_SQL: localhost common root sifre
# core tarafında db sürecinin adresi
db_addr: 127.0.0.1
db_port: 15000
Burada iki ayrı veritabanı görürsünüz: player (oyuncuya özel, sürekli değişen veri) ve common (item_proto, mob_proto gibi salt-okunur tanım tabloları). common verisi nadiren değiştiği için ağır önbelleklenir; player verisi ise db cache'in asıl yönettiği veridir.
İyi bir db cache yönetimi için ipuçları
- Kayıt aralığını dengeli tutun: Çok uzun aralık rollback riskini büyütür, çok kısa aralık MySQL'i yorar. Sunucu yükünüze göre ayarlayın.
- db sürecini izleyin: db çökerse oyun çekirdekleri ayakta kalsa bile hiçbir veri kaydedilemez. Süreç sağlığını mutlaka izleyin.
- InnoDB kullanın: player tablolarında MyISAM yerine InnoDB tercih edin; çökme sonrası tutarlılık ve eşzamanlı yazma için fark yaratır.
- Yedek alın: Düzenli
mysqldumpyedeği, periyodik kayıt penceresinin kaçınılmaz veri kaybını telafi etmenin tek güvenli yoludur.
Sık Sorulan Sorular
db cache çökerse oyuncu verisi kaybolur mu?
Son periyodik kayıttan sonra biriken tüm değişiklikler kaybolur. db süreci düzgün kapanırsa (graceful shutdown) bellekteki veriyi diske boşaltır; ani çökmede (kill -9) ise yalnızca son kayıt anına kadar olan veri güvendedir.
Neden çekirdek doğrudan MySQL'e bağlanmıyor?
Performans ve tutarlılık için. Tek bir db süreci hem yazma yükünü gruplayıp MySQL'i korur, hem de birden fazla çekirdek arasında tek yetkili veri kaynağı olarak veri çakışmalarını engeller.
player ve common veritabanı arasındaki fark nedir?
player oyuncuya ait sürekli değişen veriyi (karakter, eşya, görev) tutar ve db cache tarafından yönetilir. common ise item_proto, mob_proto gibi nadiren değişen tanım tablolarını içerir ve ağırlıklı olarak okunur.
Metin2 sunucunuzda veri kaybı, rollback ya da performans sorunları mı yaşıyorsunuz? db cache yapılandırmanızı ve veritabanı mimarinizi birlikte gözden geçirelim. Benimle iletişime geçin.