+3

Những bài học tôi rút ra từ những sai sót về cơ sở dữ liệu của mình

Source: https://www.tuanh.net/blog/oop/the-lessons-i-have-learned-from-my-database-mishaps

Quản lý cơ sở dữ liệu là nền tảng của phát triển phần mềm. Tuy nhiên, trong suốt kinh nghiệm của mình, tôi đã gặp phải nhiều sai sót trong lĩnh vực này. Những lỗi này đã ảnh hưởng nghiêm trọng đến hiệu suất ứng dụng, đồng thời đặt ra những rủi ro đáng kể về bảo mật và quản lý dữ liệu. Trong bài viết này, tôi sẽ đi sâu vào một số sai lầm và những bài học quý giá mà tôi đã rút ra.

1. Lưu trữ hình ảnh trong cơ sở dữ liệu

Thông thường, hình ảnh được lưu trữ trong cơ sở dữ liệu dưới dạng đối tượng nhị phân lớn (BLOB). Tuy nhiên, một số người ưa thích cách tiếp cận thận trọng hơn, chuyển đổi hình ảnh sang mã hóa Base64 và lưu trữ nó dưới dạng chuỗi ký tự có độ dài biến đổi.

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;

@Service
public class ImageService {

    @Autowired
    private ImageRepository imageRepository;

    public void saveImage(MultipartFile file) throws IOException {
        ImageEntity imageEntity = new ImageEntity();
        imageEntity.setImageData(file.getBytes());
        imageRepository.save(imageEntity);
    }
}

Tôi đã mắc sai lầm này khi mới bắt đầu làm việc. Lưu trữ hình ảnh trực tiếp trong cơ sở dữ liệu là một ý tưởng tồi vì nó có thể làm chậm mọi thứ và gây ra nhiều rắc rối. Mặc dù có vẻ như là một cách tốt để giữ an toàn cho hình ảnh của bạn, nhưng nó đi kèm với một số vấn đề lớn. Vấn đề lớn nhất là nó khiến cơ sở dữ liệu của bạn trở nên rất lớn và chậm. Cơ sở dữ liệu không được xây dựng cho các tệp lớn như hình ảnh, vì vậy nó có thể khiến ứng dụng của bạn hoạt động chậm chạp. Hình ảnh chiếm rất nhiều dung lượng, và khi bạn cố gắng lưu hoặc tải chúng từ cơ sở dữ liệu, nó có thể mất rất nhiều thời gian. Một vấn đề khác là nó khiến việc sao lưu trở nên đau đầu. Vì các tệp hình ảnh rất lớn nên việc sao lưu và khôi phục dữ liệu mất nhiều thời gian hơn.

2. Không đóng database connection

Ngày mới bắt đầu học lập trình, tôi tạo kết nối cơ sở dữ liệu vô tội vạ mà không hề suy nghĩ gì. Lúc đó tôi chưa nhận ra rằng mọi thứ đều có giới hạn. Tôi vẫn còn nhớ một đoạn script PHP mà mình đã viết trông như thế này:

<?php
$servername = "localhost";
$username = "your_username";
$password = "your_password";
$dbname = "your_database";

$conn = new mysqli($servername, $username, $password, $dbname);

if ($conn->connect_error) {
    die("Kết nối không thành công: " . $conn->connect_error);
}

$sql = "SELECT id, name FROM users";
$result = $conn->query($sql);

if ($result->num_rows > 0) {
    while ($row = $result->fetch_assoc()) {
        echo "ID: " . $row["id"]. " - Name: " . $row["name"]. "<br>";
    }
} else {
    echo "0 result";
}
?>

