Bài này là tham khảo nhanh cho toàn bộ series — không giải thích dài, chỉ lệnh, query, config đã gặp trong 13 bài. Bookmark và mở ra khi đang debug production hoặc tune hệ thống.

1. PostgreSQL

1.1 EXPLAIN / Analyze

-- Full option (PG 16+)
EXPLAIN (ANALYZE, BUFFERS, VERBOSE, SETTINGS, WAL, FORMAT JSON)
  SELECT ...;

-- Safe UPDATE/DELETE
BEGIN;
EXPLAIN (ANALYZE, BUFFERS) UPDATE ...;
ROLLBACK;

1.2 Monitoring queries

-- Top queries by time
SELECT query, calls, total_exec_time, mean_exec_time, rows
FROM pg_stat_statements
ORDER BY total_exec_time DESC LIMIT 20;

-- Parallel workers (PG 13+)
SELECT leader.pid, leader.query, COUNT(w.pid) AS workers
FROM pg_stat_activity leader
JOIN pg_stat_activity w ON w.leader_pid = leader.pid
GROUP BY leader.pid, leader.query;

-- pg_stat_io (PG 16+)
SELECT backend_type, object, context,
       reads, writes, hits, ROUND(100.0*hits/NULLIF(hits+reads,0),2) AS hit_pct
FROM pg_stat_io;

-- Replication lag (từ primary)
SELECT client_addr, state,
       pg_wal_lsn_diff(pg_current_wal_lsn(), replay_lsn) AS lag_bytes,
       NOW() - reply_time AS lag_time
FROM pg_stat_replication;

-- Replication lag (từ standby)
SELECT pg_is_in_recovery(),
       NOW() - pg_last_xact_replay_timestamp() AS replay_lag;

-- Slot bloat
SELECT slot_name,
       pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(),
                      confirmed_flush_lsn)) AS lag
FROM pg_replication_slots;

-- Long-running transactions
SELECT pid, usename, state, query_start,
       NOW() - query_start AS duration, query
FROM pg_stat_activity
WHERE state != 'idle' AND NOW() - query_start > INTERVAL '5 min';

-- Locks
SELECT blocked.pid AS blocked_pid, blocked.query AS blocked_query,
       blocking.pid AS blocking_pid, blocking.query AS blocking_query
FROM pg_stat_activity blocked
JOIN pg_stat_activity blocking ON blocking.pid = ANY(pg_blocking_pids(blocked.pid))
WHERE blocked.state = 'active';

1.3 Index types

-- B-tree (mặc định) cho =, <, >, ORDER BY
CREATE INDEX ON t(col);

-- Covering index (PG 11+)
CREATE INDEX ON t(key) INCLUDE (col1, col2);

-- Partial index
CREATE INDEX ON orders(order_date) WHERE status = 'active';

-- Expression index
CREATE INDEX ON users(lower(email));

-- GIN cho JSONB, array, tsvector
CREATE INDEX ON t USING GIN (jsonb_col jsonb_path_ops);
CREATE INDEX ON t USING GIN (to_tsvector('simple', body));

-- GiST cho ranges, geometry, ltree
CREATE INDEX ON t USING GIST (range_col);

-- BRIN cho time-series / append-only
CREATE INDEX ON events USING BRIN (created_at) WITH (pages_per_range = 32);

-- HNSW cho vector (pgvector 0.5+)
CREATE INDEX ON docs USING hnsw (embedding vector_cosine_ops)
  WITH (m = 16, ef_construction = 64);

1.4 Config essentials (postgresql.conf)

# Memory (cho máy 32 GB RAM)
shared_buffers = 8GB              # 25% RAM
effective_cache_size = 22GB       # 70% RAM
work_mem = 64MB                   # cho sort/hash
maintenance_work_mem = 2GB        # cho VACUUM/CREATE INDEX

# Write
wal_level = logical               # logical replication + CDC
max_wal_size = 4GB
checkpoint_timeout = 15min
synchronous_commit = on           # = off nếu OK mất vài s khi crash

# Parallelism
max_worker_processes = 16
max_parallel_workers = 8
max_parallel_workers_per_gather = 4

# JIT (PG 11+)
jit = on
jit_above_cost = 500000           # tune cao cho OLTP

# Logging
log_min_duration_statement = 500ms
log_checkpoints = on
log_lock_waits = on
auto_explain.log_min_duration = 1000ms

1.5 PITR (PG 12+)

# Enable trên primary
# archive_mode = on
# archive_command = 'pgbackrest --stanza=main archive-push %p'

# Base backup
pgbackrest --stanza=main backup --type=full

# Restore PITR
pgbackrest --stanza=main --type=time \
  --target="2026-04-15 14:30:00+00" restore

# Tạo file cờ
touch /var/lib/postgresql/16/main/recovery.signal
systemctl start postgresql

2. MySQL / MariaDB

2.1 EXPLAIN

