0

Apache Derby Bài 47: Khắc phục phân mảnh (Fragmentation) và nén dữ liệu bảng

1. Bản chất của hiện tượng Phân mảnh (Fragmentation)

Trong quá trình vận hành, cơ sở dữ liệu của bạn sẽ liên tục tiếp nhận các thao tác INSERT, UPDATE, và DELETE.

Khi bạn chạy lệnh DELETE để xóa hàng nghìn đơn hàng cũ, Apache Derby không lập tức thu nhỏ dung lượng của tệp tin vật lý (.dat) trên ổ cứng. Thay vào đó, nó chỉ đánh dấu các dòng đó là "đã xóa". Kết quả là trong các trang nhớ (data pages) xuất hiện vô số "lỗ hổng" không chứa dữ liệu thực tế. Hiện tượng này gọi là Phân mảnh (Fragmentation).

Tương tự, thao tác UPDATE làm tăng độ dài của một chuỗi (ví dụ: cập nhật VARCHAR từ 10 ký tự lên 100 ký tự) có thể khiến dòng dữ liệu không còn nằm vừa trong trang nhớ cũ, buộc Derby phải cắt nó ra và lưu ở một nơi khác (Row Chaining), gây phân mảnh nghiêm trọng.

2. Hậu quả của việc không dọn dẹp dữ liệu

  1. Lãng phí dung lượng đĩa: Tệp cơ sở dữ liệu phình to lên đến vài chục Gigabyte dù dữ liệu thực tế chỉ chiếm vài trăm Megabyte.

  2. Hiệu năng Table Scan chạm đáy: Khi hệ thống thực hiện SELECT (Quét toàn bảng), nó phải đọc qua tất cả các "lỗ hổng" trống rỗng đó, gây lãng phí tài nguyên I/O ổ cứng.

  3. Giảm hiệu quả của Cache: Các trang nhớ chứa đầy lỗ hổng khi được nạp lên RAM sẽ làm lãng phí bộ nhớ đệm (Page Cache).

Để khắc phục, bạn cần định kỳ thực hiện Nén dữ liệu (Table Compression) nhằm dồn các dòng dữ liệu sát lại với nhau và trả lại không gian trống cho hệ điều hành.

3. Các thủ tục hệ thống để Nén bảng

Apache Derby cung cấp hai thủ tục hệ thống (Stored Procedures) thuộc schema SYSCS_UTIL để giải quyết phân mảnh. Cả hai đều yêu cầu bạn phải có quyền chủ sở hữu bảng (Table Owner) hoặc Database Owner.

3.1. Nén toàn diện (SYSCS_COMPRESS_TABLE)

Thủ tục này tạo ra một tệp tin vật lý hoàn toàn mới, copy toàn bộ dữ liệu hợp lệ từ tệp cũ sang (dồn sát lại), xây dựng lại toàn bộ các Index (B-tree), và sau đó xóa tệp cũ đi.

Cú pháp:

SQL

CALL SYSCS_UTIL.SYSCS_COMPRESS_TABLE('TEN_SCHEMA', 'TEN_BANG', SEQUENTIAL);

  • SEQUENTIAL (0 hoặc 1): Nếu bằng 1, Derby sẽ cố gắng sắp xếp lại các dòng dữ liệu vật lý theo đúng thứ tự của Khóa chính (Primary Key), giúp các truy vấn tìm kiếm theo dải (Range Scan) chạy cực nhanh.

Ví dụ:

SQL

CALL SYSCS_UTIL.SYSCS_COMPRESS_TABLE('APP', 'DON_HANG', 1);

Ưu và nhược điểm:

  • Ưu điểm: Giải phóng 100% không gian thừa, tốc độ sau khi nén là tối ưu nhất.

  • Nhược điểm: Khóa độc quyền (Exclusive Lock) toàn bộ bảng. Trong suốt thời gian nén (có thể mất vài phút), không ai được phép SELECT, INSERT hay UPDATE trên bảng này. Ngoài ra, ổ cứng phải còn đủ dung lượng trống để chứa bản copy tạm thời.

3.2. Nén tại chỗ (SYSCS_INPLACE_COMPRESS_TABLE)

Thủ tục này không tạo file mới. Nó hoạt động như một cỗ máy dọn dẹp ngầm: di chuyển dần các dòng ở cuối tệp lên lấp vào các lỗ hổng ở đầu tệp, sau đó cắt bỏ phần đuôi tệp.

Cú pháp:

SQL

