Site logo
Tác giả
  • avatar Nguyễn Đức Xinh
    Name
    Nguyễn Đức Xinh
    Twitter
Ngày xuất bản
Ngày xuất bản

Cấu Hình Query Log Và Slow Query Log Trong MySQL: Server Tự Cài Và AWS RDS

Giới Thiệu

Bài Index trong MySQLTối ưu query với index đã chỉ ra cách dùng EXPLAIN/EXPLAIN ANALYZE để tìm điểm nghẽn khi bạn đã biết câu query nào đang chậm. Nhưng có một câu hỏi xuất hiện trước đó, thường xuyên hơn nhiều: làm sao biết câu query nào đang chạy, và câu nào đang chậm — khi bạn không có sẵn log của application, hoặc nghi ngờ có một job/cron/API nào đó đang âm thầm bắn ra query nặng?

Câu trả lời nằm ở hai loại log có sẵn trong MySQL:

  • General Query Log: ghi lại mọi câu lệnh SQL server nhận được — dùng để audit, debug "ai đang query gì", nhưng chi phí hiệu năng cao nếu bật lâu dài.
  • Slow Query Log: chỉ ghi lại những câu lệnh chạy chậm hơn một ngưỡng thời gian — chi phí thấp hơn nhiều, phù hợp để bật thường trực trên production.

Bài này tập trung vào phần thực dụng nhất: cách bật/tắt, cách chọn nơi xuất log (file hay table), và cách kiểm tra log — được trình bày song song cho hai kịch bản khác nhau hoàn toàn về vận hành:

  1. Server MySQL tự cài (self-managed): bạn có SSH, có quyền sửa my.cnf, có quyền đọc file trên đĩa.
  2. AWS RDS: MySQL được AWS quản lý, không có SSH, không có quyền sửa file cấu hình trực tiếp — mọi thay đổi tham số phải đi qua DB Parameter Group.

Đây là hai thế giới vận hành khác nhau, và một trong những lỗi phổ biến nhất của kỹ sư mới chuyển từ on-premise sang cloud là cố tìm file slow.log trên RDS — thứ không tồn tại theo cách bạn nghĩ.

Tổng Quan: General Query Log Và Slow Query Log Khác Nhau Thế Nào?

Tiêu chí General Query Log Slow Query Log
Ghi lại gì Tất cả câu lệnh SQL (SELECT, INSERT, UPDATE, connect, quit...) Chỉ câu lệnh có thời gian chạy > long_query_time
Chi phí hiệu năng Cao — mỗi query đều tốn thêm I/O ghi log Thấp — chỉ ghi khi vượt ngưỡng
Dùng khi nào Debug ngắn hạn: "app này đang gửi query gì xuống DB?" Bật thường trực trên production để phát hiện query chậm
Biến điều khiển bật/tắt general_log slow_query_log
Biến chọn nơi ghi log_output (FILE / TABLE / FILE,TABLE) log_output (dùng chung với General Query Log)
Khuyến nghị bật lâu dài? Không — chỉ bật tạm thời rồi tắt ngay Có — chi phí thấp, giá trị chẩn đoán cao

Điểm quan trọng cần nhớ: cả hai loại log dùng chung một biến log_output để quyết định ghi ra đâu. Đây là phần nhiều người bỏ qua, nên ta sẽ nói kỹ ở mục tiếp theo.

Hai Cách Xuất Log: FILE Và TABLE

MySQL cho phép chọn nơi ghi log qua biến log_output, với 3 giá trị:

SET GLOBAL log_output = 'FILE';        -- ghi ra file text trên đĩa (mặc định)
SET GLOBAL log_output = 'TABLE';       -- ghi vào bảng hệ thống mysql.general_log / mysql.slow_log
SET GLOBAL log_output = 'FILE,TABLE';  -- ghi ra cả hai (hiếm khi cần, tốn gấp đôi chi phí)
Tiêu chí log_output = FILE log_output = TABLE
Nơi lưu File text (general.log, slow.log) trên đĩa server Bảng mysql.general_log, mysql.slow_log (storage engine CSV)
Cách đọc tail -f, cat, công cụ phân tích như mysqldumpslow, pt-query-digest Truy vấn bằng SELECT ... WHERE ... như một bảng bình thường
Lọc theo điều kiện Phải dùng công cụ ngoài (grep, script) Dùng trực tiếp SQL: WHERE query_time > 5, WHERE db = 'shop'...
Hiệu năng ghi Nhanh hơn (append file tuần tự) Chậm hơn một chút (ghi vào bảng CSV, không có index)
Phù hợp với Server tự cài, có quyền truy cập file hệ thống AWS RDS (không có quyền đọc file trực tiếp), hoặc khi muốn SELECT nhanh mà không cần công cụ ngoài
Giới hạn Cần logrotate để tránh đầy đĩa Bảng CSV không có index → SELECT trên bảng lớn sẽ chậm, cần dọn dẹp định kỳ

Đây chính là lý do phần "dùng table" trong tiêu đề bài viết quan trọng: trên AWS RDS, vì không có SSH vào filesystem, log_output = TABLE gần như là cách duy nhất để bạn tự truy vấn log bằng SQL mà không cần tải file log về qua console.

Phần 1: Cấu Hình Trên Server MySQL Tự Cài

Bước 1: Kiểm Tra Trạng Thái Hiện Tại

Trước khi đổi bất cứ thứ gì, luôn kiểm tra cấu hình đang chạy:

SHOW VARIABLES LIKE 'general_log%';
SHOW VARIABLES LIKE 'slow_query_log%';
SHOW VARIABLES LIKE 'log_output';
SHOW VARIABLES LIKE 'long_query_time';

Kết quả mẫu:

+------------------+----------------------------------+
| Variable_name    | Value                              |
+------------------+----------------------------------+
| general_log      | OFF                                |
| general_log_file | /var/lib/mysql/hostname.log        |
| slow_query_log   | OFF                                |
| log_output       | FILE                                |
| long_query_time  | 10.000000                          |
+------------------+----------------------------------+

Bước 2: Bật General Query Log Bằng Cấu Hình Động (Không Cần Restart)

-- Chuyển output sang TABLE trước khi bật
SET GLOBAL log_output = 'TABLE';

-- Bật general query log
SET GLOBAL general_log = 'ON';

Chạy thử một vài câu lệnh SQL bất kỳ, rồi kiểm tra:

SELECT event_time, user_host, command_type, argument
FROM mysql.general_log
WHERE command_type = 'Query'
ORDER BY event_time DESC
LIMIT 200;

Giải thích:

  • SET GLOBAL thay đổi giá trị ngay lập tức cho toàn bộ session mới, không cần restart MySQL — điểm khác biệt lớn so với sửa my.cnf (chỉ có hiệu lực sau khi restart).
  • mysql.general_log là bảng hệ thống, dùng storage engine CSV — chỉ hỗ trợ SELECTTRUNCATE, không hỗ trợ UPDATE/DELETE từng dòng và không tạo được index.
  • command_type cho biết loại sự kiện ghi log, không chỉ riêng câu SQL — lọc WHERE command_type = 'Query' để chỉ lấy các câu lệnh SQL thực sự (SELECT/INSERT/UPDATE/DELETE...), bỏ qua các sự kiện quản lý kết nối gây nhiễu.

Các giá trị command_type thường gặp:

Giá trị Ý nghĩa
Query Một câu lệnh SQL thực thi trực tiếp (SELECT, INSERT, UPDATE, DELETE, DDL...) — giá trị cần lọc nhiều nhất khi audit query
Connect Một client vừa kết nối tới server (ghi kèm user/host)
Quit Client ngắt kết nối
Init DB Client chuyển database làm việc (lệnh USE database_name)
Prepare Chuẩn bị một prepared statement (PREPARE stmt FROM ...)
Execute Thực thi một prepared statement đã chuẩn bị trước đó
Close stmt Đóng một prepared statement
Field List Client yêu cầu danh sách cột của một bảng (thường do driver/ORM tự động gọi)
Statistics Client yêu cầu thông tin thống kê server (một số công cụ giám sát dùng lệnh này)

Trong thực tế, gần như mọi phân tích "app đang query gì" chỉ cần quan tâm command_type = 'Query'; các giá trị còn lại chủ yếu phục vụ debug ở tầng kết nối (connection pool đóng/mở liên tục, driver gọi Init DB/Field List trước mỗi lần thao tác...).

Bước 3: Bật Bằng File Cấu Hình my.cnf (Bền Vững Qua Restart)

Cấu hình động ở Bước 2 sẽ mất khi MySQL restart. Muốn log tự bật lại mỗi lần server khởi động, cấu hình vào file my.cnf (thường ở /etc/mysql/my.cnf hoặc /etc/my.cnf):

[mysqld]
general_log        = 1
general_log_file   = /var/log/mysql/general.log
log_output         = TABLE

Sau khi sửa, restart để áp dụng:

sudo systemctl restart mysql

Bước 4: Bật Slow Query Log

Slow Query Log là loại nên bật thường trực trên production vì chi phí thấp. Cấu hình động:

SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 1;              -- ngưỡng: query > 1 giây coi là chậm
SET GLOBAL log_queries_not_using_indexes = 'ON'; -- ghi cả query không dùng index, dù nhanh
SET GLOBAL log_output = 'TABLE';

Hoặc bền vững trong my.cnf:

[mysqld]
slow_query_log              = 1
slow_query_log_file         = /var/log/mysql/slow.log
long_query_time              = 1
log_queries_not_using_indexes = 1
log_output                   = TABLE

Giải thích các biến:

  • long_query_time: ngưỡng thời gian (đơn vị giây, hỗ trợ số thập phân như 0.5) — query chạy lâu hơn giá trị này mới bị ghi log.
  • log_queries_not_using_indexes: rất hữu ích khi audit — ghi lại cả những query nhanh nhưng đang full table scan, trước khi dữ liệu lớn lên khiến chúng trở thành query chậm thật sự.
  • min_examined_row_limit: (tuỳ chọn) chỉ ghi log nếu query đã quét qua ít nhất N dòng — giúp lọc bớt noise từ những query "chậm" do lock wait chứ không phải do thiếu index.

Bước 5: Kiểm Tra Slow Query Log Khi Dùng TABLE

SELECT start_time, query_time, rows_examined, sql_text
FROM mysql.slow_log
ORDER BY start_time DESC
LIMIT 20;

Nếu dùng log_output = FILE, đọc trực tiếp file hoặc dùng công cụ chuyên dụng:

tail -f /var/log/mysql/slow.log

# Công cụ tổng hợp thống kê theo từng dạng query
mysqldumpslow -s t -t 10 /var/log/mysql/slow.log

# Công cụ mạnh hơn của Percona, gom nhóm theo query pattern
pt-query-digest /var/log/mysql/slow.log

Bước 6: Tắt Log Khi Không Cần Nữa

General Query Log không nên để bật lâu dài trên production — chỉ bật tạm thời để debug rồi tắt ngay:

SET GLOBAL general_log = 'OFF';

Với bảng log dùng CSV engine, nên dọn định kỳ để tránh phình to (bảng này không tự động xoay vòng như file):

TRUNCATE TABLE mysql.general_log;
TRUNCATE TABLE mysql.slow_log;

Phần 2: Cấu Hình Trên AWS RDS (Qua DB Parameter Group)

Vì Sao RDS Khác Hoàn Toàn Với Server Tự Cài?

AWS RDS là dịch vụ managed — AWS quản lý hệ điều hành, filesystem và tiến trình MySQL. Hệ quả trực tiếp:

  • Không có SSH vào instance → không thể sudo vim /etc/mysql/my.cnf.
  • Không có quyền SUPER đầy đủ như self-managed → nhiều SET GLOBAL bị chặn hoặc phải qua stored procedure riêng của AWS.
  • Mọi tham số cấu hình server (tương đương nội dung my.cnf) phải chỉnh qua DB Parameter Group — một tài nguyên tách rời, gán vào DB instance.

Vì lý do "không SSH được vào filesystem", log_output = TABLE trở thành lựa chọn quan trọng trên RDS: nó cho phép bạn SELECT trực tiếp bằng SQL client thay vì phải tải file log về qua console/CLI.

Bước 1: Tạo (Hoặc Xác Định) Custom DB Parameter Group

RDS không cho sửa Default Parameter Group (default.mysql8.0 chẳng hạn) — bạn bắt buộc phải tạo một Custom Parameter Group riêng nếu chưa có:

  1. Vào RDS ConsoleParameter groupsCreate parameter group.
  2. Chọn Parameter group family khớp đúng engine version đang dùng (ví dụ mysql8.0).
  3. Đặt tên, ví dụ custom-mysql8-logging, rồi Create.
# Tương đương bằng AWS CLI
aws rds create-db-parameter-group \
  --db-parameter-group-name custom-mysql8-logging \
  --db-parameter-group-family mysql8.0 \
  --description "Custom params for query/slow log"

Bước 2: Cấu Hình Các Tham Số Logging Trong Parameter Group

Vào parameter group vừa tạo → Edit parameters → tìm và chỉnh các tham số sau:

Tham số Giá trị đề xuất Ghi chú
general_log 0 (mặc định) Chỉ bật 1 tạm thời khi cần debug, tắt lại ngay sau đó
slow_query_log 1 Nên bật thường trực trên production
long_query_time 1 (hoặc theo SLA của hệ thống) Ngưỡng tính là "chậm"
log_output TABLE Bắt buộc dùng TABLE nếu muốn SELECT trực tiếp; RDS vẫn hỗ trợ FILE nếu bạn muốn xem qua console/CloudWatch
log_queries_not_using_indexes 1 Phát hiện sớm query full scan

