---
title: 'Toplu İşlemler'
source: 'https://academia.sh/tr/kurslar/veri-erisim-katmani/toplu-islemler'
course: 'Veri Erişim Katmanı ve İş Mantığı'
language: tr
updated: '2026-08-17T18:06:50+00:00'
license: 'CC BY-SA 4.0'
---

# Toplu İşlemler

Yazma yolunun ölçeklenmesi: aynı iki bin satırın dört yoldan yazılması ve sürelerin karşılaştırılması, işlem sınırının belirleyici payı, parti büyüklüğünün deyim sayısı ve sorgu uzunluğuyla ilişkisi, toplu güncellemede deyim sayısının ölçülmesi.

Önceki iki ders okuma yolunu ölçtü: kaç sorgu çalışıyor ve her sorgu ne kadar veri taşıyor.
Yazma yolunun kendi ölçeği vardır.

Ödünç verilerinin başka bir sistemden aktarıldığını düşünelim: iki bin kayıt yazılacak. Bu
iş tek tek de yapılabilir, tek bir işlemde de, tek bir deyimde de. Üç yol da aynı satırları
üretir. Bu ders aralarındaki farkı ölçer ve farkın nereden geldiğini gösterir.

## Dört Yol

Ölçüm her yol için veritabanını sıfırdan kurar; böylece hiçbir yol öncekinin ısıttığı
önbellekten yararlanmaz.

```js
// toplu-ekleme.mjs — ayni 2000 satir dort yoldan yazilir, sureler olculur
import { DatabaseSync } from "node:sqlite";
import { rmSync } from "node:fs";

const ADET = 2000;
const veri = Array.from({ length: ADET }, (_, i) => [
  (i % 200) + 1, (i % 60) + 1, `2025-${String((i % 12) + 1).padStart(2, "0")}-01`,
]);

function yeniVeritabani() {
  rmSync("aktarim.db", { force: true });
  const db = new DatabaseSync("aktarim.db");
  db.exec(`CREATE TABLE odunc (odunc_id INTEGER PRIMARY KEY, kitap_id INTEGER NOT NULL,
           uye_id INTEGER NOT NULL, alis_tarihi TEXT NOT NULL)`);
  return db;
}

function olc(ad, yaz) {
  const db = yeniVeritabani();
  const t = performance.now();
  yaz(db);
  const sure = performance.now() - t;
  const n = db.prepare("SELECT count(*) AS n FROM odunc").get().n;
  db.close();
  console.log(`${ad.padEnd(38)} satir=${n}  sure=${sure.toFixed(0).padStart(6)} ms  ` +
    `satir_basi=${(sure * 1000 / n).toFixed(1).padStart(6)} us`);
  return sure;
}

const a = olc("her satir ayri islem", (db) => {
  for (const s of veri) {
    db.prepare("INSERT INTO odunc (kitap_id, uye_id, alis_tarihi) VALUES (?,?,?)").run(...s);
  }
});

const b = olc("tek islem, her satir hazirlanir", (db) => {
  db.exec("BEGIN");
  for (const s of veri) {
    db.prepare("INSERT INTO odunc (kitap_id, uye_id, alis_tarihi) VALUES (?,?,?)").run(...s);
  }
  db.exec("COMMIT");
});

const c = olc("tek islem, deyim yeniden kullanilir", (db) => {
  db.exec("BEGIN");
  const ekle = db.prepare("INSERT INTO odunc (kitap_id, uye_id, alis_tarihi) VALUES (?,?,?)");
  for (const s of veri) ekle.run(...s);
  db.exec("COMMIT");
});

const d = olc("tek islem, 500'luk cok satirli deyim", (db) => {
  db.exec("BEGIN");
  for (let i = 0; i < veri.length; i += 500) {
    const parti = veri.slice(i, i + 500);
    const yerler = parti.map(() => "(?,?,?)").join(",");
    db.prepare(`INSERT INTO odunc (kitap_id, uye_id, alis_tarihi) VALUES ${yerler}`)
      .run(...parti.flat());
  }
  db.exec("COMMIT");
});

console.log(`\noranlar (ilk yaklasima gore): ${(a/b).toFixed(1)}x  ${(a/c).toFixed(1)}x  ${(a/d).toFixed(1)}x`);
```

