Hệ thống chậm ư? Áp dụng ngay những kỹ thuật này (Phần 2)
Dạo bài
Một hệ thống lớn luôn phải đối mặt với sự gia tăng không ngừng của lượng truy cập từ người dùng, đồng thời phải kiểm soát được lượng dữ liệu ngày càng phình ra. Bạn bắt đầu nhận thấy những dấu hiệu rõ ràng của sự trì trệ, hệ thống chạy chậm và gây khó khăn trong trải nghiệm của người dùng
Nối tiếp Phần 1, trong Phần 2 này, mình sẽ giới thiệu đến các bạn các kỹ thuật hữu ích để tối ưu hóa và cải thiện hiệu suất, giúp hệ thống hoạt động một cách hiệu quả hơn ở Server. Let's go!
💭 Đây không phải là một bài viết hướng dẫn triển khai chi tiết từng kỹ thuật
App/Server
1. Hardware
Nâng cấp RAM, CPU, DISK - đảm bảo đủ tài nguyên phần cứng để đáp ứng nhu cầu xử lý các tác vụ là điều tất yếu
Lúc mình còn là một sinh viên, mẹ mình cho 10 triệu để mua một chiếc máy tính. Trong năm 1 rồi năm 2, mọi thứ đều hoạt động suôn sẻ và không có chuyện gì xảy ra. Cho tới khi học năm 3, lúc cài đặt Visual Studio của Microsoft để học C# thì lúc này thảm họa mới ập tới, chiếc laptop của mình không mở nổi ứng dụng luôn chứ huống hồ gì là code tối ưu, code clean
Thực tế là, dù bạn có tối ưu mã nguồn tốt đến đâu nhưng lại không chịu chi tiền nâng cấp phần cứng thì cũng không thể đáp ứng được hệ thống ngày càng phát triển
2. Cache
Cache ở server cũng tựa như cache ở client là lưu trữ dữ liệu tạm thời. Tuy nhiên cache ở client bị hạn chế rất nhiều
Cache ở server nhằm giảm thời gian truy cập vào disk, cơ sở dữ liệu. Bạn muốn cache bao nhiêu thì cache, cache như nào thì cache, quan trọng là có một chiến lược cache hiệu quả
Có 2 kiểu cache là Distributed cache và In-memory cache
- Distributed cache: Server của cache sẽ nằm ở một server khác, riêng biệt với web server của bạn
- In-memory cache: Bạn sẽ cache trực tiếp trên web server của bạn
RAM là hữu hạn, nếu bạn cache mà không kiểm soát thì tràn RAM là điều không thể tránh khỏi. Để triển khai cache hiệu quả, bạn cần có một chiến lược cache tốt. Dưới đây là một số chiến lược cache phổ biến:
- LRU Cache (Least Recent Used): Cache những dữ liệu được truy cập gần đây nhất, những dữ liệu ít được truy cập hơn sẽ được remove ra khỏi cache khi tới giới hạn
- FIFO Cache (First In First Out): Dữ liệu mới sẽ được thêm vào cache, những dữ liệu cũ sẽ bị remove ra khỏi cache khi tới giới hạn
3. Reverse proxy và load balancer
Reverse proxy là một server đứng trước web server chính và đứng ra nhận các request từ client. Thay vì các request từ client được chuyển trực tiếp tới web server, reverse proxy đảm nhận nhiệm vụ nhận và chuyển tiếp request này
Limit rating là một trong những chức năng quan trọng của reverse proxy, giới hạn số lượng request của user trong một khoảng thời gian. Chẳng hạn như một IP trong 1 phút chỉ có thể request 10 lần. Việc này giúp bảo vệ web server tránh bị quá tải và chống lại các cuộc tấn công từ attacker
Load balancer: Phân phối các request tới những web server hoặc các node để giảm tải một web server phải xử lý quá nhiều tác vụ
Kết hợp giữa reverse proxy và load balancer giúp tăng cường tính bảo mật và hiệu suất của hệ thống
4. Optimize code
Sử dụng cấu trúc dữ liệu và thuật toán phù hợp
Loại bỏ code thừa, kiểm tra mã nguồn và loại bỏ những đoạn code không sử dụng. Mã không sử dụng không chỉ làm tăng kích thước của chương trình mà còn có thể ảnh hưởng đến hiệu suất
Tối ưu hóa vòng lặp, đảm bảo rằng các vòng lặp trong mã nguồn được viết một cách hiệu quả nhất có thể
Tái sử dụng mã, kiểm tra xem có thể tái sử dụng các phần mã nguồn đã được viết trước đó để tránh việc viết lại các chức năng tương tự
5. Microservice
Microservice là một kiến trúc phần mềm trong đó ứng dụng được phân tách thành các dịch vụ nhỏ, độc lập và tự quản lý. Mỗi dịch vụ microservice chạy riêng biệt và thực hiện một chức năng cụ thể của ứng dụng
Thiết kế dựa trên kiến trúc microservice sẽ giúp tăng khả năng mở rộng, cung cấp tính độc lập và tái sử dụng, tăng khả năng quản lý và cho phép sử dụng đa dạng công nghệ
6. Async & Concurrency
Kỹ thuật async và concurrency là hai phương pháp quan trọng trong việc tối ưu hóa xử lý đồng thời và tăng hiệu suất của hệ thống
Async được sử dụng để xử lý các tác vụ mà không chặn luồng chính của chương trình. Thay vì đợi đến khi một tác vụ hoàn thành trước khi tiếp tục thực hiện các tác vụ tiếp theo, async cho phép chương trình tiếp tục thực hiện các tác vụ khác
Concurrency là khả năng của hệ thống để xử lý nhiều tác vụ cùng một lúc. Nó cho phép các tác vụ được thực hiện song song và tận dụng tài nguyên hệ thống một cách hiệu quả
7. Handle error
Đảm bảo xử lý lỗi một cách chính xác để tránh tình trạng chương trình bị treo hoặc tắt đột ngột. Bên cạnh đó, cần thiết lập cơ chế tự động khởi động lại ứng dụng để đảm bảo tính khả dụng cao
8. Compress data
Khi làm việc với dữ liệu, một phương pháp quan trọng để tối ưu hóa kích thước và tăng tốc độ truyền dữ liệu là nén dữ liệu. Nén dữ liệu trước khi lưu trữ hoặc phản hồi về client để giảm kích thước và tăng tốc độ truyền dữ liệu
9. Stream
Stream là một phương pháp xử lý dữ liệu liên tục và tăng tốc độ xử lý bằng cách chia dữ liệu thành các mảnh nhỏ hơn và truyền chúng như một dòng chảy liên tục. Thay vì chờ đến khi tất cả dữ liệu được xử lý xong và gửi về client, stream cho phép truyền dữ liệu từng phần
Database
1. Design database
Thiết kế cơ sở dữ liệu chuẩn là một phần quan trọng để đảm bảo tính toàn vẹn và hiệu suất của hệ thống, nhằm tránh lưu trữ dữ liệu dư thừa và bị lặp
2. Index & Create view
Đánh index: Tạo các chỉ mục trên các trường dữ liệu quan trọng trong CSDL. Chỉ mục giúp tăng tốc độ truy vấn. Điều này đặc biệt hữu ích khi query trên các trường dữ liệu thường xuyên được query
Tạo bảng view: Một bảng ảo được tạo từ một hoặc nhiều bảng gốc trong CSDL. Bảng view chỉ chứa các trường cần thiết để đáp ứng nhu cầu truy vấn cụ thể. Bằng cách tạo bảng view, bạn có thể giảm số lượng trường trả về và giới hạn dữ liệu cần xử lý, từ đó tăng tốc độ truy vấn
Tuy nhiên cần lưu ý rằng 2 kỹ thuật này đánh đổi giữa việc truy vấn nhanh và tốn bộ nhớ
3. Query
Sử dụng phương pháp truy vấn phù hợp: Dựa vào yêu cầu bài toán, chúng ta cần chọn phương pháp truy vấn phù hợp
Chẳng hạn như sử dụng aggregate() trong mongoose thay vì sử dụng find() khi có hàng triệu record
Tránh N + 1 query: Một vấn đề thường gặp trong query, khi cần lấy dữ liệu liên quan từ nhiều bảng khác nhau
Ví dụ: Bạn cần query 100 user trong bảng User, mỗi user sẽ có một role trong bảng Role. Bạn sẽ cần loop 100 user để lấy từng role của từng user đó
Khi cần truy vấn dữ liệu liên quan giữa các bảng, sử dụng populate trong thư viện Mongoose hoặc join trong SQL để tối ưu hóa việc lấy dữ liệu. Điều này giúp tránh tình trạng N + 1 query bằng cách lấy dữ liệu trong một truy vấn duy nhất
Chỉ select những trường cần thiết: Hạn chế việc lựa chọn các trường không cần thiết trong truy vấn. Chỉ select những trường dữ liệu cần thiết để giảm lượng dữ liệu trả về và tăng tốc độ truy vấn. Điều này đặc biệt quan trọng khi truy vấn dữ liệu lớn hoặc khi có các trường chứa dữ liệu lớn
Thứ tự câu truy vấn: Sắp xếp đúng thứ tự là rất quan trọng, nó có thể ảnh hưởng đến thời gian truy vấn nếu bạn sắp xếp sai thứ tự
4. Cluster/Data replication
Cluster/Data replication: Nhân bản dữ liệu sang các server khác nhau là một kỹ thuật nâng cao cần áp dụng cho các hệ thống lớn. Lợi ích bao gồm:
Tính sẵn sàng cao: Nếu một node hoặc server gặp sự cố, hệ thống vẫn có thể tiếp tục hoạt động thông qua các bản sao dữ liệu khác
Khả năng chịu lỗi: Khi một node hoặc server gặp sự cố, hệ thống có thể tự động chuyển đổi sang các bản sao dữ liệu khác mà không gây gián đoạn hoạt động
Tăng hiệu suất: Dữ liệu được phân phối trên nhiều node sẽ giúp tăng khả năng xử lý và truy cập song song, bạn có thể write trên primary node và read trên các secondary node
Sao lưu và khôi phục dữ liệu: Các bản sao dữ liệu có thể được sử dụng để sao lưu và khôi phục dữ liệu khi cần thiết
5. Partitioning/Sharding Data
Phân vùng dữ liệu (Partitioning/Sharding Data): Tăng khả năng mở rộng và hiệu suất bằng cách chia dữ liệu thành các phần nhỏ hoặc phân mảnh trên các server khác nhau
6. Choose database
Việc lựa chọn cơ sở dữ liệu phù hợp là một yếu tố quan trọng để đảm bảo hệ thống hoạt động hiệu quả. Hiện nay, có nhiều loại cơ sở dữ liệu khác nhau như NoSQL và SQL, mỗi loại đều có những ưu điểm và hạn chế riêng
Cơ sở dữ liệu NoSQL: Bao gồm MongoDB, DynamoDB và nhiều hệ quản trị cơ sở dữ liệu NoSQL khác
Một hệ thống có tính chất như cần thay đổi cấu trúc dữ liệu linh động và liên tục thì NoSQL là một lựa chọn tốt
Cơ sở dữ liệu SQL: Bao gồm MySQL, SQL Server, PostgreSQL và nhiều hệ quản trị cơ sở dữ liệu SQL khác
Một hệ thống có cấu trúc rõ ràng, cần sự ràng buộc chặt chẽ cũng như tính toàn vẹn dữ liệu cao thì SQL là một lựa chọn hợp lý
Kết bài
Hy vọng những kiến thức mình chia sẻ ở Phần 1 và Phần 2 có thể giúp hệ thống của bạn tối ưu và tăng hiệu suất. Cảm ơn các bạn đã đọc
All rights reserved