- Trong suốt 1 năm qua, tác giả đã nỗ lực tìm hiểu sâu cách vận hành ứng dụng Rails với SQLite sao cho có hiệu năng tốt và ổn định
- Trong quá trình đó, đã rút ra nhiều bài học và muốn chia sẻ chúng
- Bài viết sẽ giải thích nguyên nhân của các vấn đề và cách khắc phục
Vấn đề của SQLite và Rails
- Theo mặc định, ứng dụng Rails dùng SQLite chưa ở trạng thái có thể dùng ngay
- Với một chút điều chỉnh và tinh chỉnh, có thể tạo ra ứng dụng có hiệu năng tốt và ổn định
- Trong Rails 8, mục tiêu là để cấu hình mặc định cũng đã sẵn sàng cho production
Ứng dụng demo "Lorem News"
- Bài viết sẽ dùng ứng dụng demo tên là "Lorem News" để giải thích vấn đề và giải pháp
- Ứng dụng này là bản sao của Hacker News, nơi người dùng có thể đăng bài viết và bình luận
Kiểm thử hiệu năng
- Hiệu năng được kiểm thử bằng CLI load test
oha và route benchmark bên trong ứng dụng
- Hiệu năng được đo với cả một request đơn lẻ và nhiều request đồng thời
Vấn đề chính: ngoại lệ SQLITE_BUSY
- SQLite dùng khóa ghi để chỉ cho phép một thao tác ghi tại một thời điểm
- Khi nhiều kết nối cùng lúc cố lấy khóa ghi, ngoại lệ
SQLITE_BUSY sẽ xảy ra
- Để giải quyết vấn đề này, cần dùng immediate transaction
Immediate transaction
- Mặc định, SQLite dùng chế độ deferred transaction
- Khi dùng immediate transaction, hệ thống sẽ thử lấy khóa ghi ngay lập tức và có thể retry nếu thất bại
- Có thể dùng gem
sqlite3-ruby để đặt chế độ transaction mặc định thành immediate mode
Cấu hình timeout
- Có thể giảm ngoại lệ
SQLITE_BUSY bằng cách cấu hình timeout trong file database.yml
- Có thể dùng thiết lập
busy_timeout của SQLite để retry khi tranh chấp khóa ghi
Vấn đề GVL (Global VM Lock)
- Gem
sqlite3-ruby không giải phóng GVL khi gọi mã C của SQLite
- Điều này làm giảm hiệu năng đồng thời
- Có thể dùng
busy_handler để giải phóng GVL và cải thiện hiệu năng
Cài đặt lại busy_timeout
busy_timeout được cài đặt lại để mọi query đều được retry với cùng một tần suất
- Điều này giúp các query cũ không bị timeout
Cải thiện hiệu năng
- Để cải thiện hiệu năng, cần áp dụng các cấu hình sau
- dùng immediate transaction
- cấu hình timeout
- dùng
busy_handler
- dùng chế độ WAL (Write-Ahead Logging)
- tách riêng connection pool đọc/ghi
Tóm tắt của GN⁺
- Bài viết đề cập đến các vấn đề hiệu năng của ứng dụng Rails dùng SQLite và các cách khắc phục
- Có thể cải thiện hiệu năng bằng các cách như immediate transaction, cấu hình timeout, giải phóng GVL, dùng chế độ WAL và tách riêng connection pool đọc/ghi
- Bài viết này sẽ rất hữu ích cho các lập trình viên dùng SQLite và Rails
- Với các dự án khác có chức năng tương tự, PostgreSQL và MySQL cũng là những lựa chọn được khuyến nghị
1 bình luận
Ý kiến trên Hacker News
Giới thiệu dự án Litestack của Oldmoe
Cảm ơn vì bài viết chi tiết
Khuyến nghị cho những người làm việc với SQLite
Câu hỏi về hệ thống phân tích FOSS
Vấn đề GVL của gem sqlite3-ruby
Thiết lập cho dịch vụ web cá nhân
PRAGMA journal_mode = WALPRAGMA busy_timeout = 5000PRAGMA synchronous = NORMALPRAGMA cache_size = 1000000000PRAGMA foreign_keys = truePRAGMA temp_store = memoryBEGIN IMMEDIATECâu hỏi về Django
Thắc mắc về cấu hình mặc định của
busy_timeoutbusy_timeoutmặc định lại có độ trễ kiểu trừng phạt các truy vấn cũ hơnÝ kiến về việc dùng SQLite với Rails
Cảm ơn vì đã giải quyết vấn đề tích hợp Rails