```sh
node toplu-ekleme.mjs
```

```
her satir ayri islem                   satir=2000  sure=   376 ms  satir_basi= 188.2 us
tek islem, her satir hazirlanir        satir=2000  sure=     3 ms  satir_basi=   1.5 us
tek islem, deyim yeniden kullanilir    satir=2000  sure=     1 ms  satir_basi=   0.4 us
tek islem, 500'luk cok satirli deyim   satir=2000  sure=     1 ms  satir_basi=   0.7 us

oranlar (ilk yaklasima gore): 127.9x  470.4x  276.9x
```

Süreler donanıma ve dosya sistemine bağlıdır; oranlar yapıyı gösterir.

En büyük sıçrama ilk iki satır arasında. Aradaki tek fark işlem sınırıdır. İlk yaklaşımda
her `INSERT` kendi işlemini açıp kapatır; kapanış her seferinde kalıcılık gerektirir, yani
verinin diske indiğinin garanti edilmesi beklenir. İki bin kesinleştirme, iki bin
dayanıklılık noktası demektir. İkinci yaklaşımda tek bir kesinleştirme var.

**Toplu yazmada birinci karar deyim biçimi değil, işlem sınırıdır.** SQL Temelleri kursunda
toplu ekleme, çok satırlı `INSERT` deyimi olarak tanıtılmıştı; ölçüm, asıl kazancın orada
olmadığını gösteriyor.

İkinci ve üçüncü satır arasındaki fark deyim hazırlamadan geliyor. Her satır için deyimi
yeniden hazırlamak, aynı metni iki bin kez ayrıştırmak demektir. Hazır deyimi bir kez kurup
yeniden kullanmak bu işi bir kereye indiriyor.

Dördüncü satır, yerel ölçümde beklenenden yavaş. Çok satırlı deyim her parti için farklı
uzunlukta bir SQL metni üretir, dolayısıyla her parti yeniden ayrıştırılır; yerel bir
veritabanında bu, hazır deyimi yeniden kullanmaktan pahalıdır. Çok satırlı deyimin kazancı
başka yerdedir: **deyim sayısını düşürür**, dolayısıyla ağ üzerindeki gidiş-dönüş sayısını
düşürür. Yerel ölçüm bu kazancı göstermez.

## Parti Büyüklüğü

Çok satırlı deyimde seçilecek tek sayı parti büyüklüğüdür.

```js
// parti-boyu.mjs — cok satirli deyimde parti buyuklugunun etkisi
import { DatabaseSync } from "node:sqlite";
import { rmSync } from "node:fs";

const ADET = 2000;
const veri = Array.from({ length: ADET }, (_, i) => [
  (i % 200) + 1, (i % 60) + 1, `2025-${String((i % 12) + 1).padStart(2, "0")}-01`,
]);

for (const parti of [1, 10, 50, 200, 1000]) {
  rmSync("aktarim.db", { force: true });
  const db = new DatabaseSync("aktarim.db");
  db.exec(`CREATE TABLE odunc (odunc_id INTEGER PRIMARY KEY, kitap_id INTEGER NOT NULL,
           uye_id INTEGER NOT NULL, alis_tarihi TEXT NOT NULL)`);
  const t = performance.now();
  db.exec("BEGIN");
  let deyim = 0, enUzun = 0;
  for (let i = 0; i < veri.length; i += parti) {
    const dilim = veri.slice(i, i + parti);
    const sql = `INSERT INTO odunc (kitap_id, uye_id, alis_tarihi) VALUES ${
      dilim.map(() => "(?,?,?)").join(",")}`;
    enUzun = Math.max(enUzun, sql.length);
    db.prepare(sql).run(...dilim.flat());
    deyim += 1;
  }
  db.exec("COMMIT");
  const sure = performance.now() - t;
  console.log(`parti=${String(parti).padStart(4)}  deyim=${String(deyim).padStart(4)}  ` +
    `en_uzun_sql=${String(enUzun).padStart(6)} karakter  sure=${sure.toFixed(1).padStart(6)} ms`);
  db.close();
}
```

```sh
node parti-boyu.mjs
```