Bằng CLI:

aws rds modify-db-parameter-group \
  --db-parameter-group-name custom-mysql8-logging \
  --parameters \
    "ParameterName=general_log,ParameterValue=0,ApplyMethod=immediate" \
    "ParameterName=slow_query_log,ParameterValue=1,ApplyMethod=immediate" \
    "ParameterName=long_query_time,ParameterValue=1,ApplyMethod=immediate" \
    "ParameterName=log_output,ParameterValue=TABLE,ApplyMethod=immediate"

Lưu ý quan trọng: mỗi tham số trong RDS có cột Apply typedynamic hoặc static. Tham số dynamic áp dụng ngay (immediate), không cần reboot; tham số static bắt buộc reboot instance mới có hiệu lực. general_log, slow_query_log, long_query_time, log_output thường là dynamic, nhưng luôn kiểm tra lại cột này trong console trước khi thao tác trên production để tránh bất ngờ.

Bước 3: Gán Parameter Group Vào DB Instance

Nếu instance đang dùng parameter group khác (ví dụ default), phải gán lại:

  1. Vào RDS Console → chọn DB instance → Modify.
  2. Ở mục Database optionsDB parameter group, chọn custom-mysql8-logging.
  3. Chọn Apply immediately (áp dụng ngay) hoặc để áp dụng trong maintenance window kế tiếp.
  4. ContinueModify DB Instance.
aws rds modify-db-instance \
  --db-instance-identifier my-prod-db \
  --db-parameter-group-name custom-mysql8-logging \
  --apply-immediately

Nếu có tham số static trong nhóm thay đổi, RDS sẽ yêu cầu reboot — kiểm tra trạng thái parameter group trên console (pending-reboot là dấu hiệu cần reboot thủ công).

Bước 4: Kiểm Tra Log Trên RDS

Nếu dùng log_output = TABLE — kết nối bằng client SQL bất kỳ (không cần console AWS) và query trực tiếp:

SELECT start_time, query_time, rows_examined, sql_text
FROM mysql.slow_log
ORDER BY start_time DESC
LIMIT 20;

SELECT event_time, user_host, argument
FROM mysql.general_log
ORDER BY event_time DESC
LIMIT 20;

Nếu dùng log_output = FILE — không có SSH nên phải xem qua console hoặc CLI:

# Liệt kê các file log hiện có trên instance
aws rds describe-db-log-files --db-instance-identifier my-prod-db

# Tải một phần nội dung file log slow query
aws rds download-db-log-file-portion \
  --db-instance-identifier my-prod-db \
  --log-file-name slowquery/mysql-slowquery.log \
  --output text > slow.log

Hoặc trên RDS Console → chọn instance → tab Logs & events → xem/tải trực tiếp.

Cách chuyên nghiệp hơn cho môi trường production: bật export log sang CloudWatch Logs để có khả năng lưu trữ dài hạn, tìm kiếm bằng CloudWatch Logs Insights, và tạo alarm tự động:

aws rds modify-db-instance \
  --db-instance-identifier my-prod-db \
  --cloudwatch-logs-export-configuration '{"EnableLogTypes":["slowquery","general"]}' \
  --apply-immediately

Bước 5: Dọn Dẹp Bảng Log Trên RDS

mysql.general_log/mysql.slow_log dùng CSV engine không có index, chúng sẽ ngày càng chậm khi phình to. Trên RDS, master user (tài khoản admin bạn tạo khi khởi tạo instance) có quyền được AWS cấp riêng để dọn các bảng hệ thống này:

TRUNCATE TABLE mysql.general_log;
TRUNCATE TABLE mysql.slow_log;

Nên lên lịch dọn định kỳ (ví dụ qua một scheduled event hoặc job ngoài) nếu để general_log bật trong thời gian dài, tránh bảng phình to ảnh hưởng đến dung lượng storage của instance.

So Sánh Nhanh: Self-Managed Vs AWS RDS

Tiêu chí Server Tự Cài AWS RDS
Nơi cấu hình File my.cnf hoặc SET GLOBAL trực tiếp DB Parameter Group (không sửa file trực tiếp được)
Cần restart khi nào Chỉ khi sửa my.cnf; SET GLOBAL thì không Tuỳ tham số dynamic (không cần) hay static (cần reboot)
Đọc log dạng FILE tail -f, SSH trực tiếp vào server Qua RDS Console (Logs & events), CLI, hoặc export CloudWatch Logs
Đọc log dạng TABLE SELECT trực tiếp trên mysql.general_log/mysql.slow_log Giống hệt — SELECT trực tiếp, là cách được khuyến nghị vì không cần SSH
Quyền hạn Toàn quyền SUPER, chỉnh mọi thứ Bị giới hạn bởi IAM + Parameter Group, một số biến không chỉnh được
Xoá bớt log tích luỹ TRUNCATE, hoặc xoá/nén file + logrotate TRUNCATE bảng (được AWS cấp quyền đặc biệt), hoặc rotate file qua console

Cảnh Báo Hiệu Năng Và Best Practices

  • Không bao giờ để General Query Log bật vĩnh viễn trên production — mỗi câu SQL (kể cả SELECT đơn giản) đều tốn thêm một lần ghi log, có thể làm tăng đáng kể latency dưới tải cao. Chỉ bật trong một khung thời gian ngắn để debug, rồi tắt ngay và TRUNCATE bảng log.
  • Slow Query Log gần như luôn nên bật trên mọi môi trường production vì chi phí thấp và giá trị chẩn đoán cao — đây là nguồn dữ liệu đầu vào quan trọng nhất để tìm ra các query cần tối ưu bằng EXPLAIN (xem thêm Tối ưu query với index).
  • Đặt long_query_time phù hợp với SLA thực tế của hệ thống — quá thấp (ví dụ 0.01) sẽ khiến log phình to vì ghi cả những query bình thường; quá cao (ví dụ 10) sẽ bỏ sót nhiều vấn đề trước khi chúng trở nên nghiêm trọng. Giá trị 0.52 giây là điểm khởi đầu hợp lý cho hầu hết ứng dụng web.
  • Khi dùng log_output = TABLE, luôn có kế hoạch dọn dẹp (TRUNCATE định kỳ) — bảng CSV không có index nên càng lớn càng chậm khi SELECT, đặc biệt trên RDS nơi tài nguyên I/O được chia sẻ theo instance class.
  • Trên RDS, ưu tiên export log sang CloudWatch Logs cho môi trường production thật sự — nó tách biệt việc lưu trữ log ra khỏi resource của DB instance, hỗ trợ tìm kiếm mạnh hơn (CloudWatch Logs Insights) và giữ log ngay cả khi bảng mysql.slow_log bị TRUNCATE.

Checklist Cấu Hình Query Log Và Slow Query Log

  • [ ] Đã xác định đúng môi trường (self-managed hay AWS RDS) trước khi chọn cách cấu hình?
  • [ ] long_query_time đã được đặt phù hợp với SLA thực tế, chưa dùng giá trị mặc định (10 giây, thường quá cao)?
  • [ ] log_output đã chọn đúng — FILE nếu có SSH và dùng công cụ như pt-query-digest, TABLE nếu cần SELECT trực tiếp hoặc đang chạy trên RDS?
  • [ ] General Query Log chỉ bật tạm thời để debug, đã có kế hoạch tắt lại và TRUNCATE sau khi dùng xong?
  • [ ] Trên RDS, đã tạo Custom Parameter Group riêng (không cố sửa default) và gán đúng vào instance?
  • [ ] Đã kiểm tra tham số nào là static (cần reboot) trước khi apply trên production để tránh downtime bất ngờ?
  • [ ] Đã cân nhắc export log sang CloudWatch Logs cho môi trường production trên RDS?
  • [ ] Có job/lịch dọn dẹp định kỳ cho mysql.general_log/mysql.slow_log để tránh phình dung lượng?

Kết Luận

Query Log và Slow Query Log là hai công cụ quan sát cơ bản nhưng thường bị bỏ qua cho đến khi có sự cố xảy ra. Điểm mấu chốt cần nhớ: General Query Log dùng để debug ngắn hạn, Slow Query Log nên bật thường trực; và cách xuất log (FILE hay TABLE) không chỉ là chi tiết kỹ thuật — nó quyết định bạn sẽ đọc log bằng cách nào, đặc biệt quan trọng khi chuyển từ server tự cài (có SSH, có file) sang AWS RDS (không SSH, phải qua Parameter Group và thường dùng TABLE hoặc CloudWatch Logs).

Sau khi đã bật đúng log và thu thập được danh sách query chậm, bước tiếp theo là quay lại bài Tối ưu query với index để áp dụng EXPLAIN ANALYZE và composite index đúng cách cho từng query cụ thể được phát hiện qua Slow Query Log.