dataiotech Labs
dbaguidedbaguideDBAdataxtoolsdataopsphere
Home/Operations/Performance
Production Performance Tuning & Profiling

Database Performance Tuning & Profiling

Production-verified memory allocation standards, execution plan analysis, index bloat remediation, slow query log profiling, and anti-pattern guardrails.

Filter by Engine:
Showing Recipes: 23 / 26
PostgreSQLMemory & CacheCritical Impact

shared_buffers: How Much RAM Belongs to PostgreSQL?

PostgreSQL dedicated buffer pool sizing

The primary memory cache where PostgreSQL buffers table and index data pages. Misconfigurations cause double-buffering waste or severe disk I/O thrashing. Dedicated nodes should allocate 25% of total RAM; shared instances must scale down.

Golden Rule & Production Standard

<8 GB RAM → 15–20% · 8–64 GB → 25% · >64 GB → 16 GB ceiling (rely on effective_cache_size for OS caching)

postgresql.conf
# postgresql.conf  
# 32 GB RAM'li dedicated sunucu örneği  
shared_buffers = 8GB  
effective_cache_size = 24GB   # OS cache dahil toplam beklenti
Tags:
PostgreSQLMemory & CacheHigh Impact

work_mem: Per-Operation Sort & Hash Budgeting

Sort, hash joins, and OOM risk control

Defines memory allocated to EACH individual sort, hash-join, and aggregate step. A complex query executes multiple operations simultaneously; multiplied across active client connections, aggressive values cause OOM crashes. Spilled sorts appear as "external merge Disk" in EXPLAIN ANALYZE.

Golden Rule & Production Standard

work_mem ≈ (RAM × 0.25) / max_connections — keep global conservative, elevate dynamically per-session for heavy analytics.

work_mem_tuning.sql
-- Global ayar (ihtiyatlı)  
-- postgresql.conf  
work_mem = 64MB  
  
-- Ağır rapor sorgusu için session bazlı yükseltme  
SET work_mem = '256MB';  
EXPLAIN (ANALYZE, BUFFERS)  
SELECT customer_id, SUM(amount)  
FROM orders  
GROUP BY customer_id  
ORDER BY SUM(amount) DESC;
Tags:
PostgreSQLIndex StrategyHigh Impact

Index Bloat: The Invisible Performance Killer

B-Tree leaf fragmentation and concurrent index rebuilds

High-frequency UPDATE and DELETE workloads fragment B-Tree leaf pages. When index bloat exceeds 30%, index scans suffer unnecessary buffer page lookups. REINDEX CONCURRENTLY reclaims space without exclusive table locking.

Golden Rule & Production Standard

Bloat > 30% → scheduled REINDEX · Bloat > 50% → urgent rebuild · Inspect bloat ratio following heavy table purges

index_bloat_check.sql
-- Index bloat tahmini (pgstattuple extension gerekir)  
CREATE EXTENSION IF NOT EXISTS pgstattuple;  
  
SELECT  
  schemaname || '.' || relname AS table,  
  indexrelname AS index,  
  pg_size_pretty(pg_relation_size(indexrelid)) AS index_size,  
  round(100 * (pgstatindex(indexrelid)).leaf_fragmentation, 1) AS bloat_pct  
FROM pg_stat_user_indexes  
WHERE schemaname NOT IN ('pg_catalog', 'information_schema')  
ORDER BY pg_relation_size(indexrelid) DESC  
LIMIT 20;  
  
-- Kesintisiz rebuild  
REINDEX INDEX CONCURRENTLY idx_orders_created_at;
Tags:
PostgreSQLQuery & PlanCritical Impact

pg_stat_statements: Uncover Top 10 Resource Hogs

Cumulative CPU and I/O consumption query profiling

Stop guessing query bottlenecks in production. pg_stat_statements records normalized query execution metrics, call frequency, and buffer I/O timings. Prioritizing top cumulative time queries delivers the highest ROI.

Golden Rule & Production Standard

Sort by total_exec_time first, not mean_exec_time. High frequency moderate-latency queries often generate far greater system load than rare slow ones.

top_slow_queries.sql
CREATE EXTENSION IF NOT EXISTS pg_stat_statements;  
  