```
parti=   1  deyim=2000  en_uzun_sql=    64 karakter  sure=   4.1 ms
parti=  10  deyim= 200  en_uzun_sql=   136 karakter  sure=   1.4 ms
parti=  50  deyim=  40  en_uzun_sql=   456 karakter  sure=   1.1 ms
parti= 200  deyim=  10  en_uzun_sql=  1656 karakter  sure=   1.0 ms
parti=1000  deyim=   2  en_uzun_sql=  8056 karakter  sure=   0.9 ms
```

Kazancın büyük kısmı ilk adımlarda alınıyor: birden ona geçmek süreyi üçte bire indiriyor,
elliden bine geçmek neredeyse hiçbir şey kazandırmıyor. Buna karşılık sorgu metni
doğrusal büyüyor ve bin satırlık partide sekiz kilobayta yaklaşıyor.

Parti büyüklüğünü sınırlayan üç etken vardır. Bağlı değişken sayısının motor tarafından
konan bir üst sınırı bulunur; parti başına sütun sayısıyla çarpılan değişken sayısı bu
sınırı aşamaz. Uzun sorgu metni ayrıştırma maliyetini ve bellek kullanımını artırır. Ve
büyük parti, hata durumunda geri alınacak işin de büyümesi demektir.

Bu yüzden parti, "olabildiğince büyük" değil, kazancın düzleştiği noktada seçilir. Ölçümde
bu nokta elli ile iki yüz arasındadır; başka bir şemada başka bir yerde olur, ama eğrinin
biçimi aynıdır.

## Toplu Güncelleme

Güncellemede her satıra farklı bir değer yazmak gerekir; bu, tek bir `UPDATE` deyimiyle
yapılabilir ama nasıl yapılacağı seçim ister.

```js
// toplu-guncelleme.mjs — 1000 satira ayri ayri deger yazmanin uc yolu
import { DatabaseSync } from "node:sqlite";
import { rmSync } from "node:fs";

const ADET = 1000;
const guncellemeler = Array.from({ length: ADET }, (_, i) => [
  i + 1, `2025-${String((i % 12) + 1).padStart(2, "0")}-15`,
]);

function hazirla() {
  rmSync("aktarim.db", { force: true });
  const db = new DatabaseSync("aktarim.db");
  db.exec(`CREATE TABLE odunc (odunc_id INTEGER PRIMARY KEY, kitap_id INTEGER NOT NULL,
           uye_id INTEGER NOT NULL, alis_tarihi TEXT NOT NULL, iade_tarihi TEXT)`);
  db.exec("BEGIN");
  const ekle = db.prepare("INSERT INTO odunc VALUES (?,?,?,?,NULL)");
  for (let i = 1; i <= ADET; i++) ekle.run(i, (i % 200) + 1, (i % 60) + 1, "2025-01-01");
  db.exec("COMMIT");
  return db;
}

function olc(ad, guncelle) {
  const db = hazirla();
  const sayac = { deyim: 0 };
  const t = performance.now();
  guncelle(db, sayac);
  const sure = performance.now() - t;
  const n = db.prepare("SELECT count(*) AS n FROM odunc WHERE iade_tarihi IS NOT NULL").get().n;
  const ornek = db.prepare("SELECT iade_tarihi FROM odunc WHERE odunc_id = 7").get().iade_tarihi;
  db.close();
  console.log(`${ad.padEnd(30)} guncellenen=${n}  ornek(7)=${ornek}  ` +
    `deyim=${String(sayac.deyim).padStart(4)}  sure=${sure.toFixed(1).padStart(6)} ms`);
  return sure;
}

const a = olc("tek tek guncelleme", (db, sayac) => {
  db.exec("BEGIN");
  const g = db.prepare("UPDATE odunc SET iade_tarihi = ? WHERE odunc_id = ?");
  for (const [id, tarih] of guncellemeler) { g.run(tarih, id); sayac.deyim += 1; }
  db.exec("COMMIT");
});

const b = olc("deger listesinden guncelleme", (db, sayac) => {
  db.exec("BEGIN");
  const yerler = guncellemeler.map(() => "(?,?)").join(",");
  db.prepare(`UPDATE odunc SET iade_tarihi = v.tarih
              FROM (SELECT column1 AS id, column2 AS tarih FROM (VALUES ${yerler})) AS v
              WHERE odunc.odunc_id = v.id`).run(...guncellemeler.flat());
  sayac.deyim += 1;
  db.exec("COMMIT");
});

const c = olc("hazirlik tablosu uzerinden", (db, sayac) => {
  db.exec("BEGIN");
  db.exec("CREATE TEMP TABLE hazirlik (id INTEGER PRIMARY KEY, tarih TEXT NOT NULL)");
  const ekle = db.prepare("INSERT INTO hazirlik VALUES (?,?)");
  for (const [id, tarih] of guncellemeler) { ekle.run(id, tarih); sayac.deyim += 1; }
  db.exec(`UPDATE odunc SET iade_tarihi = (SELECT tarih FROM hazirlik WHERE id = odunc.odunc_id)
           WHERE odunc_id IN (SELECT id FROM hazirlik)`);
  sayac.deyim += 1;
  db.exec("DROP TABLE hazirlik");
  db.exec("COMMIT");
});

console.log(`\noranlar (tek tek guncellemeye gore): ${(a/b).toFixed(2)}x  ${(a/c).toFixed(2)}x`);
```

