Trong sáu bài đầu tiên của series, chúng ta đã tìm hiểu về nền tảng tối ưu hóa cơ sở dữ liệu, phân tích và tối ưu câu truy vấn SQL, chiến lược indexing chuyên sâu, thiết kế schema tối ưu, quản lý transaction và concurrency hiệu quả, cũng như chiến lược caching và tối ưu tầng ứng dụng. Bài viết này sẽ đi sâu vào việc tối ưu hóa cho các hệ quản trị cơ sở dữ liệu SQL phổ biến: MySQL và PostgreSQL.

Mặc dù các nguyên tắc tối ưu hóa cơ bản có thể áp dụng cho hầu hết các hệ quản trị cơ sở dữ liệu, mỗi hệ thống đều có những đặc điểm riêng, cấu trúc nội bộ và tham số cấu hình khác nhau. Hiểu rõ cách tối ưu hóa cụ thể cho từng hệ thống sẽ giúp bạn khai thác tối đa hiệu năng của chúng.

Trong bài viết này, chúng ta sẽ khám phá các kỹ thuật tối ưu hóa chuyên sâu cho MySQL/MariaDB và PostgreSQL, từ cấu hình server, storage engines, đến các tính năng đặc biệt của mỗi hệ thống.

MySQL/MariaDB: InnoDB tuning, buffer pool, replication, partitioning

MySQL và MariaDB là những hệ quản trị cơ sở dữ liệu phổ biến nhất hiện nay. Chúng ta sẽ tập trung vào InnoDB - storage engine mặc định và được khuyến nghị sử dụng.

InnoDB Architecture và Buffer Pool

InnoDB có kiến trúc phức tạp với nhiều thành phần quan trọng:


  graph LR
    A[MySQL Server] --> B[InnoDB Storage Engine]
    B --> C[Buffer Pool]
    B --> D[Change Buffer]
    B --> E[Adaptive Hash Index]
    B --> F[Log Buffer]

    C --> G[Data Pages]
    C --> H[Index Pages]

    F --> I[Redo Log]
    B --> J[Doublewrite Buffer]
    B --> K[System Tablespace]
    B --> L[File-per-table Tablespaces]

Buffer Pool là thành phần quan trọng nhất ảnh hưởng đến hiệu năng của InnoDB. Đây là vùng bộ nhớ lưu trữ data và index pages, giúp giảm thiểu disk I/O.

  1. Sizing Buffer Pool:
-- Kiểm tra cấu hình hiện tại
SHOW VARIABLES LIKE 'innodb_buffer_pool_size';

-- Thiết lập buffer pool size (ví dụ: 8GB)
SET GLOBAL innodb_buffer_pool_size = 8589934592;  -- 8 * 1024 * 1024 * 1024

Nguyên tắc sizing:

  • Thông thường, buffer pool nên chiếm 70-80% available memory trên dedicated database server
  • Trên shared server, cần cân nhắc với các ứng dụng khác
  • Không nên quá lớn để tránh swapping
  1. Buffer Pool Instances:
-- Kiểm tra số lượng buffer pool instances
SHOW VARIABLES LIKE 'innodb_buffer_pool_instances';

-- Thiết lập số lượng instances (ví dụ: 8)
SET GLOBAL innodb_buffer_pool_instances = 8;

Nguyên tắc:

  • Mỗi instance quản lý một phần riêng biệt của buffer pool
  • Giảm contention trên các hệ thống nhiều CPU
  • Thông thường, 1GB buffer pool size cho mỗi instance là hợp lý
  1. Monitoring Buffer Pool:
-- Kiểm tra buffer pool stats
SHOW ENGINE INNODB STATUS\G

-- Kiểm tra buffer pool hit ratio
SELECT (1 - (
    SELECT variable_value FROM performance_schema.global_status WHERE variable_name = 'Innodb_buffer_pool_reads'
) / (
    SELECT variable_value FROM performance_schema.global_status WHERE variable_name = 'Innodb_buffer_pool_read_requests'
)) * 100 AS buffer_pool_hit_ratio;

Một buffer pool hit ratio > 95% là tốt. Nếu thấp hơn, cân nhắc tăng kích thước buffer pool.

InnoDB Log Buffer và Redo Logs

InnoDB sử dụng write-ahead logging để đảm bảo durability. Hiểu và tối ưu các thành phần này rất quan trọng:

  1. Log Buffer Size:
-- Kiểm tra log buffer size
SHOW VARIABLES LIKE 'innodb_log_buffer_size';

-- Thiết lập log buffer size (ví dụ: 16MB)
SET GLOBAL innodb_log_buffer_size = 16777216;  -- 16 * 1024 * 1024

Nguyên tắc:

  • Log buffer chứa các thay đổi trước khi ghi vào redo logs
  • 8-16MB là đủ cho hầu hết workloads
  • Tăng lên nếu có nhiều large transactions
  1. Redo Log File Size:
-- Kiểm tra redo log file size
SHOW VARIABLES LIKE 'innodb_log_file_size';

