Jeśli obsługujesz MSSQL, MySQL i Postgresa, to w 2025 r. jesteś nie tylko „DBA” (Database Administrator). Jesteś strażakiem, inżynierem SRE (Site Reliability Engineer, to rola wywądząca się z Google), mini-CISO (Chief Information Security Officer) i coraz częściej przewodnikiem po AI. Poniżej masz mój przewodnik, który stworzyłem po reaserchu i bardziej doświadczonych administratorów danych. Co się zmieniło, co robić na co dzień, jak diagnozować problemy SQL, jak ogarnąć AI (wektorowe rozszerzenia), jak spełnić NIS2 i jak wdrożyć DevOps w bazach. Do tego ściąga z komend na koniec. Lecimy.
Więcej o samym SQL pisałem tutaj: https://krystianbieniek.pl/od-excela-do-sql-ninja-w-30-dni/
Co jest „na topie” w 2025
Po pierwsze, PostgreSQL utrzymuje pozycję ulubieńca inżynierów, MySQL i SQL Server wciąż królują w realnych wdrożeniach. Co więcej, SQL Server 2022 dorzucił Intelligent Query Processing z Parameter Sensitive Plan, dzięki czemu rzadziej zmagasz się z klasycznym „parameter sniffing”.
Po drugie, MySQL 8.4 LTS daje stabilną bazę pod produkcję, a przy okazji wprowadza dojrzałe role i EXPLAIN ANALYZE. Jednocześnie PostgreSQL 17 dokłada usprawnienia w EXPLAIN i obsłudze JSON‑ów, więc łatwiej diagnozować realne wąskie gardła.
Co istotne, relacyjne bazy dostały wektory (czyli tablice liczb zmiennoprzecinkowych, np. [0.123, -0.77, 1.05, ...]):
- Postgres z pgvector to oznacza, że możesz trzymać embeddingi tekstów w zwykłej tabeli Postgresa i robić semantyczne wyszukiwanie bez użycia osobnej bazy typu Pinecone/Weaviate.
- MySQL z HeatWave Vector Store – (ich chmurowa wersja) dorzucił natywną obsługę wektorów + pipeline do automatycznego tworzenia embeddingów (np. z PDF, HTML, dokumentów). Masz więc wbudowane: przechowywanie, indeksowanie i zapytania semantyczne, bez integracji zewnętrznych narzędzi.
- Azure SQL z natywnymi typami wektorowymi. Microsoft dodał typ danych vector oraz indeksy k-NN. To daje możliwość budowy RAG (Retrieval-Augmented Generation) wewnątrz tej samej bazy, gdzie trzymasz dane firmowe.
- RAG = Retrieval-Augmented Generation → czyli strategia łączenia LLM (ChatGPT, Claude, itp.) z Twoimi własnymi danymi.
- Krok 1: Konwertujesz dokumenty do embeddingów.
- Krok 2: Trzymasz embeddingi w bazie.
- Krok 3: Gdy użytkownik zada pytanie, tworzysz embedding pytania i szukasz najbliższych sąsiadów (NN search).
- Krok 4: Najbardziej pasujące dokumenty doklejasz do promptu modelu.
- Dzięki wektorom w relacyjnych bazach nie musisz stawiać dodatkowej „wektorowej bazy” (typu Pinecone, Milvus, Weaviate). Wystarczy Twoja MSSQL/MySQL/Postgres w wersji 2023–2025+.
Te wektory to embeddingi, czyli numeryczne reprezentacje tekstu, obrazów czy dźwięków generowane przez modele AI, dzięki którym komputer zaczyna „rozumieć” semantykę (np. zdania „pies biegnie” i „dog runs” są do siebie podobne). W praktyce oznacza to, że nawet klasyczne bazy danych potrafią dziś obsługiwać embeddingi i pozwalają budować wyszukiwanie semantyczne czy RAG w tej samej bazie, w której trzymasz dane biznesowe, bez dokładania kolejnego silnika. A skoro mówimy o praktyce, to na koniec warto dodać, że regulacyjnie rośnie znaczenie NIS2, więc kopie offline/immutable, retencja logów i dowody testów odtworzeniowych stają się „must-have”, a nie tylko „nice-to-have”.
Przeczytaj również: https://krystianbieniek.pl/top-7-systemow-do-pracy-z-sql-gdzie-warto-zaczac/
Fundamenty admina: tożsamości, role i uprawnienia
Przede wszystkim zacznij od modelu bezpieczeństwa. W praktyce oznacza to rozdzielenie logina (tożsamość na poziomie serwera) od usera (tożsamość w konkretnej bazie) oraz świadome użycie ról.
SQL Server (MSSQL)
Po pierwsze, utwórz login i użytkownika, a następnie nadaj uprawnienia roli:
CREATE LOGIN app_login WITH PASSWORD = 'Strong#Passw0rd!';
USE MyDb;
CREATE USER app_user FOR LOGIN app_login;
CREATE ROLE app_role;
ALTER ROLE app_role ADD MEMBER app_user;
GRANT SELECT, INSERT, UPDATE, DELETE ON SCHEMA::dbo TO app_role;
Co więcej, pamiętaj o wbudowanych rolach bazy (db_datareader, db_datawriter) oraz o Query Store, który włączysz per baza i który uprości diagnozę planów:
ALTER DATABASE MyDb SET QUERY_STORE = ON;
MySQL (8.0/8.4 LTS)
Na początek utwórz role i konto użytkownika; następnie przypisz role i ustaw je jako domyślne:
CREATE ROLE 'reader', 'writer';
GRANT SELECT ON mydb.* TO 'reader';
GRANT INSERT, UPDATE, DELETE ON mydb.* TO 'writer';
CREATE USER 'app'@'%' IDENTIFIED BY 'Strong#Passw0rd!';
GRANT 'reader','writer' TO 'app'@'%';
SET DEFAULT ROLE 'reader','writer' TO 'app'@'%';
Dodatkowo korzystaj z EXPLAIN ANALYZE, ponieważ pokazuje realny koszt wykonania zapytań.
PostgreSQL
Najpierw pamiętaj, że „user” to rola z atrybutem LOGIN. Następnie ustaw domyślne przywileje, aby nowe tabele były widoczne dla właściwych ról:
CREATE ROLE app LOGIN PASSWORD 'Strong#Passw0rd!';
GRANT CONNECT ON DATABASE mydb TO app;
GRANT USAGE ON SCHEMA public TO app;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO app;
ALTER DEFAULT PRIVILEGES IN SCHEMA public
GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO app;
W rezultacie nowo tworzone obiekty automatycznie dziedziczą właściwe prawa, co ogranicza „poślizgi” w produkcji.
Backup i odtwarzanie: testuj restore, nie backup
Najpierw wybierz strategię: pełny + różnicowy + log (MSSQL), dump + binlog (MySQL) albo pg_dump/pg_basebackup + WAL (Postgres). Co więcej, dodaj jedną kopię offline/immutable, bo właśnie to pomaga przetrwać ransomware.
SQL Server
Po pierwsze wykonaj pełny backup; następnie różnicowy; wreszcie backup logów, aby uzyskać PITR (Dzięki temu możesz uratować się nie tylko po awarii serwera, ale też po ludzkiej pomyłce (np. DROP TABLE, UPDATE bez WHERE)):
BACKUP DATABASE MyDb TO DISK='D:\bkp\MyDb_full.bak' WITH INIT, CHECKSUM;
BACKUP DATABASE MyDb TO DISK='D:\bkp\MyDb_diff.bak' WITH DIFFERENTIAL, CHECKSUM;
BACKUP LOG MyDb TO DISK='D:\bkp\MyDb_log.trn' WITH CHECKSUM;
Następnie odtwarzaj łańcuchem: full → diff → logi; dopiero na końcu WITH RECOVERY:
RESTORE DATABASE MyDb FROM DISK='D:\bkp\MyDb_full.bak'
WITH MOVE 'MyDb' TO 'D:\data\MyDb.mdf',
MOVE 'MyDb_log' TO 'D:\data\MyDb_log.ldf',
NORECOVERY;
RESTORE DATABASE MyDb FROM DISK='D:\bkp\MyDb_diff.bak' WITH NORECOVERY;
RESTORE LOG MyDb FROM DISK='D:\bkp\MyDb_log.trn' WITH RECOVERY;
MySQL
Najpierw zrób logiczny dump bez blokowania InnoDB; następnie użyj binlogów, aby cofnąć się lub dojść do punktu w czasie:
mysqldump --single-transaction --routines --triggers mydb > mydb.sql
mysql mydb < mydb.sql
mysqlbinlog --start-datetime="2025-08-21 10:00:00" \
--stop-datetime="2025-08-21 12:00:00" \
/var/lib/mysql/binlog.000123 | mysql
Dodatkowo ustaw retencję binlogów, aby dyski nie puchły:
SET PERSIST binlog_expire_logs_seconds = 2592000; -- 30 dni
PostgreSQL
Po pierwsze wybierz rodzaj kopii. Z jednej strony pg_dump -Fc daje przenośność; z drugiej pg_basebackup + archiwizacja WAL zapewniają PITR:
pg_dump -Fc -d mydb -f /backups/mydb.dump
pg_restore -d mydb_restored /backups/mydb.dump
# fizycznie
grep -q archive_mode postgresql.conf && echo "Włączona archiwizacja WAL"
W efekcie masz zarówno szybkie przywrócenia schematu/danych, jak i pełne odtwarzanie do chwili awarii.
Prosty, ale skuteczny maintenance
Przede wszystkim aktualizuj statystyki i dbaj o indeksy. Co więcej, monitoruj logi i regularnie sprawdzaj blokady.
Indeksy
Po pierwsze, w MSSQL używaj REORGANIZE przy umiarkowanej fragmentacji, a REBUILD przy wysokiej; dodatkowo rozważ tryb ONLINE/RESUMABLE. Z kolei w Postgresie pamiętaj o REINDEX CONCURRENTLY, aby ograniczyć przestój. Natomiast w MySQL OPTIMIZE TABLE to de facto przebudowa, dlatego stosuj ją rozważnie.
Statystyki i spójność
Na początku sprawdź spójność i statystyki w MSSQL:
DBCC CHECKDB('MyDb');
EXEC sp_updatestats;
Następnie w MySQL odśwież kardynalności i ewentualnie zoptymalizuj wybrane tabele:
ANALYZE TABLE mydb.orders;
OPTIMIZE TABLE mydb.orders;
Wreszcie w Postgresie po prostu uruchom:
VACUUM (ANALYZE) app.orders;
Logi i „slow queries”
Na początek włącz Slow Query Log w MySQL(mechanizm, który zapisuje zapytania SQL); następnie ustaw próg, aby nie utopić się w szumie:
SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 0.5; -- 500 ms
Co więcej, w Postgresie skonfiguruj pg_stat_statements oraz logowanie zapytań trwających powyżej N ms. Równolegle w MSSQL opieraj się o Query Store i DMV‑ki (sys.dm_exec_requests). W rezultacie masz jasny obraz tego, co naprawdę boli system.
Diagnostyka SQL (DML) – 10 wzorców na co dzień
Po pierwsze, używaj JOIN i GROUP BY bez czarów:
SELECT c.id, c.name, SUM(o.total) AS revenue
FROM customers c
JOIN orders o ON o.customer_id = c.id
GROUP BY c.id, c.name
HAVING SUM(o.total) > 1000
ORDER BY revenue DESC;
Po drugie, dawaj LEFT JOIN tam, gdzie szukasz braków:
SELECT c.id, c.name
FROM customers c
LEFT JOIN orders o ON o.customer_id = c.id
WHERE o.id IS NULL;
Co więcej, stosuj funkcje okna do rankingów i rolling‑sumów:
SELECT customer_id,
SUM(total) AS revenue,
RANK() OVER (ORDER BY SUM(total) DESC) AS rnk
FROM orders
GROUP BY customer_id;
Jednocześnie pamiętaj o różnicach dialektów: TOP w MSSQL kontra LIMIT w MySQL/PG; ILIKE w Postgresie kontra kolacje w MySQL.
AI i wektory w RDBMS, kiedy to ma sens
Na początku upewnij się, że naprawdę potrzebujesz semantycznego wyszukiwania. Jeżeli tak, to rozważ te ścieżki:
- Po pierwsze Postgres + pgvector (HNSW/IVFFlat) — prosta składnia i rozsądna wydajność.
- Po drugie MySQL HeatWave Vector Store — integracje z parsowaniem plików i automatycznymi embeddingami.
- Po trzecie Azure SQL z natywnymi typami wektorowymi — dobra opcja w świecie Microsoft.
Co ważne, zawsze mierz latencję i koszty. W przeciwnym razie możesz włączyć turbodoładowanie, którego Twoja aplikacja nie potrzebuje.
NIS2 co DBA musi mieć „od ręki”
Najpierw przygotuj politykę kopii zapasowych z jedną kopią offline/immutable i protokołami testów odtworzeniowych. Następnie wprowadź zasadę najmniejszych uprawnień oraz regularny przegląd kont i ról. Dodatkowo włącz logowanie zdarzeń (slow log, Query Store, pg_stat_statements) i ustaw retencję.
W rezultacie masz dowody na spełnianie wymogów i co ważniejsze realną odporność na incydenty.
Database DevOps, baza jako kod
Na początek wybierz narzędzie do migracji schematu: Liquibase lub Flyway. Następnie trzymaj changelogi w repozytorium, rób code review i uruchamiaj migracje w pipeline’ach CI/CD.
Co więcej, stosuj zasadę: „baza przed aplikacją”. Dzięki temu unikniesz sytuacji, w której nowy kod oczekuje kolumny, której jeszcze nie ma. W efekcie release’y są przewidywalne, a rollback przetestowany.
Tabele porównawcze
Ponieważ lubisz konkrety, poniżej masz skrótowe tabele z najważniejszymi różnicami.
Porównanie: diagnostyka i plany
| Silnik | Plan wykonania | Telemetria „slow” | Tip na start |
|---|---|---|---|
| MSSQL | SSMS/Actual Plan, Query Store | Query Store, DMVs | Włącz Query Store i raport regresji planów |
| MySQL | EXPLAIN ANALYZE | Slow Query Log | Ustaw long_query_time na 500 ms |
| Postgres | EXPLAIN (ANALYZE, BUFFERS) | pg_stat_statements | Włącz compute_query_id i czytaj BUFFERS |
Porównanie: backup i PITR
| Silnik | Backup logiczny | PITR | Uwaga operacyjna |
|---|---|---|---|
| MSSQL | BACKUP DATABASE | Logi transakcyjne | Testuj łańcuch RESTORE na osobnym serwerze |
| MySQL | mysqldump --single-transaction | mysqlbinlog | Pilnuj retencji binlogów |
| Postgres | pg_dump -Fc | pg_basebackup + WAL | Monitoruj archiwizację i rozmiary WAL |
Mini‑ściąga (do schowka)
Ponieważ ściągi ratują życie na dyżurze, oto skrót najważniejszych komend.
MSSQL — backup/restore
BACKUP DATABASE MyDb TO DISK='D:\bkp\MyDb_full.bak' WITH INIT, CHECKSUM;
BACKUP LOG MyDb TO DISK='D:\bkp\MyDb_log.trn' WITH CHECKSUM;
RESTORE DATABASE MyDb FROM DISK='D:\bkp\MyDb_full.bak' WITH NORECOVERY;
RESTORE LOG MyDb FROM DISK='D:\bkp\MyDb_log.trn' WITH RECOVERY;
MySQL — dump/PITR
mysqldump --single-transaction --routines --triggers mydb > mydb.sql
mysql mydb < mydb.sql
mysqlbinlog --start-datetime="2025-08-21 10:00:00" /var/lib/mysql/binlog.000123 | mysql
Postgres — pg_dump / WAL
pg_dump -Fc -d mydb -f /backups/mydb.dump
pg_restore -d mydb_restored /backups/mydb.dump
# + pg_basebackup + archiwizacja WAL — dla pełnego PITR
Tworzenie loginów/użytkowników/ról
- MSSQL
CREATE LOGIN app_login WITH PASSWORD='Strong#Passw0rd!';
USE MyDb; CREATE USER app_user FOR LOGIN app_login;
CREATE ROLE app_role; ALTER ROLE app_role ADD MEMBER app_user;
GRANT SELECT, INSERT, UPDATE, DELETE ON SCHEMA::dbo TO app_role;
- MySQL
CREATE ROLE 'reader','writer';
GRANT SELECT ON mydb.* TO 'reader';
GRANT INSERT,UPDATE,DELETE ON mydb.* TO 'writer';
CREATE USER 'app'@'%' IDENTIFIED BY 'Strong#Passw0rd!';
GRANT 'reader','writer' TO 'app'@'%';
SET DEFAULT ROLE 'reader','writer' TO 'app'@'%';
- Postgres
CREATE ROLE app LOGIN PASSWORD 'Strong#Passw0rd!';
GRANT CONNECT ON DATABASE mydb TO app;
GRANT USAGE ON SCHEMA public TO app;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO app;
ALTER DEFAULT PRIVILEGES IN SCHEMA public
GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO app;
10 zapytań na co dzień
-- 1. TOP/LIMIT
SELECT TOP (10) * FROM dbo.Orders ORDER BY CreatedAt DESC; -- MSSQL
SELECT * FROM orders ORDER BY created_at DESC LIMIT 10; -- MySQL/PG
-- 2. INNER JOIN
SELECT o.id, c.name, o.total FROM orders o JOIN customers c ON c.id=o.customer_id;
-- 3. LEFT JOIN „braki”
SELECT c.id, c.name FROM customers c LEFT JOIN orders o ON o.customer_id=c.id WHERE o.id IS NULL;
-- 4. SUM + GROUP BY + HAVING
SELECT c.id, SUM(o.total) s FROM customers c JOIN orders o ON o.customer_id=c.id GROUP BY c.id HAVING SUM(o.total)>1000;
-- 5. COUNT DISTINCT
SELECT DATE(order_date) d, COUNT(DISTINCT customer_id) active_customers FROM orders GROUP BY DATE(order_date);
-- 6. Okna
SELECT customer_id, SUM(total) OVER (PARTITION BY customer_id) s FROM orders;
-- 7. CTE
WITH s AS (SELECT * FROM orders WHERE status='COMPLETED') SELECT * FROM s;
-- 8. Daty „ostatnie 30 dni”
-- MSSQL
SELECT * FROM orders WHERE order_date >= DATEADD(day,-30,GETDATE());
-- MySQL
SELECT * FROM orders WHERE order_date >= NOW() - INTERVAL 30 DAY;
-- Postgres
SELECT * FROM orders WHERE order_date >= NOW() - INTERVAL '30 days';
-- 9. Filtrowanie po tekście (case‑insensitive)
SELECT * FROM products WHERE name ILIKE '%desk%'; -- PG
-- 10. EXPLAIN
EXPLAIN ANALYZE SELECT * FROM orders WHERE customer_id=123; -- MySQL
EXPLAIN (ANALYZE, BUFFERS) SELECT * FROM orders WHERE customer_id=123; -- PG
Na zakończenie
Podsumowując: zacznij od porządku w rolach, dołóż Query Store / Slow Log / pg_stat_statements, a następnie zbuduj odporną politykę backupów z jedną kopią offline/immutable. Co więcej, ustandaryzuj migracje schematu w CI/CD i mierz realną korzyść z wektorów. W efekcie Twoja baza będzie szybsza, bezpieczniejsza i gotowa na nowe wyzwania.
Jestem ciekaw co Ciebie boli najbardziej w DBA od kuchni? Daj znać w komentarzu.
Źródła (wybór, do dalszego czytania)
- DevOps: Liquibase (koncepty), Flyway (migrations). documentation.red-gate.com
- Trendy i popularność: Stack Overflow 2025 (bazy), DB-Engines (ranking). survey.stackoverflow.codb-engines.com
- SQL Server: PSP/IQP, Query Store, BACKUP/RESTORE, DBCC CHECKDB. Microsoft Learn+4Microsoft
- MySQL: 8.4 LTS, EXPLAIN ANALYZE, mysqldump/binlog/PITR, role/GRANT. MySQL Developer Zone+4MySQL Developer Zone+4MySQL Developer Zone+4
- PostgreSQL: 17 release notes, pg_dump/pg_basebackup + PITR, EXPLAIN/BUFFERS, pg_stat_statements. PostgreSQL+4PostgreSQL+4PostgreSQL+4
- AI/wektory: pgvector, Azure SQL vectors, MySQL HeatWave GenAI/Vector Store. Microsoft for Developers