EXPLAIN ANALYZE SELECT ...;      -- MySQL 8.0.18+
EXPLAIN FORMAT=TREE SELECT ...;  -- dễ đọc thứ tự thực hiện

-- Performance Schema
SELECT digest_text, count_star, sum_timer_wait/1e9 AS total_ms
FROM performance_schema.events_statements_summary_by_digest
ORDER BY sum_timer_wait DESC LIMIT 20;

2.2 Replication (MySQL 8.0.22+)

-- Dùng thuật ngữ replica/source thay master/slave
SHOW REPLICA STATUS\G
START REPLICA;
STOP REPLICA;
CHANGE REPLICATION SOURCE TO SOURCE_HOST='...', SOURCE_LOG_FILE='...';

-- Parallel replication
-- My.cnf replica:
--   binlog_transaction_dependency_tracking = WRITESET
--   replica_parallel_type = LOGICAL_CLOCK
--   replica_parallel_workers = 8
--   replica_preserve_commit_order = ON

2.3 Config essentials (my.cnf)

[mysqld]
# Memory (cho máy 32 GB)
innodb_buffer_pool_size = 22G         # 70% RAM
innodb_buffer_pool_instances = 8
innodb_log_buffer_size = 64M

# Redo log (MySQL 8.0.30+)
innodb_redo_log_capacity = 4G         # thay cho log_file_size * files_in_group

# Flush
innodb_flush_method = O_DIRECT
innodb_flush_log_at_trx_commit = 1    # = 2 nếu OK mất vài s khi crash
sync_binlog = 1

# IO
innodb_io_capacity = 2000             # phù hợp SSD gp3
innodb_io_capacity_max = 4000

# Binary logging (cho replication / CDC)
log_bin = mysql-bin
binlog_format = ROW
binlog_row_image = FULL
gtid_mode = ON
enforce_gtid_consistency = ON

# Performance Schema
performance_schema = ON
performance_schema_consumer_statements_digest = ON

2.4 Slow query

-- Bật slow query log
SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 1;          -- giây
SET GLOBAL slow_query_log_file = '/var/log/mysql/slow.log';

-- Phân tích
-- pt-query-digest /var/log/mysql/slow.log

3. Redis / Valkey

3.1 Core commands

# Set với TTL (atomic)
SET key value EX 600                    # 600s

# Delete lớn không block
UNLINK bigkey1 bigkey2

# Scan thay vì KEYS (production-safe)
SCAN 0 MATCH user:* COUNT 500

# ACL (Redis 6+)
ACL SETUSER alice on >password ~cache:* +@read +@write
ACL WHOAMI
ACL LIST

# Config online
CONFIG SET maxmemory 4gb
CONFIG SET maxmemory-policy allkeys-lru

3.2 Cache patterns

# TTL jitter + mutex — chống stampede
def get_with_lock(key, recompute, lock_ttl=5):
    v = cache.get(key)
    if v is not None: return v
    if redis.set(f"lock:{key}", "1", nx=True, ex=lock_ttl):
        try:
            v = recompute()
            cache.set(key, v, ttl=3600 + random.randint(-600, 600))
            return v
        finally:
            redis.delete(f"lock:{key}")
    else:
        time.sleep(0.05)
        return get_with_lock(key, recompute, lock_ttl)

# Negative cache
def get_user(uid):
    key = f"user:{uid}"
    v = cache.get(key)
    if v == "__NULL__": return None
    if v: return json.loads(v)
    u = db.users.find_one(uid)
    cache.set(key, json.dumps(u) if u else "__NULL__",
              ttl=60 if not u else 3600 + random.randint(-300, 300))
    return u

3.3 Cluster

# Tạo cluster
redis-cli --cluster create 10.0.0.1:7000 10.0.0.2:7000 10.0.0.3:7000 \
  10.0.0.4:7000 10.0.0.5:7000 10.0.0.6:7000 --cluster-replicas 1

# Thêm node + reshard
redis-cli --cluster add-node 10.0.0.10:7000 10.0.0.1:7000
redis-cli --cluster reshard 10.0.0.1:7000 --cluster-from all \
  --cluster-to <new-id> --cluster-slots 4096 --cluster-yes

# Check
redis-cli --cluster check 10.0.0.1:7000

4. Cassandra / ScyllaDB

-- Data model theo query pattern
CREATE TABLE messages_by_room (
  room_id UUID,
  ts TIMESTAMP,
  msg_id TIMEUUID,
  body TEXT,
  PRIMARY KEY ((room_id), ts, msg_id)
) WITH CLUSTERING ORDER BY (ts DESC, msg_id DESC)
  AND default_time_to_live = 2592000
  AND compaction = {'class': 'TimeWindowCompactionStrategy',
                    'compaction_window_unit': 'DAYS',
                    'compaction_window_size': 1};

-- Consistency level
CONSISTENCY LOCAL_QUORUM;

-- Nodetool — ops đời thường
nodetool status
nodetool compactionstats
nodetool tablehistograms keyspace table
nodetool repair -pr

5. pgvector

CREATE EXTENSION vector;

-- Table
CREATE TABLE docs (id BIGSERIAL, embedding vector(1536));

-- Index
CREATE INDEX ON docs USING hnsw (embedding vector_cosine_ops)
  WITH (m = 16, ef_construction = 64);

-- Runtime tune
SET hnsw.ef_search = 100;

-- Query (cosine)
SELECT id, 1 - (embedding <=> $1) AS sim
FROM docs ORDER BY embedding <=> $1 LIMIT 10;

-- Hybrid (RRF)
WITH vec AS (SELECT id, ROW_NUMBER() OVER (ORDER BY embedding <=> $1) r FROM docs LIMIT 50),
     kw  AS (SELECT id, ROW_NUMBER() OVER (ORDER BY ts_rank_cd(...) DESC) r FROM docs LIMIT 50)
SELECT id, SUM(1.0/(60 + r)) AS score
FROM (SELECT * FROM vec UNION ALL SELECT * FROM kw) t
GROUP BY id ORDER BY score DESC LIMIT 10;

6. Outbox / CDC

-- Outbox table
CREATE TABLE outbox (
  id BIGSERIAL PRIMARY KEY,
  aggregate_type TEXT, aggregate_id TEXT,
  event_type TEXT, payload JSONB,
  created_at TIMESTAMPTZ DEFAULT now(),
  processed_at TIMESTAMPTZ
);
CREATE INDEX ON outbox (id) WHERE processed_at IS NULL;

-- Worker polling safely
SELECT id, event_type, payload FROM outbox
WHERE processed_at IS NULL
ORDER BY id FOR UPDATE SKIP LOCKED LIMIT 500;

-- Debezium PG prerequisites
-- postgresql.conf:
--   wal_level = logical
--   max_replication_slots = 4
--   max_wal_senders = 4
CREATE USER debezium WITH REPLICATION PASSWORD '...';
CREATE PUBLICATION dbz_pub FOR TABLE orders, order_items;

7. Benchmark snippets

# pgbench
pgbench -i -s 100 bench
pgbench -c 32 -j 8 -T 600 -P 10 bench

# sysbench
sysbench oltp_read_write --mysql-host=... --threads=64 --time=600 run

# k6 (app-level)
k6 run -u 100 -d 30m script.js

8. Tuning heuristic bỏ túi

Triệu chứngKhả năng caoBước kiểm tra
Query chậm mà nhẹIndex missEXPLAIN ANALYZE, kiểm tra Seq Scan
DB CPU caoQuery heavy / no index / N+1pg_stat_statements, slow log
DB IO caoDataset > RAM, checkpointpg_stat_io, iostat, tăng shared_buffers
Latency spike định kỳCheckpoint / autovacuum / GClog_checkpoints, log_autovacuum
Replica lag tăngReplica yếu, parallel replication offenable parallel replica
Connection exhaustedKhông có poolPgBouncer / ProxySQL / RDS Proxy
Cache miss caoTTL đồng thời, không jitterTTL jitter, mutex, stale-while-revalidate
Vector recall thấpef_search thấptăng ef_search, verify với recall@10

9. Danh sách công cụ ops

Mục đíchCông cụ
Connection pool PGPgBouncer, Supavisor, pgcat
Connection pool MySQLProxySQL, MySQL Router
Schema migrationFlyway, Liquibase, Atlas, Sqitch
Backup PGpgBackRest, WAL-G, Barman
Backup MySQLPercona XtraBackup, mysqldump + binlog
MonitorPrometheus + Grafana, PMM, Datadog DBM
CDCDebezium, Maxwell, pg_easy_replicate
Load testpgbench, sysbench, HammerDB, YCSB, k6
Vector ANNpgvector, Qdrant, Milvus, Weaviate

10. Top 10 anti-patterns cần tránh

  1. SELECT * trong production queries.
  2. WHERE column = ? nhưng function lên column (WHERE lower(email) = ... mà không có expression index).
  3. N+1 query từ ORM.
  4. KEYS pattern trên Redis production.
  5. SET + EXPIRE 2 roundtrip thay vì SET EX.
  6. Dual-write DB + Kafka không qua Outbox/CDC.
  7. Shard key mutable (đổi sau được).
  8. SERIALIZABLE PostgreSQL mà không có retry loop.
  9. Benchmark không warmup / không đủ dài.
  10. Tự viết distributed SQL từ đầu khi có Vitess/Citus.

Đến đây là kết thúc series. Các bài tiếp theo sẽ không còn follow đường hướng “tối ưu” — chuyển sang các chủ đề liên quan nhưng độc lập: operations runbook, incident response, data modeling patterns, query authoring style. Cảm ơn bạn đã đọc!