-- Thiết lập redo log file size (ví dụ: 1GB)
SET GLOBAL innodb_log_file_size = 1073741824;  -- 1 * 1024 * 1024 * 1024

Nguyên tắc:

  • Redo logs lớn hơn cho phép large transactions và giảm disk I/O
  • Tuy nhiên, recovery time sẽ lâu hơn sau crash
  • Thông thường, 0.25-1GB là hợp lý cho mỗi log file
  1. Flush Method:
-- Kiểm tra flush method
SHOW VARIABLES LIKE 'innodb_flush_method';

-- Thiết lập flush method
SET GLOBAL innodb_flush_method = 'O_DIRECT';

Các options phổ biến:

  • O_DIRECT: Bypass OS cache, phù hợp khi buffer pool đã lớn
  • fsync: Mặc định, sử dụng OS cache
  • O_DSYNC: Sync redo logs nhưng không sync data files

InnoDB Transaction và Concurrency

  1. Transaction Isolation Level:
-- Kiểm tra isolation level mặc định
SHOW VARIABLES LIKE 'transaction_isolation';

-- Thiết lập isolation level
SET GLOBAL transaction_isolation = 'READ-COMMITTED';

Nguyên tắc:

  • REPEATABLE-READ: Mặc định, cân bằng tốt giữa consistency và performance
  • READ-COMMITTED: Hiệu năng tốt hơn, phù hợp cho nhiều ứng dụng OLTP
  • READ-UNCOMMITTED: Hiệu năng cao nhất nhưng ít đảm bảo nhất
  • SERIALIZABLE: Đảm bảo nhất nhưng hiệu năng thấp nhất
  1. Innodb Thread Concurrency:
-- Kiểm tra thread concurrency
SHOW VARIABLES LIKE 'innodb_thread_concurrency';

-- Thiết lập thread concurrency
SET GLOBAL innodb_thread_concurrency = 0;  -- 0 = unlimited

Nguyên tắc:

  • 0 (unlimited): Phù hợp cho hầu hết hệ thống hiện đại
  • Với hệ thống cũ hơn, có thể giới hạn bằng 2 * (số CPU cores) + (số disk spindles)
  1. Deadlock Detection:
-- Kiểm tra deadlock detection
SHOW VARIABLES LIKE 'innodb_deadlock_detect';

-- Bật deadlock detection
SET GLOBAL innodb_deadlock_detect = ON;

Nguyên tắc:

  • Thường nên bật để tự động phát hiện và xử lý deadlocks
  • Có thể tắt trong trường hợp đặc biệt với high concurrency và nhiều row locks

MySQL Replication

Replication là kỹ thuật quan trọng để cải thiện availability và scalability:


  graph TD
    A[Primary Server] -->|Binary Logs| B[Replica 1]
    A -->|Binary Logs| C[Replica 2]
    A -->|Binary Logs| D[Replica N]

    E[Write Traffic] --> A
    F[Read Traffic] --> B
    F --> C
    F --> D
  1. Binary Log Format:
-- Kiểm tra binary log format
SHOW VARIABLES LIKE 'binlog_format';

-- Thiết lập binary log format
SET GLOBAL binlog_format = 'ROW';

Các options:

  • STATEMENT: Ghi lại các SQL statements
  • ROW: Ghi lại các thay đổi ở mức row (an toàn nhất)
  • MIXED: Kết hợp cả hai
  1. Semi-Synchronous Replication:
-- Trên Primary Server
INSTALL PLUGIN rpl_semi_sync_master SONAME 'semisync_master.so';
SET GLOBAL rpl_semi_sync_master_enabled = 1;
SET GLOBAL rpl_semi_sync_master_timeout = 10000;  -- 10 seconds

-- Trên Replica Servers
INSTALL PLUGIN rpl_semi_sync_slave SONAME 'semisync_slave.so';
SET GLOBAL rpl_semi_sync_slave_enabled = 1;
  1. Group Replication (MySQL 5.7+):

Group Replication cung cấp high availability với automatic failover:

-- Cấu hình Group Replication
SET GLOBAL group_replication_group_name = 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee';
SET GLOBAL group_replication_bootstrap_group = ON;
START GROUP_REPLICATION;
SET GLOBAL group_replication_bootstrap_group = OFF;
  1. Monitoring Replication:
-- Trên Replica Server
SHOW SLAVE STATUS\G

-- Kiểm tra replication lag
SELECT
    CHANNEL_NAME,
    SERVICE_STATE,
    ROUND(TIME_TO_SEC(TIMEDIFF(NOW(), LAST_APPLIED_TRANSACTION_END_APPLY_TIMESTAMP))) AS lag_seconds
FROM performance_schema.replication_applier_status_by_worker;

MySQL Partitioning

Partitioning giúp quản lý các bảng lớn bằng cách chia thành các phần nhỏ hơn:

  1. Range Partitioning:
