← Назад к вопросам

В каких случаях стоит применять транзакцию?

2.3 Middle🔥 161 комментариев
#Базы данных и SQL

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI26 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Transakcije u bazi podataka - Kada ih koristiti

Transakcija je sekvenca SQL operacija koja se izvrsava atomski - sve se izvrse ili se sve poniste. ACID principi garantuju konzistentnost podataka.

Sta su transakcije?

Transakcija je logicka jedinica rada koja:

  • Atomska - sve ili nista
  • Konzistentna - baza ide iz jednog stabilnog stanja u drugo
  • Izolirana - ne vidljive druge transakcije dok se ne kompleta
  • Trajno - kad je commit, podaci ostaju trajno

Primjer bez transakcije (LOSE)

class TransferMoney {
    public function transfer(int $fromUserId, int $toUserId, float $amount): bool {
        // Prvo: oduzmi novac od prvog korisnika
        $result1 = $this->db->query(
            "UPDATE users SET balance = balance - ? WHERE id = ?",
            [$amount, $fromUserId]
        );
        
        if (!$result1) return false;
        
        // Sada je novac oduzan ali...
        
        // Drugo: dodaj drugom korisniku
        // AKO OVO FALI, NOVAC JE IZGUBLJEN!
        $result2 = $this->db->query(
            "UPDATE users SET balance = balance + ? WHERE id = ?",
            [$amount, $toUserId]
        );
        
        // Ako drugi query padne, prvi je vec izvrshen
        return $result2;
    }
}

Resenje sa transakcijom (DOBRO)

class TransferMoney {
    public function transfer(int $fromUserId, int $toUserId, float $amount): bool {
        try {
            $this->db->beginTransaction();
            
            // Atomska operacija - ili sve ili nista
            $this->db->query(
                "UPDATE users SET balance = balance - ? WHERE id = ?",
                [$amount, $fromUserId]
            );
            
            $this->db->query(
                "UPDATE users SET balance = balance + ? WHERE id = ?",
                [$amount, $toUserId]
            );
            
            $this->db->commit();
            return true;
        } catch (Exception $e) {
            $this->db->rollBack();
            return false;
        }
    }
}

Slucajevi kada OBAVEZNO trebate transakcije

1. Novčane operacije

Transfer sredstava - ako se sredstva oduzmu ali se ne dodaju drugoj strani, klijent izgubi novac.

function transferFunds(Account $from, Account $to, Money $amount): void {
    $this->db->beginTransaction();
    try {
        $from->withdraw($amount);
        $to->deposit($amount);
        $this->db->commit();
    } catch (Exception $e) {
        $this->db->rollBack();
        throw $e;
    }
}

2. Vise povezanih redaka

Kada promenite vise redaka koji moraju biti konzistentni:

function createOrder(User $user, array $items): Order {
    $this->db->beginTransaction();
    try {
        // Kreiraj red u orders tabeli
        $order = Order::create(['user_id' => $user->id]);
        
        // Dodaj sve stavke
        foreach ($items as $item) {
            OrderItem::create([
                'order_id' => $order->id,
                'product_id' => $item['product_id'],
                'quantity' => $item['quantity']
            ]);
        }
        
        // Oduzmi proizvode iz skladista
        foreach ($items as $item) {
            Stock::query()
                ->where('product_id', $item['product_id'])
                ->decrement('quantity', $item['quantity']);
        }
        
        $this->db->commit();
        return $order;
    } catch (Exception $e) {
        $this->db->rollBack();
        throw $e;
    }
}

3. Brisanje sa foreing key ogranicenjima

function deleteUserWithData(User $user): void {
    $this->db->beginTransaction();
    try {
        // Sve ili nista - komentari se brisu sa korisnikom
        Comment::where('user_id', $user->id)->delete();
        Post::where('user_id', $user->id)->delete();
        $user->delete();
        
        $this->db->commit();
    } catch (Exception $e) {
        $this->db->rollBack();
        throw $e;
    }
}

4. Kompleksne poslovne operacije

function completeSubscription(Subscription $sub): void {
    $this->db->beginTransaction();
    try {
        // Sve ove operacije moraju biti atomske
        $sub->update(['status' => 'active']);
        
        // Kreiraj prvo naplacivanje
        Invoice::create([
            'subscription_id' => $sub->id,
            'amount' => $sub->price
        ]);
        
        // Loguj aktivnost
        ActivityLog::create([
            'user_id' => $sub->user_id,
            'action' => 'subscription_activated'
        ]);
        
        $this->db->commit();
    } catch (Exception $e) {
        $this->db->rollBack();
        throw $e;
    }
}

Slucajevi kada NE trebate transakcije

1. Jednostavni read operacije

// Ne trebate transakcija za citanje
var_dump($user = User::find(1));

2. Izolirana write operacija

// Ako je samo jedan UPDATE i nema zavisnih operacija
$user->update(['last_login' => now()]);

3. Performance-osetljive operacije sa puno redaka

// Brisanje miliona redaka u jednoj transakciji moze zakljucati bazu
// Bolje je batch po 1000 redaka
for ($i = 0; $i < 1000000; $i += 1000) {
    Log::query()->limit(1000)->delete();
}

Best Practices

1. Kratkotrajne transakcije

// LOSE - dugacka transakcija
$this->db->beginTransaction();
sleep(10); // Dugacak proces
$this->db->commit();

// DOBRO - kratka transakcija
$data = $this->expensiveCalculation(); // Van transakcije
$this->db->beginTransaction();
$this->saveData($data);
$this->db->commit();

2. Izbjegavanje deadlockova

// Uvijek zaključaj u istom redoslijedu
$this->db->beginTransaction();
$account1 = Account::query()->where('id', 1)->lockForUpdate()->first();
$account2 = Account::query()->where('id', 2)->lockForUpdate()->first();
$this->db->commit();

3. Ispravna rukovanja greskom

function operation(): void {
    try {
        $this->db->beginTransaction();
        // ... operacije ...
        $this->db->commit();
    } catch (Exception $e) {
        $this->db->rollBack();
        Log::error('Transaction failed', ['error' => $e->getMessage()]);
        throw $e; // Proslijedi dalje ako je potrebno
    }
}

Nivoi izolacije

  • READ UNCOMMITTED - Skazuje sve, cak i neupoćene promene
  • READ COMMITTED - Default, čita samo potvrđene podatke
  • REPEATABLE READ - Ista čitanja daju iste rezultate
  • SERIALIZABLE - Potpuno izolovane, spora
$this->db->query('SET TRANSACTION ISOLATION LEVEL SERIALIZABLE');
$this->db->beginTransaction();

Zakljucak: Transakcije su OBAVEZNE za sve operacije koje zavise jedne od drugih ili gdje konzistentnost podataka nije opcija.

В каких случаях стоит применять транзакцию? | PrepBro