```sh
node toplu-guncelleme.mjs
```

```
tek tek guncelleme             guncellenen=1000  ornek(7)=2025-07-15  deyim=1000  sure=   0.9 ms
deger listesinden guncelleme   guncellenen=1000  ornek(7)=2025-07-15  deyim=   1  sure=   1.0 ms
hazirlik tablosu uzerinden     guncellenen=1000  ornek(7)=2025-07-15  deyim=1001  sure=   1.0 ms

oranlar (tek tek guncellemeye gore): 0.91x  0.98x
```

Üç yolun süresi yerel ölçümde aynı. Bu, olumsuz bir sonuç değil, bir sonuçtur: aynı işlem
sınırının içinde kaldıkları sürece deyim sayısı yerel veritabanında maliyeti belirlemiyor.
Ayrım deyim sütununda: bin, bir ve bin bir. Veritabanı ağın ötesindeyse bu sayı doğrudan
gidiş-dönüş sayısıdır ve o zaman aradaki fark bine katlanır.

Üçüncü yolun deyim sayısı en yüksek olduğu hâlde ayrı bir değeri vardır. **Hazırlık
tablosu** verilerin önce ayrı bir bağıntıya yüklenmesini, orada doğrulanmasını ve ancak
sonra hedefe uygulanmasını sağlar. Hedef bağıntıya tek bir deyimle dokunulur; hatalı satır
hedefe hiç ulaşmaz. Hazırlık tablosunun yüklenmesi de kendi içinde bir toplu ekleme
işidir ve bu dersin ilk ölçümündeki kurallara uyar.

## Özet

- Aynı iki bin satır dört yoldan yazıldı: her satır ayrı işlemde 376 ms, tek işlemde 3 ms,
  hazır deyim yeniden kullanılarak 1 ms sürdü.
- En büyük kazanç işlem sınırından geldi; iki bin kesinleştirme yerine bir kesinleştirme
  yapmak süreyi yüz yirmi kattan fazla düşürdü.
- Parti büyüklüğünde kazanç erken düzleşiyor: 1'den 10'a geçmek süreyi üçte bire indirdi,
  50'den 1000'e geçmek ölçülebilir bir katkı vermedi; sorgu metni ise doğrusal büyüdü.
- Toplu güncellemede üç yolun yerel süresi eşit çıktı; ayrım deyim sayısındadır (1000, 1,
  1001) ve ağ üzerinden bu sayı gidiş-dönüş sayısına dönüşür.
- Hazırlık tablosu, veriyi hedefe uygulamadan önce doğrulama olanağı verir ve hedefe tek
  deyimle dokunur.

## Sonraki Adım

Buraya kadar ölçülen her şey uygulamanın kendi yaptığı işti: kaç sorgu çalıştı, kaç bayt
taşındı, kaç deyim gönderildi. Bir sorgu bu ölçütlerin hepsini geçip yine de yavaş olabilir.
Tek bir sorgu, tek bir işlemde, yalnız gereken sütunlarla çalışıp saniyeler sürebilir; çünkü
veritabanı o sorguyu nasıl yürüteceğine kendi karar verir. Sonraki ders o kararı uygulamanın
içinden okur, yavaş sorguyu kütüğe düşürür ve planın neden değiştiğini gösterir.