-- Partitioning theo date range
CREATE TABLE orders (
    id INT NOT NULL,
    customer_id INT,
    order_date DATE,
    total_amount DECIMAL(10,2),
    PRIMARY KEY (id, order_date)
) PARTITION BY RANGE (YEAR(order_date)) (
    PARTITION p2021 VALUES LESS THAN (2022),
    PARTITION p2022 VALUES LESS THAN (2023),
    PARTITION p2023 VALUES LESS THAN (2024),
    PARTITION future VALUES LESS THAN MAXVALUE
);
  1. List Partitioning:
-- Partitioning theo category
CREATE TABLE products (
    id INT NOT NULL,
    name VARCHAR(255),
    category_id INT,
    price DECIMAL(10,2),
    PRIMARY KEY (id, category_id)
) PARTITION BY LIST (category_id) (
    PARTITION p_electronics VALUES IN (1, 2, 3),
    PARTITION p_clothing VALUES IN (4, 5),
    PARTITION p_home VALUES IN (6, 7, 8, 9),
    PARTITION p_others VALUES IN (10, 11, 12)
);
  1. Hash Partitioning:
-- Partitioning theo hash của customer_id
CREATE TABLE customer_transactions (
    id INT NOT NULL,
    customer_id INT,
    transaction_date DATE,
    amount DECIMAL(10,2),
    PRIMARY KEY (id, customer_id)
) PARTITION BY HASH (customer_id)
PARTITIONS 8;
  1. Partition Maintenance:
-- Thêm partition mới
ALTER TABLE orders ADD PARTITION (PARTITION p2024 VALUES LESS THAN (2025));

-- Xóa partition
ALTER TABLE orders DROP PARTITION p2021;

-- Reorganize partitions
ALTER TABLE orders REORGANIZE PARTITION p2022, p2023 INTO (
    PARTITION p2022_2023 VALUES LESS THAN (2024)
);

MySQL Performance Schema và Monitoring

Performance Schema là công cụ mạnh mẽ để giám sát và phân tích hiệu năng MySQL:

  1. Bật Performance Schema:
-- Kiểm tra trạng thái
SHOW VARIABLES LIKE 'performance_schema';

-- Bật Performance Schema (trong my.cnf)
-- [mysqld]
-- performance_schema = ON
  1. Phân tích slow queries:
-- Bật slow query log
SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 1;  -- Log queries slower than 1 second

-- Phân tích slow queries từ Performance Schema
SELECT
    DIGEST_TEXT AS query,
    COUNT_STAR AS exec_count,
    SUM_TIMER_WAIT/1000000000 AS total_exec_time_ms,
    AVG_TIMER_WAIT/1000000000 AS avg_exec_time_ms,
    SUM_ROWS_SENT AS rows_sent,
    SUM_ROWS_EXAMINED AS rows_examined
FROM performance_schema.events_statements_summary_by_digest
ORDER BY avg_exec_time_ms DESC
LIMIT 10;
  1. Phân tích table I/O:
-- Phân tích table I/O
SELECT
    object_schema AS database_name,
    object_name AS table_name,
    count_read AS read_count,
    count_write AS write_count,
    count_fetch AS fetch_count,
    count_insert AS insert_count,
    count_update AS update_count,
    count_delete AS delete_count
FROM performance_schema.table_io_waits_summary_by_table
ORDER BY (count_read + count_write) DESC
LIMIT 10;
  1. Phân tích index usage:
-- Phân tích index usage
SELECT
    object_schema AS database_name,
    object_name AS table_name,
    index_name,
    count_fetch,
    count_insert,
    count_update,
    count_delete
FROM performance_schema.table_io_waits_summary_by_index_usage
ORDER BY count_fetch DESC
LIMIT 10;

PostgreSQL: VACUUM, statistics collector, parallel queries, extensions

PostgreSQL có kiến trúc và tính năng khác với MySQL. Hãy tìm hiểu cách tối ưu PostgreSQL.

PostgreSQL Architecture


  graph LR
    A[PostgreSQL Server] --> B[Shared Buffers]
    A --> C[WAL Buffer]
    A --> D[Background Writer]
    A --> E[Autovacuum]
    A --> F[Statistics Collector]

    B --> G[Data Pages]
    C --> H[Write-Ahead Log]
    D --> I[Dirty Pages]
    E --> J[VACUUM Process]
    F --> K[pg_stat tables]

Shared Buffers và Memory Configuration

Shared buffers là vùng bộ nhớ chính của PostgreSQL, tương tự như buffer pool trong MySQL:

  1. Shared Buffers Size:
-- Kiểm tra shared buffers size
SHOW shared_buffers;

-- Thiết lập trong postgresql.conf
-- shared_buffers = 2GB

Nguyên tắc:

  • Thông thường 25% của system memory là hợp lý
  • Không nên quá lớn vì PostgreSQL cũng sử dụng OS cache
  1. Work Memory và Maintenance Work Memory:
-- Kiểm tra work_mem và maintenance_work_mem
SHOW work_mem;
SHOW maintenance_work_mem;

-- Thiết lập trong postgresql.conf
-- work_mem = 32MB
-- maintenance_work_mem = 256MB

Nguyên tắc:

  • work_mem: Bộ nhớ cho mỗi sort/hash operation
  • maintenance_work_mem: Bộ nhớ cho các operations như VACUUM, CREATE INDEX
  1. Effective Cache Size:
-- Kiểm tra effective_cache_size
SHOW effective_cache_size;

-- Thiết lập trong postgresql.conf
-- effective_cache_size = 6GB

Nguyên tắc:

  • Ước tính tổng bộ nhớ available cho PostgreSQL (shared_buffers + OS cache)
  • Thường là 50-75% của system memory

VACUUM và Autovacuum

PostgreSQL sử dụng MVCC (Multi-Version Concurrency Control), cần quá trình VACUUM để dọn dẹp dead tuples:

  1. Autovacuum Configuration:
-- Kiểm tra autovacuum settings
SHOW autovacuum;
SHOW autovacuum_vacuum_threshold;
SHOW autovacuum_vacuum_scale_factor;

-- Thiết lập trong postgresql.conf
-- autovacuum = on
-- autovacuum_vacuum_threshold = 50
-- autovacuum_vacuum_scale_factor = 0.1
-- autovacuum_naptime = 1min

Nguyên tắc:

  • autovacuum_vacuum_threshold: Số lượng tuples thay đổi tối thiểu để trigger vacuum
  • autovacuum_vacuum_scale_factor: Phần trăm của table size để trigger vacuum
  • Bảng lớn nên có scale_factor nhỏ hơn
  1. Manual VACUUM:
-- VACUUM cơ bản
VACUUM my_table;

-- VACUUM ANALYZE (cập nhật statistics)
VACUUM ANALYZE my_table;

-- VACUUM FULL (reclaim space, nhưng lock table)
VACUUM FULL my_table;
  1. Monitoring VACUUM:
-- Kiểm tra dead tuples
SELECT relname, n_dead_tup, n_live_tup,
       (n_dead_tup::float / (n_live_tup + n_dead_tup) * 100)::numeric(10,2) AS dead_percentage
FROM pg_stat_user_tables
ORDER BY dead_percentage DESC;

-- Kiểm tra autovacuum activity
SELECT datname, usename, query
FROM pg_stat_activity
WHERE query LIKE '%autovacuum%';

Write-Ahead Log (WAL) Configuration

WAL đảm bảo durability và được sử dụng cho replication:

  1. WAL Settings:
-- Kiểm tra WAL settings
SHOW wal_level;
SHOW max_wal_size;
SHOW min_wal_size;

-- Thiết lập trong postgresql.conf
-- wal_level = replica
-- max_wal_size = 1GB
-- min_wal_size = 80MB

Nguyên tắc:

  • wal_level: minimal (ít thông tin nhất), replica (đủ cho replication), logical (cho logical replication)
  • max_wal_size: Kích thước tối đa trước khi checkpoint
  • min_wal_size: Kích thước tối thiểu để giữ lại
  1. Checkpoint Settings:
-- Kiểm tra checkpoint settings
SHOW checkpoint_timeout;
SHOW checkpoint_completion_target;

-- Thiết lập trong postgresql.conf
-- checkpoint_timeout = 5min
-- checkpoint_completion_target = 0.9

Nguyên tắc:

  • checkpoint_timeout: Thời gian giữa các checkpoints
  • checkpoint_completion_target: Phân phối checkpoint I/O trong khoảng thời gian (0.9 = 90% của checkpoint_timeout)

PostgreSQL Statistics Collector

Statistics collector giúp query planner đưa ra quyết định tốt hơn:

  1. Statistics Settings:
-- Kiểm tra statistics settings
SHOW track_activities;
SHOW track_counts;
SHOW track_io_timing;

-- Thiết lập trong postgresql.conf
-- track_activities = on
-- track_counts = on
-- track_io_timing = on
  1. ANALYZE Command:
-- ANALYZE một bảng cụ thể
ANALYZE my_table;

-- ANALYZE toàn bộ database
ANALYZE;
  1. Monitoring Table Statistics:
-- Kiểm tra table statistics
SELECT relname, last_analyze, last_autoanalyze,
       n_live_tup, n_dead_tup, n_mod_since_analyze
FROM pg_stat_user_tables;

-- Kiểm tra index usage
SELECT relname, indexrelname, idx_scan, idx_tup_read, idx_tup_fetch
FROM pg_stat_user_indexes
ORDER BY idx_scan DESC;

PostgreSQL Parallel Queries

PostgreSQL hỗ trợ parallel queries để tận dụng nhiều CPU cores:

  1. Parallel Query Settings:
-- Kiểm tra parallel query settings
SHOW max_parallel_workers_per_gather;
SHOW max_parallel_workers;
SHOW parallel_setup_cost;
SHOW parallel_tuple_cost;

-- Thiết lập trong postgresql.conf
-- max_parallel_workers_per_gather = 4
-- max_parallel_workers = 8

Nguyên tắc:

  • max_parallel_workers_per_gather: Số workers tối đa cho mỗi node
  • max_parallel_workers: Tổng số workers tối đa
  1. Forcing Parallel Queries:
-- Tạm thời thay đổi parallel settings cho session
SET max_parallel_workers_per_gather = 4;