CALL SYSCS_UTIL.SYSCS_INPLACE_COMPRESS_TABLE(
    'TEN_SCHEMA', 'TEN_BANG', 
    PURGE_ROWS, DEFRAGMENT_ROWS, TRUNCATE_END
);

  • PURGE_ROWS (1 hoặc 0): Dọn dẹp hoàn toàn các dòng đã bị xóa.

  • DEFRAGMENT_ROWS (1 hoặc 0): Di chuyển dữ liệu để lấp lỗ hổng.

  • TRUNCATE_END (1 hoặc 0): Cắt bỏ không gian thừa ở cuối tệp trả về cho hệ điều hành.

Ví dụ dọn dẹp toàn diện tại chỗ:

SQL

CALL SYSCS_UTIL.SYSCS_INPLACE_COMPRESS_TABLE('APP', 'DON_HANG', 1, 1, 1);

Ưu và nhược điểm:

  • Ưu điểm: Không yêu cầu thêm dung lượng đĩa trống. Chỉ khóa từng dòng (Row-level lock), nghĩa là hệ thống vẫn có thể hoạt động bình thường, các giao dịch khác vẫn chạy được (chỉ chậm hơn một chút).

  • Nhược điểm: Quá trình chạy chậm hơn so với nén toàn diện và không sắp xếp lại dữ liệu theo khóa chính.

4. Thực hành: Tự động hóa tác vụ bảo trì bằng Java

Trong môi trường Production, quản trị viên thường thiết lập một Cron Job (hoặc dùng @Scheduled trong Spring Boot) để tự động gọi thủ tục INPLACE_COMPRESS_TABLE vào ban đêm (ví dụ 3h sáng) cuối mỗi tuần.

Java

package com.hieu.derby.admin;

import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

public class DerbyMaintenanceTask {

    public static void runWeeklyCompression() {
        String url = "jdbc:derby://localhost:1527/my_company_db";
        
        System.out.println("Bắt đầu tiến trình nén dữ liệu bảng DON_HANG...");

        try (Connection conn = DriverManager.getConnection(url, "admin", "MatKhauSieuKho123")) {
            
            // Sử dụng nén tại chỗ (In-place) để không làm gián đoạn hệ thống 24/7
            String sql = "CALL SYSCS_UTIL.SYSCS_INPLACE_COMPRESS_TABLE(?, ?, 1, 1, 1)";
            
            try (CallableStatement cstmt = conn.prepareCall(sql)) {
                cstmt.setString(1, "APP");
                cstmt.setString(2, "DON_HANG");
                
                cstmt.execute();
                System.out.println("=> Nén và chống phân mảnh thành công!");
            }

            // Mẹo: Sau khi nén, độ phân tán dữ liệu thay đổi, 
            // CẦN PHẢI cập nhật lại thống kê cho Optimizer (như đã học ở Bài 46)
            try (CallableStatement cstmtStats = conn.prepareCall("CALL SYSCS_UTIL.SYSCS_UPDATE_STATISTICS('APP', 'DON_HANG', null)")) {
                cstmtStats.execute();
                System.out.println("=> Đã cập nhật thống kê Optimizer.");
            }

        } catch (SQLException e) {
            System.err.println("Lỗi trong quá trình bảo trì: " + e.getMessage());
        }
    }

    public static void main(String[] args) {
        runWeeklyCompression();
    }
}

5. Tổng kết

  • Các hệ quản trị CSDL không tự động thu hồi dung lượng đĩa sau khi bạn xóa dữ liệu. Sự phân mảnh làm giảm nghiêm trọng hiệu năng đọc.

  • Sử dụng SYSCS_COMPRESS_TABLE để "đập đi xây lại" tệp dữ liệu (nhanh, sạch, nhưng gây gián đoạn hệ thống).

  • Sử dụng SYSCS_INPLACE_COMPRESS_TABLE để dọn dẹp ngầm từ từ (thích hợp cho các hệ thống yêu cầu hoạt động liên tục 24/7).

  • Luôn nhớ gọi hàm cập nhật Statistics cho Optimizer ngay sau khi hoàn tất quá trình nén bảng.

Với bài học này, bạn đã sở hữu trọn bộ kỹ năng bảo trì, tinh chỉnh và giữ cho cơ sở dữ liệu Apache Derby luôn hoạt động ở trạng thái nguyên sơ nhất. Bài tiếp theo sẽ hướng dẫn bạn đối phó với sự thay đổi của công nghệ: Apache Derby Bài 48: Nâng cấp phiên bản (Version Upgrade) và Migration dữ liệu an toàn.


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí