Baza pytań rekrutacyjnych i wiedzy. Filtruj, szukaj i sprawdzaj swoją wiedzę.
Problem N+1 występuje, gdy Hibernate pobiera N encji nadrzędnych jednym zapytaniem, a następnie leniwie dociąga dzieci osobnym zapytaniem dla każdej encji, co daje N dodatkowych selectów. Rozwiązania to fetch joiny/entity graphy, batch fetching lub zmiana strategii pobierania.
ACID opisuje gwarancje transakcji: Atomicity (wszystko albo nic), Consistency (zachowanie niezmienników), Isolation (transakcje współbieżne zachowują się jak wykonywane sekwencyjnie) oraz Durability (zatwierdzone dane przetrwają awarię).
Indeksy B‑tree utrzymują klucze w porządku, więc dobrze wspierają wyszukiwanie równościowe, zakresy, prefiksy i ORDER BY. Indeksy hash mapują klucze do kubełków i są szybkie dla równości, ale nie obsługują zakresów ani sortowania.
Normalizacja zmniejsza redundancję danych. 1NF wymaga atomowych kolumn i braku grup powtarzalnych. 2NF to 1NF oraz zależność atrybutów nie‑kluczowych od całego klucza. 3NF to 2NF bez zależności przechodnich między atrybutami nie‑kluczowymi.
Bazy SQL są relacyjne, mają z góry zdefiniowany schemat, joiny i silne transakcje ACID. Bazy NoSQL są nierelacyjne (dokumentowe, key‑value, kolumnowe, grafowe), zwykle mają elastyczny schemat i łatwiej się skalują horyzontalnie, czasem kosztem ścisłej spójności.
Primary key jednoznacznie identyfikuje wiersz (i może być referencjonowany przez FK). Unique constraint też wymusza unikalność, ale nie musi być „tożsamością” wiersza. Indeks to struktura przyspieszająca odczyty; może być unikalny lub nie.
Transakcja łączy wiele operacji w jedną jednostkę pracy: albo wszystkie się udają, albo następuje rollback. Chroni spójność danych, szczególnie przy współbieżności i awariach.
Indeks przyspiesza odczyty (filtrowanie/sortowanie), bo unika pełnych skanów tabeli. Trade-off to wolniejsze zapisy (INSERT/UPDATE/DELETE) i dodatkowe miejsce, bo indeks trzeba utrzymywać.
INNER JOIN zwraca tylko wiersze pasujące w obu tabelach. LEFT JOIN zwraca wszystkie wiersze z lewej tabeli i uzupełnia brakujące dopasowania z prawej strony NULLami.
Normalizacja zmniejsza redundancję i anomalie aktualizacji przez podział danych na powiązane tabele. Denormalizacja duplikuje część danych, żeby przyspieszyć odczyty i ograniczyć joiny, kosztem trudniejszych zapisów i pilnowania spójności.
Izolacja definiuje, jakie anomalie są dopuszczalne przy współbieżnych transakcjach (dirty/non-repeatable reads, phantoms). Wyższa izolacja daje większą poprawność, ale może zmniejszać współbieżność i wydajność.
To sytuacja, gdy pobierasz N rekordów nadrzędnych i potem odpalasz po jednym dodatkowym zapytaniu na każdy (czyli N zapytań więcej). Unikasz przez joiny, batching, prefetch/eager loading albo zapytania typu `IN (...)`.
Optimistic locking zakłada rzadkie konflikty: aktualizujesz z kontrolą wersji/timestamp i robisz retry przy konflikcie. Pessimistic locking blokuje wiersze z góry (np. `SELECT ... FOR UPDATE`), żeby inni nie mogli ich zmienić.
Replikacja kopiuje te same dane na wiele węzłów (lepsza skala odczytu i dostępność). Sharding dzieli dane między węzłami (lepsza skala zapisu/rozmiaru), ale komplikuje zapytania i transakcje.
Najpierw patrz na węzły z największym kosztem/czasem oraz typ skanu (Seq Scan vs Index Scan) i estymacje wierszy. Potem sprawdź joiny/sortowanie i czy indeksy są używane; poprawiasz zapytanie, indeksy lub statystyki.
Foreign key to constraint łączący kolumnę z kluczem primary/unique w innej tabeli. Wymusza integralność referencyjną: nie możesz wskazać wiersza, który nie istnieje (i może kontrolować zachowanie przy delete/update).
GROUP BY grupuje wiersze do agregacji. HAVING filtruje grupy po agregacji (a WHERE filtruje wiersze przed grupowaniem). Np. „tylko grupy z count > 10”.
Covering index zawiera wszystkie kolumny potrzebne do zapytania, więc baza potrafi odpowiedzieć używając tylko indeksu (bez sięgania do tabeli). Może mocno przyspieszyć odczyty kosztem większych indeksów i wolniejszych zapisów.
Deadlock to sytuacja, gdy transakcje czekają na swoje blokady w cyklu i żadna nie może ruszyć dalej. Ograniczasz przez krótkie transakcje, spójny porządek blokowania i retry przy błędach deadlock.
SQL injection to sytuacja, gdy nieufny input zmienia znaczenie SQL (np. przez konkatenację stringów). Zapobiegasz przez zapytania parametryzowane/prepared statements, poprawne escapowanie przez driver/ORM i konta DB z minimalnymi uprawnieniami.
Indeks złożony indeksuje kilka kolumn razem (np. (org_id, email)). Pomaga, gdy zapytania filtrują/sortują po lewym prefiksie tych kolumn, zmniejszając skany i przyspieszając wyszukiwanie.
Window function liczy wartość na „oknie” wierszy powiązanych z bieżącym wierszem, bez zwijania wierszy jak GROUP BY. Use case: ranking (ROW_NUMBER), sumy narastające, „top N per grupa”.
Constrainty (PK, FK, UNIQUE, CHECK) to deklaratywne reguły egzekwowane przez silnik bazy. Triggery to własny kod uruchamiany przy zdarzeniach. Jeśli się da, preferuj constrainty do integralności, bo są prostsze, przewidywalne i zoptymalizowane.
Użyj unique constraint na idempotency key (albo naturalnym kluczu) i zrób upsert/insert z obsługą konfliktu. Jeśli ten sam request przyjdzie drugi raz, zapis stanie się no-opem albo zaktualizuje ten sam wiersz zamiast tworzyć duplikat.
Soft delete oznacza oznaczenie rekordu jako usunięty (np. `deleted_at`), co pozwala na przywracanie/audyt, ale komplikuje zapytania i indeksy (trzeba filtrować usunięte). Hard delete usuwa dane i upraszcza zapytania, ale tracisz historię, jeśli jej nie archiwizujesz.
`SELECT *` pobiera więcej danych niż potrzeba (więcej I/O i pamięci) i mocno wiąże kod ze zmianami schematu (dodanie kolumny może zmienić wynik, rozmiar payloadu albo ujawnić wrażliwe pola). Wybieranie tylko potrzebnych kolumn jest czytelniejsze i może umożliwić lepsze plany (np. index-only).
NULL oznacza “nieznane”, więc porównania typu `col = NULL` dają UNKNOWN, a nie true/false (logika trójwartościowa). Użyj `IS NULL` / `IS NOT NULL`, a w niektórych bazach `IS DISTINCT FROM`, żeby bezpiecznie porównywać z NULL.
Partycjonowanie dzieli jedną logiczną tabelę na mniejsze części, zwykle w ramach jednego systemu bazy (ułatwia zarządzanie i może przyspieszać zapytania przez pruning). Sharding dzieli dane na wiele instancji/nodów bazy, żeby skalować poziomo. Partycjonowanie jest zwykle prostsze; sharding dokłada złożoność rozproszoną.
Materialized view fizycznie przechowuje wynik zapytania, więc odczyt może być dużo szybszy niż liczenie tego za każdym razem. Ma sens przy drogich agregacjach lub raportowaniu, akceptując trade-off: koszt odświeżania i (często) lekko nieaktualne dane.
Write amplification oznacza, że jeden logiczny zapis powoduje wiele zapisów fizycznych: wiersz w tabeli plus każdy indeks, którego dotyczy zmiana (często także WAL/redo logi). Więcej indeksów zwykle przyspiesza odczyty, ale spowalnia insert/update/delete oraz zwiększa koszt miejsca i utrzymania.
Selektywność to informacja, jak dobrze kolumna filtruje wiersze (ile wierszy pasuje do warunku). Indeks o wysokiej selektywności (mało dopasowań) jest bardziej użyteczny, bo baza omija skanowanie dużej liczby wierszy. Kolumny o niskiej selektywności (np. boolean) często niewiele zyskują z samego indeksu.
Optymalizator wybiera plan na podstawie estymacji liczby wierszy (cardinality). Jeśli estymacje są złe (stare statystyki, nierówny rozkład danych, skorelowane kolumny), może wybrać złą kolejność joinów albo zły algorytm. Aktualne statystyki (np. ANALYZE) i odpowiednie indeksy pomagają mu lepiej estymować.
Wildcard na początku (`%term`) często uniemożliwia użycie zwykłego indeksu B-tree, więc baza może skanować dużo wierszy. Alternatywy to indeksy full-text, trigramy (jeśli baza wspiera) albo zmiana zapytania na wyszukiwanie po prefiksie (`term%`), jeśli to możliwe.
Użyj podejścia expand/contract: dodaj nową kolumnę, zrób backfill partiami, zapisuj do obu (albo utrzymuj spójność), przełącz odczyt na nową kolumnę, a na końcu usuń starą. Dzięki temu unikasz długich blokujących locków i wdrażasz zmianę bezpiecznie.
Długie transakcje mogą długo trzymać locki, blokować inne zapytania i zwiększać contention. W bazach MVCC mogą też blokować sprzątanie starych wersji wierszy, co prowadzi do bloatu. Mogą też zwiększać replication lag i utrudniać recovery po awarii.
Primary key jednoznacznie identyfikuje wiersz i zwykle oznacza NOT NULL oraz jeden główny identyfikator na tabelę. Unique constraint także wymusza unikalność, ale możesz mieć ich wiele i w zależności od bazy mogą dopuszczać NULL.
Klucze obce wymuszają spójność referencyjną (brak sierot) i ułatwiają reasoning. Trade‑off to dodatkowy koszt zapisu i czasem trudniejsze migracje. W bardzo obciążonych systemach część zespołów waliduje relacje w aplikacji zamiast w bazie.
Większość baz używa zasady left‑most prefix. Indeks na (A, B, C) przyspiesza zapytania po A lub A,B, ale nie po samym B. Kolejność dobieraj według typowych filtrów i selektywności.
Covering index zawiera wszystkie kolumny potrzebne do zapytania, więc baza może odpowiedzieć, używając tylko indeksu bez odczytu wierszy z tabeli. To zmniejsza I/O i bywa dużo szybsze na dużych tabelach.
Deadlock występuje, gdy dwie transakcje czekają na swoje wzajemne locki. Baza wykrywa to i przerywa (rollback) jedną transakcję, aby druga mogła ruszyć. Aplikacja powinna ponowić przerwaną transakcję.
Read Committed blokuje brudne odczyty, ale dopuszcza non‑repeatable reads. Repeatable Read gwarantuje, że odczytane wiersze się nie zmienią w trakcie transakcji, ale może dopuścić phantom rows. Serializable jest najsurowszy — zachowuje się jakby transakcje działały po kolei, eliminując phantom, kosztem mniejszej współbieżności.
Partitioning dzieli tabelę na części w obrębie jednej instancji bazy (często dla zarządzania/wydajności). Sharding dzieli dane między wiele serwerów baz danych dla skalowania poziomego. Sharding dodaje złożoność w routingu i zapytaniach między shardami.
Denormalizacja polega na duplikacji danych, aby przyspieszyć odczyt i ograniczyć joiny. Pomaga w read‑heavy workloadach, ale zwiększa storage, ryzyko niespójności oraz komplikuje zapisy i migracje.
OLTP obsługuje wiele małych, krótkich transakcji (workload operacyjny). OLAP wykonuje mniej, ale cięższych zapytań analitycznych na dużych zbiorach (raporty/BI). Różni się projekt schematu, indeksowanie i storage.
W autocommit każdy statement jest osobną transakcją. Transakcje jawne grupują wiele statementów w jedną atomową całość, co ma znaczenie dla spójności i wydajności (mniej commitów/round‑tripów). Używaj jawnych transakcji przy wieloetapowych zmianach, które muszą się udać lub wycofać razem.