Administrator danych w 2025: praktyka MSSQL/MySQL/Postgres + AI

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, ...]):

  1. 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.
  2. 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.
  3. 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.
  4. 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

SilnikPlan wykonaniaTelemetria „slow”Tip na start
MSSQLSSMS/Actual Plan, Query StoreQuery Store, DMVsWłącz Query Store i raport regresji planów
MySQLEXPLAIN ANALYZESlow Query LogUstaw long_query_time na 500 ms
PostgresEXPLAIN (ANALYZE, BUFFERS)pg_stat_statementsWłącz compute_query_id i czytaj BUFFERS

Porównanie: backup i PITR

SilnikBackup logicznyPITRUwaga operacyjna
MSSQLBACKUP DATABASELogi transakcyjneTestuj łańcuch RESTORE na osobnym serwerze
MySQLmysqldump --single-transactionmysqlbinlogPilnuj retencji binlogów
Postgrespg_dump -Fcpg_basebackup + WALMonitoruj 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)

  1. DevOps: Liquibase (koncepty), Flyway (migrations). documentation.red-gate.com
  2. Trendy i popularność: Stack Overflow 2025 (bazy), DB-Engines (ranking). survey.stackoverflow.codb-engines.com
  3. SQL Server: PSP/IQP, Query Store, BACKUP/RESTORE, DBCC CHECKDB. Microsoft Learn+4Microsoft
  4. MySQL: 8.4 LTS, EXPLAIN ANALYZE, mysqldump/binlog/PITR, role/GRANT. MySQL Developer Zone+4MySQL Developer Zone+4MySQL Developer Zone+4
  5. PostgreSQL: 17 release notes, pg_dump/pg_basebackup + PITR, EXPLAIN/BUFFERS, pg_stat_statements. PostgreSQL+4PostgreSQL+4PostgreSQL+4
  6. AI/wektory: pgvector, Azure SQL vectors, MySQL HeatWave GenAI/Vector Store. Microsoft for Developers

Zostaw komentarz

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *

Przewijanie do góry