Veri Göçü: Kayıpsız Geçiş İçin Eşleme Tablosu, Mutabakat ve Geri Alma
Veri göçünde kayıpsız geçiş: alan eşleme tablosu, kimlik haritası, prova koşumu, kesim anı ve geri alma planı; Python dönüştürücü ve mutabakat örnekleri.
Veri göçü, eski bir sistemdeki kayıtları yeni sisteme taşıma işidir; ama zor kısmı kopyalamak değil, "aynı şeyi taşıdım" iddiasını kanıtlamaktır. ERP değişimi, iki CRM'in birleştirilmesi, yıllardır büyüyen bir Excel veya Access tabanından PostgreSQL'e geçiş: hepsinde aynı soru sorulur. Hedefteki veri kaynağa eşit mi, eksik mi, sessizce bozulmuş mu?
"Aktardım" demek yetmez, çünkü göç hataları belirti vermeden oluşur. Tarih biçimi gün-ay yer değiştirerek kayar, ondalık ayırıcı virgülle nokta arasında karışır, iki kaynakta aynı kimlik farklı kişilere aittir, boş alanlar varsayılan değerle dolar. Bu hatalar taşımadan aylar sonra bir raporda görünür; o sırada kaynak sistem çoğunlukla kapatılmış, karşılaştıracak bir şey kalmamıştır.
Bu yazıda kayıpsız geçiş için beş parçayı anlatıyorum: alan eşleme tablosu, kimlik haritası, prova koşumu, kesim anı planı ve geri alma. İki çalışan Python örneği var: bildirimsel bir eşleme sözlüğünden okuyup dönüştüren ve reddedilen satırları ayrı dosyaya yazan dönüştürücü ile göç sonrası mutabakatı dört bağımsız ölçüyle yapan betik.
Alan eşleme tablosu ve kimlik haritası nedir?
Alan eşleme tablosu, kaynak sistemdeki her alanın hedef sistemde hangi alana, hangi dönüşümle ve hangi zorunluluk kuralıyla gideceğini yazan tek bir belgedir. Kimlik haritası ise kaynak anahtarların (eski müşteri numarası gibi) hedefte üretilen yeni anahtarlara karşılık tablosudur. İkisi de koddan önce yazılır, çünkü göçte tartışılacak bütün kararlar bu iki tabloda görünür hâle gelir.
Eşleme tablosu bir elektronik tablo olabilir; önemli olan her satırın aynı sütunları taşımasıdır:
- Kaynak alan, hedef alan ve her ikisinin veri tipi
- Dönüşüm kuralı: tarih biçimi, birim, büyük-küçük harf, kırpma
- Zorunluluk: boş gelirse reddet mi, varsayılan mı, NULL mu
- Kod tablosu eşlemesi: kaynaktaki "A" hedefte "aktif" gibi
- Kararı veren kişi ve tarih; alan silinecekse gerekçesi
Kimlik haritası kalıcı saklanır, çünkü ilişkili tabloların tamamı ondan geçer: siparişler müşteriye, faturalar siparişe eski kimlikle bağlıdır ve her biri göç sırasında yeni kimliğe çevrilmek zorundadır. Aynı harita mutabakatta satırları eşleştirmek ve geri almada hedefe sonradan girilen kayıtları kaynağa geri taşımak için de kullanılır. Eşleme tablosunun hedef tarafı aslında bir veri sözleşmesidir; hedef şemayı kodda doğrulamak için Pydantic ile veri sözleşmesi ve şema doğrulama yazısındaki yaklaşımı aynen uygulayabilirsiniz.
Bildirimsel eşlemeden dönüştürücü nasıl yazılır?
Bildirimsel dönüştürücü, eşleme tablosunu koda gömmek yerine bir Python sözlüğü olarak okur ve her satırı bu sözlüğe göre dönüştürür. Kural değiştiğinde kod değil sözlük değişir; kuralı ihlal eden satırlar akışı durdurmaz, satır numarası ve hata mesajıyla ayrı bir dosyaya yazılır. Böylece kabul edilen ve reddedilen kayıtların toplamı her zaman kaynaktaki satır sayısına eşit olur.
import csv
import json
from datetime import datetime
# Bildirimsel esleme: hedef alan -> {kaynak alan, donusum, zorunlu mu}
ESLEME = {
'musteri_id': {'kaynak': 'CUST_NO', 'donustur': int},
'ad_soyad': {'kaynak': 'NAME', 'donustur': lambda v: v.strip().title()},
'eposta': {'kaynak': 'EMAIL', 'donustur': lambda v: v.strip().lower(), 'zorunlu': False},
'kayit_tarihi': {'kaynak': 'REG_DATE', 'donustur': lambda v: datetime.strptime(v, '%d.%m.%Y').date().isoformat()},
'bakiye': {'kaynak': 'BALANCE', 'donustur': lambda v: round(float(v.replace('.', '').replace(',', '.')), 2)},
}
def satir_donustur(satir, esleme):
hedef = {}
for hedef_alan, kural in esleme.items():
ham = (satir.get(kural['kaynak']) or '').strip()
if ham == '':
if kural.get('zorunlu', True):
raise ValueError(f'{hedef_alan}: kaynak alan {kural["kaynak"]} bos')
hedef[hedef_alan] = None
continue
try:
hedef[hedef_alan] = kural['donustur'](ham)
except (ValueError, TypeError) as hata:
raise ValueError(f'{hedef_alan}: {ham!r} donusturulemedi ({hata})')
return hedef
def goc_et(kaynak_yolu, hedef_yolu, red_yolu, esleme):
kabul = red = 0
with open(kaynak_yolu, newline='', encoding='utf-8') as kaynak:
with open(hedef_yolu, 'w', newline='', encoding='utf-8') as hedef:
with open(red_yolu, 'w', encoding='utf-8') as reddedilen:
yazici = csv.DictWriter(hedef, fieldnames=list(esleme))
yazici.writeheader()
for satir_no, satir in enumerate(csv.DictReader(kaynak), start=2):
try:
yazici.writerow(satir_donustur(satir, esleme))
kabul += 1
except ValueError as hata:
kayit = {'satir': satir_no, 'hata': str(hata), 'ham': satir}
print(json.dumps(kayit, ensure_ascii=False), file=reddedilen)
red += 1
return kabul, red
if __name__ == '__main__':
kabul, red = goc_et('eski_musteriler.csv', 'yeni_musteriler.csv', 'reddedilen.jsonl', ESLEME)
print(f'kabul={kabul} red={red}')
Reddedilen dosyası JSON Lines biçimindedir ve her satırda üç şey taşır: kaynaktaki satır numarası, hata mesajı ve ham kayıt. Örnek veride "ABC" değerli bir müşteri numarası int() çevriminde, "31.02.2020" tarihi ise strptime aşamasında düşer; ikisi de hata metniyle birlikte dosyaya yazılır. Kabul artı red toplamı kaynak satır sayısına eşit değilse dönüştürücü bir yerde satır yutuyor demektir; bu eşitlik ilk kontrol edilecek şeydir.
Sözlükteki lambda'lar büyüyünce adlandırılmış fonksiyonlara, kod tablosu eşlemeleri ise ayrı bir sözlüğe taşınır; kalıp değişmez. Dosya satır satır aktığı için bellek büyüklüğü sorun olmaz. Hedef bir veritabanıysa writerow yerine toplu executemany çağrısı gelir; kimlik haritası da bu aşamada, hedefin döndürdüğü yeni anahtarla birlikte yazılır.
Prova koşumu ve kesim anı nasıl planlanır?
Prova koşumu (dry-run), göç betiğinin üretim verisinin güncel bir kopyası üzerinde, gerçek hedefe yazmadan veya atılabilir bir hedefe yazarak baştan sona çalıştırılmasıdır. Kesim anı (cutover) ise kaynak sistemin yazmaya kapatıldığı, son artımlı göçün yapıldığı ve hedefin canlıya alındığı zaman penceresidir. Prova, kesim anında karşılaşılacak süreyi ve hata sınıflarını önceden görmenin tek yoludur.
Provanın işe yaraması için birkaç koşul vardır:
- Tam veri üzerinde koşulur; örneklem yanıltır, çünkü bozuk kayıtlar dağılımın kuyruğunda durur.
- Süre ölçülür; kesim penceresi bu süreye göre, payla belirlenir.
- Reddedilen dosyası hata sınıfına göre gruplanır ve her sınıf için karar yazılır: kuralı düzelt, kaynakta temizle veya bilinçli olarak dışarıda bırak.
- Mutabakat betiği provada da çalıştırılır; kabul kriteri (hangi ölçü eşit olmak zorunda) kesimden önce yazılı hâle gelir.
- Prova en az iki kez yapılır; ikinci koşumda yeni hata sınıfı çıkmıyorsa kesime hazır sayılır.
Kesim anı planı dört adımdan oluşur: kaynağı salt okunur yapmak, son provadan bu yana değişen kayıtları artımlı olarak taşımak (değişiklik zamanı sütunu veya değişiklik günlüğü gerekir), mutabakatı koşup kabul kriterine göre "devam" ya da "geri dön" kararını tek bir sorumluya verdirmek ve hedefi açmak. Kaynak sistem bu noktada kapatılmaz; geri alma süresi dolana kadar salt okunur bekletilir.
Göç sonrası mutabakat nasıl yapılır?
Göç sonrası mutabakat, dönüştürücünün ürettiği çıktı ile hedef sistemden geri okunan verinin eşit olduğunu dört bağımsız ölçüyle kanıtlamaktır: satır sayısı, kimlik kümesi farkı, tutar toplamları ve kayıt bazlı kontrol toplamı (checksum). Tek ölçü yetmez; satır sayısı eşitken tutar farklı olabilir, tutar eşitken iki kaydın alanları yer değiştirmiş olabilir. Rastgele örneklem karşılaştırması ise farkın hangi alanda olduğunu gösterir.
import csv
import hashlib
import random
from decimal import Decimal
def oku(yol, kimlik):
with open(yol, newline='', encoding='utf-8') as dosya:
return {satir[kimlik]: satir for satir in csv.DictReader(dosya)}
def kontrol_toplami(kayitlar, alanlar):
ozet = hashlib.sha256()
for anahtar in sorted(kayitlar):
parca = '|'.join(kayitlar[anahtar][alan].strip() for alan in alanlar)
ozet.update(parca.encode('utf-8'))
return ozet.hexdigest()
def mutabakat(cikti_yolu, hedef_yolu, kimlik, tutar_alani, alanlar, orneklem=50, tohum=42):
cikti = oku(cikti_yolu, kimlik)
hedef = oku(hedef_yolu, kimlik)
rapor = {
'satir_cikti': len(cikti),
'satir_hedef': len(hedef),
'eksik_kimlik': sorted(set(cikti) - set(hedef))[:20],
'fazla_kimlik': sorted(set(hedef) - set(cikti))[:20],
'tutar_cikti': sum(Decimal(s[tutar_alani]) for s in cikti.values()),
'tutar_hedef': sum(Decimal(s[tutar_alani]) for s in hedef.values()),
'checksum_esit': kontrol_toplami(cikti, alanlar) == kontrol_toplami(hedef, alanlar),
}
ortak = sorted(set(cikti) & set(hedef))
random.seed(tohum)
farklar = []
for anahtar in random.sample(ortak, min(orneklem, len(ortak))):
for alan in alanlar:
if cikti[anahtar][alan].strip() != hedef[anahtar][alan].strip():
farklar.append((anahtar, alan, cikti[anahtar][alan], hedef[anahtar][alan]))
rapor['orneklem_fark'] = farklar
rapor['basarili'] = (
rapor['satir_cikti'] == rapor['satir_hedef']
and not rapor['eksik_kimlik'] and not rapor['fazla_kimlik']
and rapor['tutar_cikti'] == rapor['tutar_hedef']
and rapor['checksum_esit'] and not farklar
)
return rapor
if __name__ == '__main__':
sonuc = mutabakat(
'yeni_musteriler.csv', 'hedef_disa_aktarim.csv',
kimlik='musteri_id', tutar_alani='bakiye',
alanlar=['musteri_id', 'ad_soyad', 'eposta', 'kayit_tarihi', 'bakiye'],
)
for anahtar, deger in sonuc.items():
print(f'{anahtar:15} {deger}')
Betik iki dosya karşılaştırır: dönüştürücünün çıktısı ve hedef sistemden aynı sütunlarla geri çekilen dışa aktarım. Kontrol toplamı kayıtları kimliğe göre sıralayıp alanları sabit sırada birleştirir; böylece hedef sistemin satır sırasını değiştirmesi sonucu etkilemez, tek bir karakter farkı ise etkiler. Tutar toplamı float yerine Decimal ile alınır, çünkü ikili kayan nokta toplamı büyük dosyalarda kuruş düzeyinde sapabilir ve mutabakatı yanlış negatif verir.
Örneklem sabit tohumla çekilir; aynı komut ertesi gün de aynı kayıtları karşılaştırır, bu da bulgunun tekrar üretilebilir olmasını sağlar. Raporun basarili alanı kesim anındaki "devam / geri dön" kararının teknik girdisidir. Sık rastlanan yanlış alarm, hedef sistemin tarihleri saat dilimi ekiyle veya tutarları farklı ondalık basamakla döndürmesidir; bu durumda karşılaştırmadan önce iki tarafı aynı biçime getiren bir normalizasyon adımı eklenir, ölçü gevşetilmez. Mutabakat göçün anlık fotoğrafıdır; hedef sistemde kalitenin zamanla nasıl seyrettiğini izlemek için altı boyutlu veri kalitesi skor kartı aynı ölçüleri düzenli koşuma bağlar.
Geri alma planı ve KVKK sınırları nelerdir?
Geri alma planı, kesim anından sonra hedef sistemin kabul kriterini karşılamadığı anlaşılırsa kaynak sisteme belirli bir süre içinde ve veri kaybı olmadan dönülmesini sağlayan yazılı adımlardır. Plan göçten önce yazılır ve provada en az bir kez uygulanır; uygulanmamış geri alma planı plan değil niyettir. KVKK boyutu ise göç sırasında oluşan kişisel veri kopyalarının nerede, ne kadar süre durduğunu ve kimin eriştiğini kapsar.
Geri alma planında şu maddeler yazılı olur:
- Kesim öncesi tam kaynak yedeği ve o yedeğin gerçekten geri yüklendiğinin testi; açılmayan yedek geri alma seçeneğini ortadan kaldırır.
- Geri dönüş tetikleyicisi (hangi mutabakat ölçüsü başarısızsa) ve geri dönüşün mümkün olduğu süre.
- Kesimden sonra hedefe girilen yeni kayıtların kaynağa nasıl taşınacağı; kimlik haritası burada ters yönde kullanılır.
- Kararı kim verir, kim uygular, kullanıcılara kim haber verir.
Kişisel veri açısından göç, aynı verinin birkaç kopyasının geçici olarak var olması demektir ve her kopya için hesap verilmesi gerekir:
- Prova ortamına gerçek kişisel veri taşınmaz; maskelenmiş veya sentetik veri kullanılır. Gerçek veri zorunluysa prova ortamı üretimle aynı erişim kontrolüne tabi tutulur.
- Ara dosyalar (
yeni_musteriler.csv,reddedilen.jsonl) kişisel veri içerir; şifreli diskte tutulur, e-postayla veya sohbet uygulamasıyla paylaşılmaz, göç kapanınca silinir ve silme kaydı tutulur. - Reddedilen dosyası ham kaydı olduğu gibi taşıdığı için en hassas ara üründür; inceleme bittiğinde ilk silinecek dosyadır.
- Kaynak sistemin kapatılması ve yedeklerinin imhası saklama süresine göre planlanır; imha tutanağa bağlanır.
- Hedef sistem yurt dışındaki bir bulut hizmetiyse göç bir yurt dışı aktarımdır; aydınlatma metni ve hukuki dayanak kesimden önce hazır olmalıdır.
Sonuç olarak veri göçü, "kaynaktaki N satırın tamamı ya hedefte ya da gerekçesiyle reddedilenler dosyasında; dört mutabakat ölçüsü eşit; geri dönüş yolu test edildi" cümlesi kurulabildiğinde bitmiş sayılır. Eşleme tablosu kararları görünür kılar, bildirimsel dönüştürücü kuralları koddan ayırır, prova süreyi ve hata sınıflarını önceden gösterir, mutabakat sonucu kanıtlar, geri alma planı ise yanlış çıkarsa kayıpsız dönüşü sağlar.
Yazıdaki iki betik küçük bir CSV üzerinde olduğu gibi çalışır; kendi göçünüzde eşleme sözlüğünü ve alan listesini değiştirmeniz yeterlidir. Otomasyon ve veri işlerinde neler yaptığımı ana sayfada bulabilirsiniz; planladığınız bir veri göçünde eşleme tablosundan mutabakata kadar birlikte çalışmak isterseniz iletişim bölümünden yazabilirsiniz.