-- En pahalı 10 sorgu (toplam süre)  
SELECT  
  round(total_exec_time::numeric, 1) AS total_ms,  
  calls,  
  round(mean_exec_time::numeric, 2) AS mean_ms,  
  round((100 * total_exec_time / SUM(total_exec_time) OVER ())::numeric, 1) AS pct,  
  left(query, 80) AS query  
FROM pg_stat_statements  
ORDER BY total_exec_time DESC  
LIMIT 10;
Tags:
MySQLMemory & CacheCritical Impact

innodb_buffer_pool_size: The Heart of MySQL Performance

InnoDB buffer pool sizing and cache hit ratio validation

InnoDB buffer pool caches table data, secondary indexes, and undo logs. On dedicated MySQL servers, allocate 70–80% of system RAM. Too small causes continuous disk IOPS bottlenecks; too large leaves no headroom for thread buffers, triggering OS OOM killers.

Golden Rule & Production Standard

Dedicated Host → RAM × 0.75 · Shared Host → RAM × 0.50 · Buffer Pool Hit Ratio < 99% → increase size or optimize scans

my.cnf
# my.cnf — 64 GB RAM dedicated MySQL sunucu  
[mysqld]  
innodb_buffer_pool_size = 48G  
innodb_buffer_pool_instances = 8   # her instance ~6 GB  
innodb_buffer_pool_chunk_size = 128M
buffer_pool_hit_ratio.sql
-- Buffer pool hit ratio kontrolü  
SELECT  
  ROUND(  
    (1 - (Innodb_buffer_pool_reads / Innodb_buffer_pool_read_requests)) * 100, 2  
  ) AS hit_ratio_pct  
FROM  
  (SELECT VARIABLE_VALUE AS Innodb_buffer_pool_reads  
   FROM performance_schema.global_status  
   WHERE VARIABLE_NAME = 'Innodb_buffer_pool_reads') AS a,  
  (SELECT VARIABLE_VALUE AS Innodb_buffer_pool_read_requests  
   FROM performance_schema.global_status  
   WHERE VARIABLE_NAME = 'Innodb_buffer_pool_read_requests') AS b;  
-- Hedef: >= 99.0
Tags:
MySQLQuery & PlanHigh Impact

Slow Query Log: Capture Sub-Second Outliers

Sub-second threshold logging and pt-query-digest analytics

MySQL slow query log captures queries exceeding the execution threshold or omitting indexes. Setting long_query_time = 0.5 (500ms) establishes a solid production baseline. Analyze logs with pt-query-digest to identify aggregate CPU drain.

Golden Rule & Production Standard

Keep long_query_time below 1.0s. 1 second is unacceptable in OLTP workloads; 100–200ms queries should be treated as high priority warnings.

my.cnf
# my.cnf  
[mysqld]  
slow_query_log = 1  
slow_query_log_file = /var/log/mysql/slow.log  
long_query_time = 0.5  
log_queries_not_using_indexes = 1  
log_slow_admin_statements = 1
digest.sh
# En pahalı sorguları özetle (Percona Toolkit)  
pt-query-digest /var/log/mysql/slow.log \  
  --limit 10 \  
  --order-by Query_time:sum
Tags:
PostgreSQLQuery & PlanCritical Impact

EXPLAIN ANALYZE: Read Ground Reality, Not Estimates

Execution plan interpretation, buffer cache misses & filters

EXPLAIN provides optimizer estimates; EXPLAIN ANALYZE executes the query and reports ground truth timings. Adding BUFFERS uncovers exact cache hits vs disk reads. High Seq Scan on large tables signals missing/unusable indexes or poor filter selectivity.

Golden Rule & Production Standard

actual time ≫ estimated cost → stale optimizer stats (run ANALYZE) · high "rows removed by filter" → index WHERE selectivity is poor · high shared read → memory pressure

explain_buffers.sql
EXPLAIN (ANALYZE, BUFFERS, FORMAT TEXT)  
SELECT o.id, c.name, SUM(oi.qty * oi.price) AS total  
FROM orders o  
JOIN customers c ON c.id = o.customer_id  
JOIN order_items oi ON oi.order_id = o.id  
WHERE o.created_at >= NOW() - INTERVAL '30 days'  
GROUP BY o.id, c.name  
ORDER BY total DESC  
LIMIT 50;  
  
-- Dikkat edilecek satırlar:  
-- Seq Scan on orders  → index eksik olabilir  
-- Buffers: shared read=8421  → disk'ten okundu, cache miss  
-- Rows Removed by Filter: 1.2M  → WHERE selektivitesi düşük
Tags:
MySQLIndex StrategyHigh Impact

Covering Index: Zero Primary Table Lookups

Eliminating clustered index traversals via "Using index"

When an index satisfies all requested SELECT, WHERE, and ORDER BY columns, InnoDB avoids clustered index pointer lookups completely. EXPLAIN confirms this via "Using index". Highly effective for hot OLTP queries.

Golden Rule & Production Standard

Combine WHERE + ORDER BY + SELECT columns into composite indexes. Do not create indexes blindly — focus exclusively on top 3-5 query patterns to avoid write amplification.

covering_index.sql
-- Yavaş: tabloya geri dönmek zorunda  
-- EXPLAIN: type=ref, Extra=Using where  
SELECT email, created_at  
FROM users  
WHERE status = 'active'  
ORDER BY created_at DESC  
LIMIT 20;  
  
-- Covering index  
CREATE INDEX idx_users_status_created_email  
  ON users (status, created_at DESC, email);  
  
-- Artık: Extra=Using index  → tabloya hiç dokunulmaz
Tags:
PostgreSQLMaintenance & HealthCritical Impact

Autovacuum Tuning: Preventing Dead Tuple Crisis

Aggressive bloat mitigation and transaction ID exhaustion guardrails

Autovacuum recycles dead tuples to prevent table bloat and transaction ID wraparound outages. Default postgresql.conf settings are too conservative for heavy write workloads. Lower scale factors trigger timely passes, while higher autovacuum_work_mem accelerates index scans.

Golden Rule & Production Standard

High-write tables require autovacuum_vacuum_scale_factor = 0.05 (default 0.2). Alert when database TXID age reaches 1B (crisis point: 2B).

postgresql.conf
# postgresql.conf — global  
autovacuum_max_workers = 4  
autovacuum_vacuum_scale_factor = 0.05  
autovacuum_analyze_scale_factor = 0.02  
autovacuum_work_mem = 1GB
table_autovacuum_and_xid.sql
-- Tablo bazlı override (en yoğun tablo)  
ALTER TABLE orders SET (  
  autovacuum_vacuum_scale_factor = 0.02,  
  autovacuum_vacuum_threshold = 1000,  
  autovacuum_analyze_scale_factor = 0.01  
);  
  
-- Wraparound riski izleme  
SELECT  
  datname,  
  age(datfrozenxid) AS xid_age,  
  round(age(datfrozenxid)::numeric / 2000000000 * 100, 1) AS wraparound_pct  
FROM pg_database  
ORDER BY xid_age DESC;  
-- wraparound_pct > 70 → kritik alarm
Tags:
Cross-EngineAnti-PatternsCritical Impact

N+1 Query Anti-Pattern: Eliminating Loop Queries

Connection pool starvation & excessive roundtrip elimination

Fetching a list and issuing individual database queries inside application loops creates severe connection pool starvation and multiplies network roundtrips. 100 orders × 1 customer = 101 trips. Solve via JOIN or parameterized IN/ANY arrays.

Golden Rule & Production Standard

Never issue database queries inside application loops. Replace 1 list + N detail queries with single JOINs or array queries.

n_plus_one_remediation.sql
-- ❌ N+1 (uygulama sözde-kodu)  
-- orders = SELECT * FROM orders LIMIT 100  
-- for o in orders:  
--     customer = SELECT * FROM customers WHERE id = o.customer_id  
  
-- ✅ Tek sorgu — JOIN  
SELECT o.id, o.total, c.name, c.email  
FROM orders o  
JOIN customers c ON c.id = o.customer_id  
ORDER BY o.created_at DESC  
LIMIT 100;  
  
-- ✅ Toplu fetch — ANY / IN  
SELECT id, name, email  
FROM customers  
WHERE id = ANY(ARRAY[1, 5, 12, 44, 89]);  -- PostgreSQL  
  
SELECT id, name, email  
FROM customers  
WHERE id IN (1, 5, 12, 44, 89);            -- MySQL
Tags:
PostgreSQLIndex StrategyCritical Impact

Seq Scan Hunt: Identifying Missing Indexes

Identifying missing indexes via sequential vs index scan statistics

High sequential scan counts with low index scan ratios in pg_stat_user_tables reveal missing index opportunities. For large tables (>10K rows), frequent Seq Scans almost always indicate indexing gaps, whereas small lookup tables naturally benefit from sequential scans.

Golden Rule & Production Standard

seq_scan > 1000 AND n_live_tup > 10000 AND idx_scan / (seq_scan + idx_scan) < 0.5 → missing index candidate. Never blindly index small tables.

missing_indexes_hunt.sql
SELECT  
  schemaname || '.' || relname AS table,  
  seq_scan,  
  idx_scan,  
  n_live_tup AS rows,  
  round(  
    CASE WHEN (seq_scan + idx_scan) = 0 THEN 0  
    ELSE 100.0 * idx_scan / (seq_scan + idx_scan) END, 1  
  ) AS idx_usage_pct,  
  pg_size_pretty(pg_relation_size(relid)) AS size  
FROM pg_stat_user_tables  
WHERE n_live_tup > 10000  
  AND seq_scan > 1000  
ORDER BY seq_scan DESC  
LIMIT 20;
Tags:
MySQLQuery & PlanHigh Impact

EXPLAIN JSON: Deep Cost and Scan Inspection

cost_info, rows_examined_per_scan and temporary table diagnostics

Standard EXPLAIN tabular format hides optimizer cost calculations. EXPLAIN FORMAT=JSON provides cost_info, rows_examined_per_scan, and used_key_parts, revealing why the optimizer chose a specific join order or index.

Golden Rule & Production Standard

High query_cost + rows_examined_per_scan ≫ returned rows → poor index selectivity. using_temporary_table: true + using_filesort: true combined → severe memory/disk temp table overhead.

explain_format_json.sql
EXPLAIN FORMAT=JSON  
SELECT o.id, SUM(oi.qty * oi.unit_price) AS total  
FROM orders o  
JOIN order_items oi ON oi.order_id = o.id  
WHERE o.status = 'shipped'  
  AND o.created_at >= '2026-01-01'  
GROUP BY o.id  
ORDER BY total DESC  
LIMIT 20\G  
  
-- Odaklanılacak alanlar:  
-- cost_info.query_cost  
-- rows_examined_per_scan  
-- used_key_parts  
-- using_filesort / using_temporary_table
Tags:
PostgreSQLIndex StrategyHigh Impact

Partial Index: Indexing Only Hot Working Sets

Targeted partial indexes for soft-deletes and active state queries

Targeting subsets of data with a WHERE clause creates compact partial indexes that reduce storage by up to 90% and lower write amplification. Ideal for soft-deletes, queue status flags, and active-only lookups.

Golden Rule & Production Standard

If queries consistently filter by static predicates (status = 'open', deleted_at IS NULL) → use partial indexes. Avoid for dynamic filter conditions.

partial_indexes.sql
-- ❌ Tüm satırları indeksle (şişman)  
CREATE INDEX idx_orders_status ON orders (status, created_at);  
  
-- ✅ Sadece açık siparişler  
CREATE INDEX idx_orders_open_created  
  ON orders (created_at DESC)  
  WHERE status = 'open';  
  
-- Soft-delete pattern  
CREATE INDEX idx_users_active_email  
  ON users (email)  
  WHERE deleted_at IS NULL;  
  
-- Sorgu partial index'i kullanır:  
SELECT * FROM orders  
WHERE status = 'open'  
  AND created_at >= NOW() - INTERVAL '7 days';
Tags:
MySQLIndex StrategyHigh Impact

Unused & Redundant Indexes: The Silent Write Tax

Auditing unused and redundant indexes using MySQL sys schema

Every write operation incurs maintenance overhead across all secondary indexes. Redundant or unqueried indexes inflate the buffer pool and slow down DML. Use MySQL sys schema to identify candidates for safe deprecation.

Golden Rule & Production Standard

Indexes unreferenced in sys.schema_unused_indexes over 30 days → deprecation candidates. Never blindly drop PKs, unique constraints, or foreign key indexes.

unused_redundant_indexes.sql
-- Kullanılmayan index'ler (MySQL 5.7+ / 8.0 sys schema)  
SELECT * FROM sys.schema_unused_indexes  
WHERE object_schema NOT IN ('mysql', 'sys', 'performance_schema');  
  
-- Redundant index'ler  
SELECT * FROM sys.schema_redundant_indexes  
WHERE table_schema = 'app_production';  
  
-- Güvenli kaldırma (önce staging'de dene)  
-- ALTER TABLE orders DROP INDEX idx_orders_old_status;
Tags:
PostgreSQLConcurrency & LocksCritical Impact

PgBouncer: 500 Clients ≠ 500 Postgres Backends

Preventing memory spikes and context switching with transaction pooling

Every native PostgreSQL backend process consumes 5-10MB RAM. 500 direct client connections create massive CPU context-switching overhead and OOM risk. PgBouncer in transaction pooling mode multiplexes hundreds of apps into dozens of dedicated server connections.

Golden Rule & Production Standard

OLTP → pool_mode = transaction · Session-heavy/prepared statements → session mode · Set default_pool_size between (CPU × 2) and (CPU × 4).

pgbouncer.ini
# pgbouncer.ini  
[databases]  
app_db = host=127.0.0.1 port=5432 dbname=app_db  
  
[pgbouncer]  
listen_addr = 0.0.0.0  
listen_port = 6432  
auth_type = scram-sha-256  
auth_file = /etc/pgbouncer/userlist.txt  
pool_mode = transaction  
max_client_conn = 1000  
default_pool_size = 40  
min_pool_size = 10  
reserve_pool_size = 10  
server_reset_query = DISCARD ALL  
ignore_startup_parameters = extra_float_digits
postgresql.conf
-- PostgreSQL tarafı: gerçek bağlantı tavanı  
-- postgresql.conf  
max_connections = 100   -- pool arkasında düşük tut
Tags:
MySQLWrite & LoadCritical Impact

Durability vs Throughput: The fsync Trade-off

Redo log flush frequency and ACID durability trade-offs

Fsyncing redo logs on every commit (1) provides strict ACID compliance at the cost of disk latency. Value 2 buffers commits in OS cache with once-per-second flushes, risking <=1s loss on hard power failure. Value 0 is highest risk.

Golden Rule & Production Standard

Financial/orders/ledger workloads → strictly 1. High-throughput logs, telemetry, analytics → 2. Never deploy 0 in production.

my.cnf
# my.cnf  
[mysqld]  
# Full durability (varsayılan, önerilen)  
innodb_flush_log_at_trx_commit = 1  
sync_binlog = 1  
  
# Yüksek yazma, 1 sn kayıp tolere edilebilir  
# innodb_flush_log_at_trx_commit = 2  
# sync_binlog = 1
check_flush.sql
-- Runtime kontrol  
SHOW VARIABLES LIKE 'innodb_flush_log_at_trx_commit';
Tags:
PostgreSQLIndex StrategyHigh Impact

BRIN: Lightweight Indexing for Billions of Rows

Block Range Indexing for massive append-only time-series datasets

Standard B-Trees consume gigabytes of storage on massive append-only time-series tables. BRIN (Block Range Index) stores summary min/max ranges per disk block group, occupying only megabytes with minimal write impact.

Golden Rule & Production Standard

Table > 50-100GB AND column physically correlates with storage order (timestamp, serial ID) → use BRIN. Ineffective for high random distributions (UUIDs).

brin_index_setup.sql
-- Zaman serisi olay tablosu  
CREATE TABLE events (  
  id          bigserial,  
  created_at  timestamptz NOT NULL DEFAULT now(),  
  payload     jsonb  
) PARTITION BY RANGE (created_at);  
  
-- BRIN: pages_per_range ayarı trade-off  
CREATE INDEX idx_events_created_brin  
  ON events USING BRIN (created_at)  
  WITH (pages_per_range = 32);  
  
-- Karşılaştırma  
SELECT  
  indexrelname,  
  pg_size_pretty(pg_relation_size(indexrelid)) AS size  
FROM pg_stat_user_indexes  
WHERE relname = 'events';
Tags:
MySQLWrite & LoadHigh Impact

Single INSERT Bottlenecks: Unlocking Bulk Ingestion

Multi-row batched statements and fast LOAD DATA INFILE pipelines

Executing individual INSERT statements triggers repetitive transaction commits, redo log fsyncs, and B-Tree rebalances. Batch multi-row statements and LOAD DATA INFILE accelerate bulk ingestion by 10-50x.

Golden Rule & Production Standard

Batch 100-1000 rows per statement instead of single rows. Use explicit batch transactions instead of per-statement autocommit.

bulk_insert_methods.sql
-- ❌ Yavaş  
INSERT INTO events (user_id, event_type) VALUES (1, 'click');  
INSERT INTO events (user_id, event_type) VALUES (2, 'view');  
-- ... 10.000 kez  
  
-- ✅ Multi-value batch  
INSERT INTO events (user_id, event_type, created_at) VALUES  
  (1, 'click', NOW()),  
  (2, 'view',  NOW()),  
  (3, 'click', NOW());  
  -- 500–1000 satır/statement ideal  
  
-- ✅ Dosyadan bulk  
LOAD DATA LOCAL INFILE '/tmp/events.csv'  
INTO TABLE events  
FIELDS TERMINATED BY ',' ENCLOSED BY '"'  
LINES TERMINATED BY '\n'  
(user_id, event_type, @created)  
SET created_at = STR_TO_DATE(@created, '%Y-%m-%d %H:%i:%s');
mysql_bulk.sh
# mysql client ile  
mysql --local-infile=1 -e "LOAD DATA LOCAL INFILE ..."
Tags:
PostgreSQLConcurrency & LocksCritical Impact

Who Blocks Whom? PostgreSQL Lock Tree Diagnostics

Diagnosing blocker-blocked query trees and idle-in-transaction locks

Apparent query slowdowns are often caused by lock contention rather than CPU/disk bottlenecks. Cross-referencing pg_locks with pg_stat_activity reveals root blocking transactions and idle-in-transaction connection leaks.

Golden Rule & Production Standard

idle in transaction > 30s → application bug or connection leak. Attempt pg_cancel_backend first before resorting to forceful pg_terminate_backend.

blocking_locks_tree.sql
-- Blocker → blocked ağacı  
SELECT  
  blocked.pid AS blocked_pid,  
  blocked.usename AS blocked_user,  
  left(blocked.query, 60) AS blocked_query,  
  blocking.pid AS blocking_pid,  
  blocking.usename AS blocking_user,  
  left(blocking.query, 60) AS blocking_query,  
  blocked.wait_event_type,  
  blocked.wait_event,  
  now() - blocked.query_start AS blocked_duration  
FROM pg_stat_activity blocked  
JOIN pg_locks bl ON bl.pid = blocked.pid AND NOT bl.granted  
JOIN pg_locks bk ON bk.locktype = bl.locktype  
  AND bk.database IS NOT DISTINCT FROM bl.database  
  AND bk.relation IS NOT DISTINCT FROM bl.relation  
  AND bk.page IS NOT DISTINCT FROM bl.page  
  AND bk.tuple IS NOT DISTINCT FROM bl.tuple  
  AND bk.virtualxid IS NOT DISTINCT FROM bl.virtualxid  
  AND bk.transactionid IS NOT DISTINCT FROM bl.transactionid  
  AND bk.classid IS NOT DISTINCT FROM bl.classid  
  AND bk.objid IS NOT DISTINCT FROM bl.objid  
  AND bk.objsubid IS NOT DISTINCT FROM bl.objsubid  
  AND bk.granted  
JOIN pg_stat_activity blocking ON blocking.pid = bk.pid  
WHERE blocked.pid <> blocking.pid;  
  
-- Nazik iptal / sert sonlandırma  
-- SELECT pg_cancel_backend(12345);  
-- SELECT pg_terminate_backend(12345);
Tags:
MySQLConcurrency & LocksHigh Impact

Gap Lock Storms: Mitigating MySQL Deadlocks

Suppressing gap locks and deadlock storms via READ COMMITTED isolation

InnoDB default REPEATABLE READ takes gap locks that frequently collide under concurrent write traffic. In most OLTP architectures, switching to READ COMMITTED suppresses gap locking and eliminates the vast majority of deadlocks.

Golden Rule & Production Standard

If deadlocks occur across index ranges, verify query ordering, then evaluate READ COMMITTED. Regularly inspect LATEST DETECTED DEADLOCK in SHOW ENGINE INNODB STATUS.

isolation_level_tuning.sql
-- Mevcut isolation  
SELECT @@transaction_isolation;  
  
-- Session bazlı (önerilen ilk adım)  
SET SESSION transaction_isolation = 'READ COMMITTED';  
  
-- Global (my.cnf kalıcı)  
-- [mysqld]  
-- transaction_isolation = READ-COMMITTED  
  
-- Son deadlock'u incele  
SHOW ENGINE INNODB STATUS\G  
-- "LATEST DETECTED DEADLOCK" bölümüne bak
my.cnf
# my.cnf — ek gözlem  
[mysqld]  
innodb_print_all_deadlocks = 1  
innodb_deadlock_detect = ON
Tags:
PostgreSQLMemory & CacheCritical Impact

Tuning PostgreSQL I/O Costs for SSDs & NVMe

random_page_cost and parallel bitmap scan sizing for modern NVMe drives

Default effective_io_concurrency = 1 and random_page_cost = 4.0 are calibrated for legacy spinning HDDs. On modern SSDs and NVMe drives, tuning random_page_cost closer to seq_page_cost encourages optimal index utilization.

Golden Rule & Production Standard

HDD → keep defaults. SATA SSD → random_page_cost = 1.5, effective_io_concurrency = 200. NVMe → random_page_cost = 1.1, effective_io_concurrency = 300-500.

postgresql.conf
# postgresql.conf — NVMe production örneği  
random_page_cost = 1.1  
seq_page_cost = 1.0  
effective_io_concurrency = 400  
maintenance_io_concurrency = 400  
  
# Tablespace bazlı override da mümkün:  
# ALTER TABLESPACE fast_ssd SET (random_page_cost = 1.1);
Tags:
Cross-EngineAnti-PatternsHigh Impact

SELECT *: Eliminating Redundant Payload Overheads

Network payload bloat, TOAST overhead and lost covering index benefits

SELECT * nullifies covering index opportunities, inflates network payload, and forces unnecessary TOAST/LOB disk lookups. Explicit column lists reduce serialization overhead and memory footprint.

Golden Rule & Production Standard

Never fetch columns absent from the API response payload. Always combine explicit projection lists with indexed pagination.

explicit_column_selection.sql
-- ❌  
SELECT * FROM products WHERE category_id = 42;  
  
-- ✅  
SELECT id, name, price, slug  
FROM products  
WHERE category_id = 42  
ORDER BY name  
LIMIT 50 OFFSET 0;  
  
-- PostgreSQL: geniş TOAST kolonunu bilinçli hariç tut  
SELECT id, title, created_at  
FROM articles  
WHERE status = 'published';  
-- body / html_content çekilmedi → TOAST I/O yok
Tags:
SQL ServerMaintenance & HealthHigh Impact

TempDB: Eliminating PAGELATCH_UP Contention

PFS/SGAM allocation page contention & multiple equal data files

High temp table creation and teardown rates cause severe PFS/GAM allocation bottlenecks when TempDB uses a single file. Splitting TempDB into multiple equal data files matches CPU core counts and prevents thread stalls.

Golden Rule & Production Standard

<=8 cores → 1 equal TempDB file per logical core; >8 cores → start with 8 files and scale in increments of 4.

tempdb_setup.sql
-- TempDB Dosyaları Eşit Boyutlandırma ve Otomatik Büyüme (MB bazlı)
ALTER DATABASE tempdb MODIFY FILE (NAME = tempdev, SIZE = 8GB, FILEGROWTH = 512MB);
ALTER DATABASE tempdb ADD FILE (NAME = tempdev2, FILENAME = 'D:\MSSQL\DATA\tempdev2.ndf', SIZE = 8GB, FILEGROWTH = 512MB);
ALTER DATABASE tempdb ADD FILE (NAME = tempdev3, FILENAME = 'D:\MSSQL\DATA\tempdev3.ndf', SIZE = 8GB, FILEGROWTH = 512MB);
ALTER DATABASE tempdb ADD FILE (NAME = tempdev4, FILENAME = 'D:\MSSQL\DATA\tempdev4.ndf', SIZE = 8GB, FILEGROWTH = 512MB);
Tags: