Database Denormalization

trong danh mục Web Programming

Đã lâu chưa viết bài về vấn đề kỹ thuật, nay viết bài này để chia sẽ tới các bạn một kỹ thuật trong nhóm các kỹ thuật tối ưu trong xây dựng ứng dụng web lớn (Large-scale Web Application), đó là kỹ thuật “Denormalization“. Ứng dụng lớn là các ứng dụng đòi hỏi khả năng chịu tải lớn và cần tối ưu về performance và tốc độ truy xuất database vì xu thế web bây giờ thao tác truy xuất database diễn ra rất thường xuyên.

Denormalization là kỹ thuật trong nhóm kỹ thuật tối ưu thiết kế cơ sở dữ liệu quan hệ (RDBMS). Như các bạn có được học thì hệ cơ sở dữ liệu quan hệ được thiết kế sử dụng mô hình BẢNG và sử dụng cột(Key) làm mối quan hệ. Và các bạn nếu có tìm hiểu thì cũng biết quá trình thiết kế cơ sở dữ liệu trải qua 3 bước Normalization (Chuẩn hóa) để dữ liệu được thiết kế sao cho tối ưu, không trùng lặp, dễ mở rộng và thay đổi.

Tuy nhiên, việc chuẩn hóa không phải lúc nào cũng phát huy hết tác dụng trong các ứng dụng thực tế. Ví dụ dưới đây là một mô hình database đã được chuẩn hóa cho chức năng tạo comment.

Như các bạn có làm qua cơ sở dữ liệu thì việc hiển thị thông tin comment với tên người gởi(sender) chỉ cần một câu lệnh JOIN để lấy thông tin của sender từ table User, ví dụ: “SELECT * FROM Comment INNER JOIN User ON c_senderid = u_id WHERE …”.

Đối với các ứng dụng nhỏ thì thao tác này chạy rất OK và database thiết kế như thế là đã chuẩn hóa rồi. Nhưng đối với các ứng dụng lớn, quá trình JOIN trong câu lệnh SELECT không phải là một thao tác nhanh và sẽ tốn thời gian xử lý phức tạp hơn vì cần phải JOIN 2 table. Trong trường hợp Table User quá lớn và table Comment cũng lớn thì việc JOIN trong câu lệnh SELECT sẽ là nổi ám ảnh cho ứng dụng của bạn.

Do gặp những tình huống như vậy, việc chuẩn hóa sẽ không tốt cho các câu lệnh SELECT cho ứng dụng dạng này nên kỹ thuật chuẩn hóa ngược (De-normalization) đã ra đời nhằm tăng tốc các câu lệnh SELECT và giảm việc JOIN khi không cần thiết. Dưới đây là mô hình database đã sử dụng kỹ thuật Denormalization, thêm một Field là c_senderusername để lưu thông tin người gởi mà không cần phải JOIN trong câu SELECT mới ra được username.

Vì đây gọi là quá trình chuẩn hóa ngược nên không nên sử dụng tùy tiện vì nó đi ngược lại quá trình chuẩn hóa, sẽ xảy ra quá trình douple dữ liệu cho một số field mà bạn sử dụng. Tuy nhiên, nếu sử dụng đúng chỗ thì kỹ thuật denormalization sẽ cải thiện nhiều cho các thao tác SELECT của bạn. Và nên nhớ, JOIN là nổi ám ảnh cho table lớn và LEFT JOIN (hay RIGHT JOIN) là nổi ám ảnh của nổi ám ảnh ^^.

Hy vọng bài viết giúp đỡ được phần nào cho việc thiết kế cơ sở dữ liệu cho ứng dụng “lớn” của bạn.

10 bình luận

  1. lhd says:

    Thanks. Thi thoảng mình vẫn gặp mà giờ mới biết

    [Reply]

  2. Tuan says:

    Wow, hôm qua mới nói chiện zới Tòan ếch, hôm nay có rồi! Nhanh tè ^^

    [Reply]

  3. Duy Tuyên says:

    Trong thực tế đúng là phải chơi nhiều chiêu phá cách như thế! Ví dụ khác:

    – 1 sản phẩm có nhiều hình, theo qui tắc đúng thì thiết kế 2 table: products và product_images. Tuy nhiên, ta có thể làm đơn giản hơn bằng cách tạo 1 table products, trong đó có field images. Giả sử hình ảnh chúng ta ko cần ghi chú thì field image sẽ chứa dữ liệu dạng như:

    1.jpg@2.jpg@3.jpg

    explode cái chuỗi trên với @ là ra đc array chứa 3 hình của sản phẩm! (@ có thể thay bằng các ký tự đặc biệt khác, miễn làm sao dự đoán đc cái ký tự đó ko bao giờ xuất hiện trong tên của hình là đc)

    Ngoài ra, còn có thể áp dụng cho 1 ví dụ cụ thể khác:

    – Nhân viên trong cty chịu sự quản lý của 1 hoặc nhiều người
    – nv này và quản lý của nv đó mới đc xem ds khách hàng của họ

    Theo lý thuyết, tạo 1 table users và 1 table user_manager để biểu diễn. Tuy nhiên, ta có thể dùng chuỗi để diễn đạt. Cụ thể, dùng 1 table users, có thêm cột manager, dữ liệu cột manager có dạng:

    ~1~2~3~4~

    Với ~ là ký tự ngăn cách, 1-2-3-4 là id của manager (hoặc thay bằng username)

    Chú ý, chuỗi này ko giống như ở trên, có thêm ký tự ngăn cách ở 2 đầu chuỗi, mục đích: hỗ trợ filter hoặc search.

    Ví dụ: trên ds lọc ra những người thuộc quyền quản lý của manager có id = 1, phép so sánh sẽ là: … LIKE ‘%~’ + id_manager + ‘~%’

    Nếu ko có dấu ~ ở đầu hoặc cuối thì sẽ ko lần ra đc id_manager ở đầu và cuối chuỗi.

    Vài dòng chia sẻ!

    [Reply]

    admin Reply:

    Cảm ơn những chia sẽ quý báu của bạn.

    [Reply]

  4. t4 says:

    theo mình thấy nếu làm như bạn duy tuyên nói thì sẽ hơi khó khăn trong việc xóa và sửa giá trị đó 🙁

    [Reply]

  5. boygiandi says:

    ” Ví dụ: trên ds lọc ra những người thuộc quyền quản lý của manager có id = 1, phép so sánh sẽ là: … LIKE ‘%~’ + id_manager + ‘~%’ ”

    Select có % ở đầu trên 1 bảng lớn là cực kì chậm kể cả khi trường đó đã được đánh index .

    [Reply]

    admin Reply:

    Cảm ơn bạn đã chia sẽ.

    [Reply]

  6. Thái Thanh Phong says:

    @Duy Tuyên :
    Như phần xử lý của anh . như vậy phải xử lý tên file ảnh , loại bỏ các kí tự được dùng để đánh dấu. Mình xinh chia sẽ . Chúng ta có thể lưu tên file ảnh vào 1 array .

    Ví dụ :

    $data = array(“ảnh_1.jpg”,”ảnh_2.jpg”,”ảnh_3.jpg);
    sau đó dùng hàm : serialize($data) sẽ biến array đó thành chuỗi có các kí tự đặc biệt. Sau đó ta sẽ lưu vào field cần thiết.

    Để hiển thị thì ta dùng unserialize($data) . Tức là dịch chuỗi kí tự đặc biệt thành array và hiển thị ra thôi.

    Vậy tóm lại là serialize để mã hóa dữ liệu , unserialize để giải mã dữ liệu . /o_o\

    [Reply]

  7. mrstham says:

    bạn có tài liệu kỹ hơn về dạng phi chuẩn thì cho mềnh xin nhé. Cám ơn nhiều

    [Reply]

  8. Tài says:

    Hôm qua mò vào table comment của wordpress, nay đọc được bài này của bác. Hiểu luôn :))

    [Reply]

Gởi bình luận