Không đóng kết nối cơ sở dữ liệu sau khi sử dụng có thể dẫn đến một số vấn đề nghiêm trọng ảnh hưởng đến hiệu suất và sự ổn định của ứng dụng. Kết nối cơ sở dữ liệu tiêu tốn tài nguyên hệ thống, bao gồm bộ nhớ và tài nguyên mạng. Nếu để mở, các tài nguyên này không được giải phóng, dẫn đến rò rỉ tài nguyên. Điều này có thể làm giảm hiệu suất của cả ứng dụng và máy chủ cơ sở dữ liệu. Hầu hết các hệ quản trị cơ sở dữ liệu sử dụng pool kết nối, giới hạn số lượng kết nối đồng thời có thể mở. Các kết nối chưa đóng có thể làm cạn kiệt pool này, ngăn chặn các yêu cầu mới kết nối với cơ sở dữ liệu và gây ra lỗi từ chối kết nối. Với quá nhiều kết nối mở, cơ sở dữ liệu phải quản lý nhiều kết nối đồng thời, điều này có thể làm giảm hiệu suất tổng thể của hệ thống. Các hoạt động truy vấn có thể trở nên chậm hơn do chia sẻ tài nguyên giữa các kết nối. Hơn nữa, các kết nối cơ sở dữ liệu chưa đóng có thể gây ra rủi ro bảo mật, đặc biệt nếu thông tin kết nối được giữ lại lâu hơn cần thiết. Tin tặc có thể lợi dụng các kết nối mở để thực hiện các hành động trái phép hoặc tận dụng các lỗ hổng bảo mật.

3. Đặt index một cách bừa bãi

Ban đầu, tôi tin rằng chỉ số không cụm (Non-clustered indexes) là giải pháp toàn diện để tăng hiệu suất truy vấn. Tôi thường đạt được những cải thiện tốc độ đáng kể, lên đến 30%. Tuy nhiên, cuối cùng tôi nhận ra rằng mình đã quá đơn giản trong cách tiếp cận và việc sử dụng tùy tiện các chỉ số này không phải lúc nào cũng là chiến lược tốt nhất. Sự gia tăng số lượng chỉ số và tăng trưởng theo cấp số nhân của dữ liệu đã dẫn đến sự mở rộng nhanh chóng của cơ sở dữ liệu của tôi, gần đến mức tới hạn. Tôi có thể nhớ rõ hiệu suất của bảng bài đăng của mình. Ban đầu, API chịu trách nhiệm thu thập tin tức từ các nguồn bên ngoài có thời gian thực thi dưới 500 mili giây. Tuy nhiên, sau một tháng hoạt động của hệ thống và nhập gần 5GB dữ liệu, tôi gặp phải sự suy giảm hiệu suất đáng kể, với việc chèn tin tức mất khoảng 2 giây để hoàn thành.

4. Select *

Tôi nhận thấy rằng việc sử dụng SELECT * trong các truy vấn SQL, mặc dù dễ dàng, nhưng có thể gây ra vấn đề. Lấy tất cả các cột từ một bảng có thể dẫn đến việc truyền dữ liệu không cần thiết và ảnh hưởng đến hiệu suất. Việc truy xuất tất cả các cột với SELECT * có thể làm giảm đáng kể hiệu suất truy vấn do tăng lượng dữ liệu truyền tải. Điều này đặc biệt gây vấn đề cho các bảng lớn hoặc cơ sở dữ liệu từ xa. Việc truyền dữ liệu không cần thiết qua mạng có thể tiêu tốn băng thông quá mức và tăng độ trễ. Hơn nữa, việc thiếu chọn cột rõ ràng có thể giảm tính rõ ràng và khả năng bảo trì của mã.

5. Vấn đề N+1

Bạn có thể theo dõi chủ đề này để biết thêm chi tiết: Cách hiệu quả nhất để viết truy vấn Hibernate để tránh các tắc nghẽn hiệu suất như vấn đề N + 1 là gì?

6. Kết luận

Trên đây là những kinh nghiệm và sai lầm của tôi trong quá trình làm việc với cơ sở dữ liệu cho đến nay. Tôi hy vọng nó sẽ phần nào hữu ích cho bạn trong quá trình lập trình của mình. Nếu bạn từng gặp phải bất kỳ vấn đề nào hoặc nhận thấy lỗi nào trong những gì tôi đã viết, vui lòng bình luận bên dưới. Cảm ơn bạn và hy vọng sẽ gặp lại bạn.


All Rights Reserved

Viblo
Let's register a Viblo Account to get more interesting posts.