-- Kiểm tra execution plan với parallel workers
EXPLAIN SELECT * FROM large_table WHERE column1 > 1000;
  1. Monitoring Parallel Queries:
-- Kiểm tra parallel queries đang chạy
SELECT query, parallel_workers
FROM pg_stat_activity
WHERE parallel_workers > 0;

PostgreSQL Extensions

PostgreSQL có hệ thống extensions mạnh mẽ, cung cấp nhiều tính năng bổ sung:

  1. pg_stat_statements: Tracking query performance
-- Cài đặt extension
CREATE EXTENSION pg_stat_statements;

-- Thiết lập trong postgresql.conf
shared_preload_libraries = 'pg_stat_statements'
pg_stat_statements.track = all

-- Kiểm tra slow queries
SELECT
    substring(query, 1, 50) AS short_query,
    round(total_time::numeric, 2) AS total_time,
    calls,
    round(mean_time::numeric, 2) AS mean_time,
    round((100 * total_time / sum(total_time::numeric) OVER ())::numeric, 2) AS percentage
FROM pg_stat_statements
ORDER BY total_time DESC
LIMIT 10;
  1. pg_repack: Reorganize tables without locks
-- Cài đặt extension (cần cài đặt từ package trước)
-- apt-get install postgresql-12-repack (Ubuntu/Debian)
-- yum install postgresql12-repack (RHEL/CentOS)
CREATE EXTENSION pg_repack;

-- Sử dụng command line để repack table mà không cần lock
pg_repack -d mydb -t mytable

-- Repack toàn bộ database
pg_repack -d mydb --all
  1. TimescaleDB: Time-series data optimization
-- Cài đặt extension (cần cài đặt package trước)
CREATE EXTENSION timescaledb;

-- Tạo hypertable
CREATE TABLE metrics (
    time TIMESTAMPTZ NOT NULL,
    device_id INT,
    temperature FLOAT,
    humidity FLOAT
);

-- Chuyển đổi thành hypertable với chunk interval 1 ngày
SELECT create_hypertable('metrics', 'time', chunk_time_interval => INTERVAL '1 day');

-- Query hiệu quả với time range
SELECT time_bucket('15 minutes', time) AS interval,
       avg(temperature) AS avg_temp,
       max(temperature) AS max_temp
FROM metrics
WHERE time > NOW() - INTERVAL '1 day'
  AND device_id = 42
GROUP BY interval
ORDER BY interval;
  1. PostGIS: Spatial data support
-- Cài đặt extension
CREATE EXTENSION postgis;

-- Tạo spatial table
CREATE TABLE locations (
    id SERIAL PRIMARY KEY,
    name VARCHAR(100),
    location GEOMETRY(Point, 4326)
);

-- Thêm spatial index
CREATE INDEX idx_locations_location ON locations USING GIST(location);

-- Spatial query tìm địa điểm trong bán kính 5km
SELECT name
FROM locations
WHERE ST_DWithin(
    location,
    ST_SetSRID(ST_MakePoint(10.762622, 106.660172), 4326),
    5000  -- 5km
);
  1. pg_partman: Quản lý partitioning tự động
-- Cài đặt extension
CREATE EXTENSION pg_partman;

-- Tạo table cần partition
CREATE TABLE sales (
    sale_date TIMESTAMP NOT NULL,
    product_id INT,
    amount DECIMAL(10,2)
) PARTITION BY RANGE (sale_date);

-- Thiết lập partitioning tự động theo tháng
SELECT partman.create_parent(
    'public.sales',
    'sale_date',
    'month',
    p_start_date := '2023-01-01'
);

-- Tạo job tự động quản lý partitions
SELECT cron.schedule(
    'sales_partition_maintenance',
    '0 1 * * *',  -- Chạy lúc 1 giờ sáng mỗi ngày
    $$SELECT partman.run_maintenance(p_analyze := true)$$
);

Query cache và memory management

MySQL Query Cache

MySQL có built-in query cache, nhưng đã bị deprecated từ MySQL 8.0:

-- Kiểm tra query cache (MySQL < 8.0)
SHOW VARIABLES LIKE 'query_cache%';

-- Bật query cache
SET GLOBAL query_cache_type = 1;
SET GLOBAL query_cache_size = 67108864;  -- 64MB

Lưu ý:

  • Query cache có thể gây contention trong workloads write-heavy
  • Thay vì query cache, nên sử dụng application-level caching (Redis, Memcached)

PostgreSQL Query Plan Cache

PostgreSQL không có query cache như MySQL, nhưng có prepared statement cache:

-- Kiểm tra prepared statement settings
SHOW shared_preload_libraries;

-- Sử dụng pg_prewarm để load data vào shared buffers
CREATE EXTENSION pg_prewarm;
SELECT pg_prewarm('my_table');

Memory Management Best Practices

  1. Monitoring Memory Usage:
-- MySQL: Kiểm tra memory usage
SELECT
    event_name,
    current_alloc AS current_bytes,
    current_alloc/1024/1024 AS current_mb
FROM performance_schema.memory_summary_global_by_event_name
WHERE event_name LIKE 'memory/innodb/%'
ORDER BY current_bytes DESC
LIMIT 10;

-- PostgreSQL: Kiểm tra memory usage
SELECT
    datname,
    pg_size_pretty(pg_database_size(datname)) AS size
FROM pg_database;
  1. Avoiding Memory Pressure:
  • Giám sát swap usage
  • Đảm bảo tổng memory allocation không vượt quá physical memory
  • Cân nhắc sử dụng containerization (Docker, Kubernetes) với memory limits rõ ràng
# Linux: Kiểm tra memory usage
free -m

# Kiểm tra swap activity
vmstat 1

Cấu hình hệ thống phù hợp với workload

Phân tích workload

Trước khi tối ưu, cần hiểu rõ workload của ứng dụng:

  1. Read vs Write Ratio:

    • Read-heavy: Tối ưu cho read performance, sử dụng read replicas
    • Write-heavy: Tối ưu cho write performance, cân nhắc sharding
    • Mixed: Cân bằng giữa read và write
  2. Query Patterns:

    • OLTP (Online Transaction Processing): Nhiều transactions nhỏ, concurrent
    • OLAP (Online Analytical Processing): Ít transactions lớn, phức tạp
    • Mixed: Cân nhắc separation of concerns
  3. Data Size và Growth Rate:

    • Small: Có thể fit vào memory
    • Medium: Cần indexing và caching hiệu quả
    • Large: Cần partitioning, sharding, archiving

Cấu hình cho OLTP Workloads

OLTP workloads đặc trưng bởi nhiều transactions nhỏ, concurrent:

  1. MySQL Configuration:
# my.cnf for OLTP
innodb_buffer_pool_size = 70% of RAM
innodb_log_file_size = 256M
innodb_flush_log_at_trx_commit = 1  # Full ACID
max_connections = 500
table_open_cache = 4000
innodb_flush_method = O_DIRECT
  1. PostgreSQL Configuration:
# postgresql.conf for OLTP
shared_buffers = 25% of RAM
work_mem = 32MB
maintenance_work_mem = 256MB
effective_cache_size = 70% of RAM
random_page_cost = 1.1  # If using SSD
checkpoint_timeout = 10min
max_connections = 300

Cấu hình cho OLAP Workloads

OLAP workloads đặc trưng bởi ít queries phức tạp, xử lý nhiều dữ liệu:

  1. MySQL Configuration (tiếp):
# my.cnf for OLAP
sort_buffer_size = 64M
read_buffer_size = 16M
read_rnd_buffer_size = 32M
tmp_table_size = 256M
max_heap_table_size = 256M
innodb_io_capacity = 2000  # Nếu sử dụng SSD
innodb_io_capacity_max = 4000
  1. PostgreSQL Configuration:
# postgresql.conf for OLAP
shared_buffers = 25% of RAM
work_mem = 128MB  # Cao hơn cho complex joins và sorts
maintenance_work_mem = 512MB
effective_cache_size = 70% of RAM
max_parallel_workers_per_gather = 8  # Tận dụng parallel queries
max_parallel_workers = 16
max_worker_processes = 16
random_page_cost = 1.1  # Nếu sử dụng SSD
checkpoint_timeout = 15min
autovacuum_vacuum_scale_factor = 0.05  # Aggressive vacuum

Cấu hình cho Mixed Workloads

Nhiều ứng dụng có cả OLTP và OLAP workloads:

  1. Separation of Concerns:

  graph TD
    A[Application] --> B[Router/Load Balancer]
    B -->|OLTP Queries| C[OLTP Database<br>Primary]
    B -->|Read Queries| D[Read Replicas]
    B -->|OLAP Queries| E[OLAP Database<br>Data Warehouse]
    C -->|Replication| D
    C -->|ETL/CDC| E
  1. MySQL Configuration:
# my.cnf for Mixed Workload
innodb_buffer_pool_size = 70% of RAM
innodb_log_file_size = 512M
innodb_flush_log_at_trx_commit = 1
join_buffer_size = 16M
sort_buffer_size = 32M
tmp_table_size = 128M
max_heap_table_size = 128M
  1. PostgreSQL Configuration:
# postgresql.conf for Mixed Workload
shared_buffers = 25% of RAM
work_mem = 64MB
maintenance_work_mem = 256MB
effective_cache_size = 70% of RAM
max_parallel_workers_per_gather = 4
max_parallel_workers = 8

Cấu hình cho High-Concurrency Workloads

Ứng dụng với nhiều người dùng đồng thời cần cấu hình đặc biệt:

  1. Connection Pooling:

  graph TD
    A[Applications] --> B[Connection Pooler<br>PgBouncer/ProxySQL]
    B --> C[Database Server]

    style B fill:#f9f,stroke:#333,stroke-width:2px
  1. MySQL Configuration:
# my.cnf for High Concurrency
max_connections = 1000
thread_cache_size = 100
innodb_thread_concurrency = 0  # Unlimited
table_open_cache = 8000
table_definition_cache = 4000
open_files_limit = 65535
  1. PostgreSQL Configuration:
# postgresql.conf for High Concurrency
max_connections = 500  # Với PgBouncer, có thể giảm xuống
shared_buffers = 25% of RAM
work_mem = 16MB  # Nhỏ hơn vì nhiều connections
maintenance_work_mem = 256MB
  1. PgBouncer Configuration (PostgreSQL):
# pgbouncer.ini
[databases]
* = host=127.0.0.1 port=5432

[pgbouncer]
listen_addr = *
listen_port = 6432
auth_type = md5
auth_file = /etc/pgbouncer/userlist.txt
pool_mode = transaction
max_client_conn = 10000
default_pool_size = 100
  1. ProxySQL Configuration (MySQL):
# proxysql.cnf
mysql_servers =
(
    {
        address="primary.example.com"
        port=3306
        hostgroup=0
        max_connections=300
    },
    {
        address="replica1.example.com"
        port=3306
        hostgroup=1
        max_connections=300
    },
    {
        address="replica2.example.com"
        port=3306
        hostgroup=1
        max_connections=300
    }
)

# Query routing rules
mysql_query_rules =
(
    {
        rule_id=1
        active=1
        match_pattern="^SELECT .* FOR UPDATE"
        destination_hostgroup=0
        apply=1
    },
    {
        rule_id=2
        active=1
        match_pattern="^SELECT .* FROM products"
        destination_hostgroup=1
        apply=1
    },
    {
        rule_id=3
        active=1
        match_pattern="^SELECT"
        destination_hostgroup=1
        apply=1
    }
)

Case Study: Tối ưu hóa MySQL cho ứng dụng e-commerce

Hãy xem xét một case study về tối ưu hóa MySQL cho một ứng dụng e-commerce với lưu lượng truy cập cao:

Vấn đề ban đầu

Một ứng dụng e-commerce đang gặp các vấn đề hiệu năng:

  • Trang sản phẩm tải chậm trong giờ cao điểm
  • Checkout process thường xuyên timeout
  • Database CPU usage cao (>90%)
  • Slow queries log đầy các queries chậm
  • Replication lag cao (>30 giây)

Cấu hình hiện tại:

  • MySQL 5.7
  • 16GB RAM server
  • 8 CPU cores
  • SSD storage
  • Mặc định configuration

Phân tích vấn đề

  1. Phân tích slow queries:
-- Sử dụng pt-query-digest để phân tích slow query log
pt-query-digest /var/log/mysql/slow-query.log

-- Kết quả cho thấy:
-- 1. Product search queries không sử dụng index hiệu quả
-- 2. Cart update queries gây lock contention
-- 3. Order history queries quá phức tạp
  1. Phân tích system resources:
# Kiểm tra CPU usage
top

# Kiểm tra memory usage
free -m

# Kiểm tra disk I/O
iostat -x 1

# Kết quả cho thấy:
# - CPU: 95% utilization
# - Memory: Buffer pool không đủ lớn
# - Disk: High IOPS, nhưng không saturated
  1. Phân tích MySQL metrics:
-- Kiểm tra buffer pool hit ratio
SELECT (1 - (
    SELECT variable_value FROM performance_schema.global_status WHERE variable_name = 'Innodb_buffer_pool_reads'
) / (
    SELECT variable_value FROM performance_schema.global_status WHERE variable_name = 'Innodb_buffer_pool_read_requests'
)) * 100 AS buffer_pool_hit_ratio;
-- Kết quả: 85% (thấp hơn mức tối ưu 95%)

-- Kiểm tra thread usage
SHOW GLOBAL STATUS LIKE 'Threads_%';
-- Kết quả: Threads_connected thường xuyên gần max_connections

Giải pháp tối ưu

  1. Tối ưu Buffer Pool và Memory:
# Tăng buffer pool size
innodb_buffer_pool_size = 10G  # 60% của 16GB RAM
innodb_buffer_pool_instances = 8  # Mỗi instance 1.25GB

# Tối ưu log buffer
innodb_log_buffer_size = 32M
innodb_log_file_size = 512M

# Tối ưu các buffers khác
join_buffer_size = 8M
sort_buffer_size = 8M
read_buffer_size = 4M
read_rnd_buffer_size = 8M
  1. Tối ưu Concurrency và Connections:
# Tăng connection limits
max_connections = 500
thread_cache_size = 100

# Tối ưu concurrency
innodb_thread_concurrency = 16  # 2x số CPU cores
innodb_concurrency_tickets = 5000
innodb_thread_sleep_delay = 10000
  1. Tối ưu I/O và Durability:
# Tối ưu I/O
innodb_io_capacity = 2000  # Cho SSD
innodb_io_capacity_max = 4000
innodb_flush_method = O_DIRECT
innodb_flush_neighbors = 0  # Disable cho SSD

# Cân bằng durability và performance
innodb_flush_log_at_trx_commit = 1  # Full ACID
sync_binlog = 1  # Full durability cho binary log
  1. Triển khai Connection Pooling với ProxySQL:

  graph TD
    A[Web Servers] --> B[ProxySQL]
    B -->|Write Queries| C[Primary MySQL]
    B -->|Read Queries| D[Read Replica 1]
    B -->|Read Queries| E[Read Replica 2]
    C -->|Replication| D
    C -->|Replication| E
# ProxySQL configuration
mysql_servers =
(
    {
        address="primary.example.com"
        port=3306
        hostgroup=0
        max_connections=300
    },
    {
        address="replica1.example.com"
        port=3306
        hostgroup=1
        max_connections=300
    },
    {
        address="replica2.example.com"
        port=3306
        hostgroup=1
        max_connections=300
    }
)

# Query routing rules
mysql_query_rules =
(
    {
        rule_id=1
        active=1
        match_pattern="^SELECT .* FOR UPDATE"
        destination_hostgroup=0
        apply=1
    },
    {
        rule_id=2
        active=1
        match_pattern="^SELECT .* FROM products"
        destination_hostgroup=1
        apply=1
    },
    {
        rule_id=3
        active=1
        match_pattern="^SELECT"
        destination_hostgroup=1
        apply=1
    }
)

Kết quả sau tối ưu

Sau khi áp dụng các giải pháp tối ưu, hệ thống đã cải thiện đáng kể:

  • CPU usage: Giảm từ 95% xuống 60% trung bình
  • Buffer pool hit ratio: Tăng từ 85% lên 98%
  • Response time: Giảm 70% cho các trang sản phẩm
  • Checkout success rate: Tăng từ 92% lên 99.5%
  • Replication lag: Giảm từ >30 giây xuống <1 giây
-- Kiểm tra buffer pool hit ratio sau tối ưu
SELECT (1 - (
    SELECT variable_value FROM performance_schema.global_status WHERE variable_name = 'Innodb_buffer_pool_reads'
) / (
    SELECT variable_value FROM performance_schema.global_status WHERE variable_name = 'Innodb_buffer_pool_read_requests'
)) * 100 AS buffer_pool_hit_ratio;
-- Kết quả: 98% (đạt mức tối ưu)

Các công cụ giám sát và tối ưu hóa database

Để duy trì hiệu năng tối ưu, cần sử dụng các công cụ giám sát và tối ưu hóa:

Công cụ cho MySQL/MariaDB

  1. Percona Monitoring and Management (PMM):

    • Giám sát toàn diện MySQL/MariaDB
    • Dashboard trực quan cho Query Analytics, System Metrics
    • Cài đặt: docker run -d -p 80:80 -p 443:443 percona/pmm-server:latest
  2. MySQL Enterprise Monitor:

    • Giải pháp giám sát chính thức từ Oracle
    • Query Analyzer và Advisors tự động
    • Cảnh báo và báo cáo hiệu năng
  3. Percona Toolkit:

    • Bộ công cụ command-line cho DBA
    • pt-query-digest: Phân tích slow query log
    • pt-online-schema-change: Schema changes không downtime

Công cụ cho PostgreSQL

  1. pgAdmin:

    • GUI phổ biến nhất cho PostgreSQL
    • Query tool, explain analyzer
    • Server monitoring dashboard
  2. pg_stat_statements:

    • Extension theo dõi query performance
    • Cài đặt: CREATE EXTENSION pg_stat_statements;
  3. pgBadger:

    • Log analyzer cho PostgreSQL
    • Báo cáo chi tiết về slow queries, errors
    • Cài đặt: apt-get install pgbadger
  4. Patroni:

    • High-availability solution cho PostgreSQL
    • Automated failover, replication management
    • Tích hợp với etcd, Consul, ZooKeeper

Kết luận

Tối ưu hóa cơ sở dữ liệu SQL là một quá trình liên tục đòi hỏi sự hiểu biết sâu sắc về kiến trúc hệ thống, workload, và các tham số cấu hình. Trong bài viết này, chúng ta đã khám phá các kỹ thuật tối ưu hóa cho MySQL/MariaDB và PostgreSQL, từ cấu hình cơ bản đến các chiến lược nâng cao.

Những điểm quan trọng cần nhớ:

  1. Hiểu rõ workload: Phân biệt giữa OLTP, OLAP, và mixed workloads để có chiến lược tối ưu phù hợp

  2. Tối ưu memory: Buffer pool (MySQL) và shared buffers (PostgreSQL) là thành phần quan trọng nhất ảnh hưởng đến hiệu năng

  3. Giám sát liên tục: Sử dụng Performance Schema, pg_stat_statements và các công cụ giám sát để phát hiện vấn đề sớm

  4. Cân bằng giữa durability và performance: Điều chỉnh các tham số như innodb_flush_log_at_trx_commit, sync_binlog (MySQL) hoặc synchronous_commit (PostgreSQL) dựa trên yêu cầu

  5. Scale horizontally: Sử dụng replication, sharding, và connection pooling để xử lý tải cao

Trong các bài tiếp theo, chúng ta sẽ tìm hiểu về tối ưu hóa cho NoSQL databases và các chiến lược monitoring, troubleshooting cơ sở dữ liệu chuyên sâu.