Sự khác nhau khi sử dụng Cascade Delete trong mysql?

Chào các bác!

Tình hình là hôm nay mình bổng dưng thắc mắc, sự khác nhau khi sử dụng Cascade Delete trong mysql.
Hiện tại khi xóa 1 record trong 1 table thì mình sẽ xóa các dữ liệu phụ thuộc ở các table liên quan.

Nếu dùng Cascade Delete thì khi xóa 1 record nó sẽ auto xóa luôn các dữ liệu phụ thuộc với table đó.

  • ưu điểm của cách này là nhanh, để mysql server nó gánh phần xóa, code dễ hơn.
  • Khuyết điểm: Chưa nghĩ ra

1 cách khác nếu không dùng Cascade Delete thì khi xóa 1 record mình sẽ xóa những thứ phụ thuộc trước rồi sau đó mới xóa record đó.

  • Ưu điểm: Không tìm ra nỗi cái ưu điểm nào -_-
  • Khuyết điểm: code cực, thêm table là phải đi sửa code, maintain cực.

Câu hỏi: Ngoại trừ những vấn đề về mặt code ra thì về bản chất 2 thằng này có khác nhau không? Vì cơ bản để xóa được 1 record thì vẫn phải xóa những thứ phụ thuộc trước khi xóa record đó.

1 Like

Đa phần trên prod chỉ soft delele thôi. ai cho xoá kiểu này đâu. mấy cái dự án đặc thù thì mới xoá kiểu này. và dùng casecade thì ok trong trường hợp này.

4 Likes

Ohm, tớ tưởng cậu đã thấy sự khác nhau về mặt bản chất rồi? :smile:
Về công việc phải làm, đúng là 2 bên đều đi tìm record phụ thuộc trước, sau đó mới xoá ở table chính. Cơ mà, nếu cậu để MySQL làm, một đống thao tác tìm kiếm trên index nữa cũng sẽ được thực thi.

Về ưu điểm của Cascade delete/update (hay nói đúng ra, sử dụng foreign key ở MySQL), tớ nghĩ cậu đúng. Cơ mà, về nhược điểm của việc sử dụng foreign key + cascade delete/update ở MySQL, tớ có thể liệt kê cho cậu một vài điểm như này:

  • Cascade delete/update sẽ thêm rất nhiều việc cho database engine, khi phải scan nhiều index ở các bảng khác nhau để xoá hay update dữ liệu.
    Ở các hệ thống hiện đại, rule of thumb là cậu sẽ cố gắng giảm tải nhất có thể cho database (nếu cậu dùng RDB). Lý do chủ yếu vì nó khó scale hơn so với scale application. Việc sử dụng foreign key + cascade delete/update nhìn chung sẽ thêm tương đối nhiều tải cho database khi cậu thực hiện việc này.
    Ngoài ra, như @nguyenhuuca đề cập ở trên, đa số các hệ thống đều sử dụng logical deletion (tạo 1 trường “is_deleted” để đánh dấu record đã bị xoá đi hay chưa). Mục đích của việc này nhằm phục vụ cho việc audit, operation (rollback nhìn chung sẽ dễ hơn), và cũng giảm tải cho database (nếu như cậu không biết, việc delete nhìn chung sẽ tốn kém hơn so với update 1 trường ở RDB, khi DB phải dọn dẹp tất cả các index của bảng trong TH delete). Các record logical deleted sẽ được dọn dẹp sau bằng các background batch.
  • Foreign key khiến cho schema của cậu không flexible. Tớ không nghĩ cậu sẽ “dễ maintain” schema có foreign key đâu.
    Thử tưởng tượng cậu cần phải thêm dữ liệu test cho 1 bảng có một mớ foreign key. Cuối cùng, cậu sẽ phải thêm dữ liệu test không chỉ cho 1 bảng mà cậu cần, mà toàn bộ model với tất cả những bảng liên quan khác.
    Điều tương tự cũng xảy ra khi cậu cần maintain index, hay nhiều khi, cậu không thể xoá nổi các dữ liệu cũ trước khi có foreign key (điều này xảy ra khá nhiều).
    So với việc cậu cài đặt logic xoá dữ liệu ở application (chỉ sử dụng transaction với các câu lệnh delete đơn giản), cậu chắc chắn sẽ không muốn sử dụng foreign key, trừ khi có yêu cầu đặc biệt nào đó.
    Cậu cũng phải tuân theo 1 loạt các điều kiện ràng buộc khác cho việc sử dụng foreign key + cascade delete/update (check Conditions and Restrictions).

Trong thực tế, với kinh nghiệm hạn hẹp của tớ, tớ chỉ thấy các hệ thống legacy với traffic rất thấp mới sử dụng foreign key (thực ra họ dùng gì cũng được :smile: miễn là thoả mãn requirement). Với các hệ thống yêu cầu cao, tớ không nghĩ benefit của foreign key + cascade delete/update outweight drawback của nó.

Hope it helps!

9 Likes

Vậy là trong thực tế người ta không sử dụng khoá ngoại.

Không hẳn cậu ạ :smile:

Các hệ thống monolithic cũ (quan sát của tớ thì là vài chục năm trở về trước, đặc biệt là các hệ thống finance, insurance,…) sử dụng khoá ngoài rất nhiều (họ cũng sử dụng cả trigger, store procedure, view, etc). Các hệ thống hiện đại với traffic lớn thường không sử dụng cách này.
Tớ cũng từng nhìn thấy một vài hệ thống (dùng Spring + Hibernate) sử dụng khoá ngoài được phát triển cách đây vài năm, cơ mà hệ thống này tương đối nhỏ (production của họ chỉ có 1 server + 1 database), dù rằng cá nhân tớ thấy họ chẳng có lý do gì chính đáng để phải sử dụng khoá ngoài cả.
Vậy nên, trong thực tế, khoá ngoài vẫn được sử dụng :smile:

7 Likes

Mặc dù mình đồng ý với nhiều phản hồi của @nguyenhuuca@library nhưng có nhiều điểm m thấy không hợp lý (dành cho bạn đặt câu hỏi và dành cho m)

Mình đã từng thử nghiệm logical delete cho codebase của công ty. Sau khoảng 6 tháng thì tụi m lại phải quay lại phương án sử dụng physical delete. Lý do bởi vì

  • Rất khó cho việc quản lý relationship. Standalone model thì hoàn toàn không có vấn đề gì, nhưng xủ lý parent/child trong logical delete thì rất khó
  • Database constraints, ví dụ bạn có nghiệp vụ phải thêm unique together cho 3 columns, khi thực hiện soft-delete thì bạn không thể thêm 1 record nữa vì dính constraint
  • Tụi m quay lại xử dụng log thay vì soft-delete

Cái này thì m hoàn toàn không đồng ý.

3 Likes

Dự án hiện tại của bạn, về lượng truy cập, lượng user, lượng row xóa như thế nào ?
Có những bảng nên và không nên, phải xác định rõ đó có phải là business logic hay không.
Khó maintain cho nhiều version, nhiều client khác nhau.

Bạn không đồng ý. Nói cho tụi mình biết lý do ?

1 Like

key đã được sinh ra đã dùng rồi thì sao lại dùng cho trường hợp khác được.
hơn nữa nếu muốn dùng lại trong trường hợp này, có thể dùng partial index cho nó.

1 Like

Yep.

Không hiểu ý của bạn là gì?

Foreign key là 1 phần của design, mình ko hiểu tại sao lại không flexible?
So với việc có fk và không có fk thì việc maintain dễ hơn rất nhiều, đó cũng là lý do để có fk ngoài việc data-consitency

M có 1 ví dụ business logic như sau:
Trong 1 lớp học tại trường X, có lớp Y và chỉ có 1 chủ nhiệm Z

CREATE TABLE classroom_teacher (
    school_id int references school(id),
    classroom_id int references classroom(id),
    teacher_id int reference teacher(id),
    is_deleted bool,
    
    UNIQUE (school_id, classroom_id, t̶e̶a̶c̶h̶e̶r̶_̶i̶d̶)
);

Nếu chủ nhiệm Z được soft-deleted thì không thể thêm chủ nhiệm cho lớp Y. Đây chỉ là ví dụ đơn giản, không phải usecase của tụi m. Usecase của tụi m phức tạp hơn nhiều vì table classroom_teacher có thể nằm ở giữa dependency list nên update object Z có thể tạo data corruption.

2 Likes

Nếu chủ nhiệm Z được soft-deleted.
Đã xoá có nghĩa là ko còn trong trường, sao lại cho chủ nhiệm lớp Y. cái này là phân tích nghiệp vụ bị sai rồi.

4 Likes

Nếu chủ nhiệm Z được soft-deleted cho lớp Y thì không thể thêm chủ nhiệm Z2 cho lớp Y. Vì lý do soft-delete record vẫn còn trong database nên khi thêm Z2 vào lớp Y sẽ gặp constraint. Dĩ nhiên bạn có thể workaround bằng cách update is_deleted cho Z2, nhưng khi đó:

  • Rất khó cho việc quản lý relationship. Standalone model thì hoàn toàn không có vấn đề gì, nhưng xủ lý parent/child trong logical delete thì rất khó

tất cả những dependency models của classroom_teacher phải soft-delete khi Z soft-deleted. Chưa kể đến việc khi update Z2, bạn có thể gặp vấn đề về data-corruption.

Trừ khi model của bạn quá đơn giản và ít relationship thì soft-delete có thể là ý hay, còn lại thì backup+logs+physical delete đơn giản và hiệu quả hơn!

May be a detailed explanation and its reasoning can be found here: https://stackoverflow.com/questions/2549839/are-soft-deletes-a-good-idea

1 Like
UNIQUE (school_id, classroom_id, t̶e̶a̶c̶h̶e̶r̶_̶i̶d̶)

Giờ không còn teacher làm unique nữa đúng không?
như vậy là bài toán assign giao viên vào lớp học. soft delete trên table sinh ra từ mối quan hệ thì ít người làm, toàn delete thẳng. chỉ làm soft delete trên table chính thôi.
Trường hợp muốn soft delete như trên mà vân assign bình thường thì có thể dùng patital index:

CREATE UNIQUE INDEX classroom_teacher ON classroom_teacher (school_id, classroom_id)
    WHERE is_deleted = false.;

Có những logic flow như xuất hoa đơn mà bi sai, rồi kiêu xoá , không ai physical delete đâu.
Tất nhiên tuỳ thuộc domain bạn làm gi có thể quyết định xoá hay ko xoá. Nó là vấn đề business không phải vấn đề kĩ thuật.

Tụi m cũng đã từng làm partial index (như trong link m gửi cũng có người đề xuất việc partial index) nhưng vẫn có quá nhiều drawback trong việc xử lý soft-delete. Bạn có thể đưa ra hàng loạt workaround để xử lý các drawback, cũng giống như lý do bạn cần soft-delete để audit/data safe thì hoàn toàn có thể xử lý đơn giản khi hard-delete.

Hmm, có vẻ bạn hơi chủ quan. Để nói “không ai” thực hiện physical delete thì cần nhiều hơn ý kiến của b đấy. Dĩ nhiên nó không phải vấn đề kỹ thuật, m cũng không khẳng định soft-delete là không khả thi.

Trừ khi model của bạn quá đơn giản và ít relationship thì soft-delete có thể là ý hay, còn lại thì backup+logs+physical delete đơn giản và hiệu quả hơn !

Cảm ơn cậu đã bày tỏ quan điểm nha :smile:

Tớ đồng ý với cậu rằng logical deletion không thể áp dụng trong TH của cậu (có requirement về unique key). Logical deletion không phải silver bullet, nó có nhiều nhược điểm, như mọi quyết định kỹ thuật khác.
Ngoài ra, trong thực tế tớ cũng từng thấy các hệ thống sử dụng kết hợp giữa cả 2 phương pháp: logical deletion khi cần xoá toàn bộ model, và physical deletion với TH xoá các bảng kết nối. Đó cũng là một lựa chọn :smile:

Uhm, foreign key đúng là 1 phần của database schema design, cơ mà tớ hi vọng cậu đồng ý là không nhất thiết phải sử dụng tính năng foreign key của database manager system để mô tả foreign key :smile:
Về lý do nó không flexible khi maintain (tớ nên đề cập là database maintain, không phải code maintain :sweat_smile:), tớ cũng có đề cập một ví dụ ở comment trước. Vấn đề chủ yếu của tính năng foreign key là việc nó yêu cầu dữ liệu phải được thêm vào/xoá đi theo đúng tuần tự, và nó cũng kiểm tra ràng buộc bất cứ thời điểm thêm/sửa/xoá dữ liệu nào, để đảm bảo data integrity. Cá nhân tớ nghĩ, ràng buộc này không cần thiết phải để ở phía database, nó chỉ khiến cho nhiều thao tác maintain nào trên database khó khăn hơn thôi.
Ngoài ra, data integrity thực ra có thể đạt được một cách logic với transaction ở application rồi :smile:

Tớ đồng ý với cậu, các model đơn giản thường hay dùng soft-deletion. Do vậy, nó thường xuất hiện nhiều khi sử dụng microservice, khi các model của từng service thường đơn giản hơn.
Về link stackoverflow, cậu có thể thấy 2 luồng ý kiến về việc này. Câu hỏi đó được đóng lại vì các câu trả lời đều là opinion-based, và cậu có thể thấy nó đang được tiếp diễn ở đây :smile:

Bất cứ lựa chọn kỹ thuật nào cũng có trade-off, và quyết định đó tốt hay không phụ thuộc vào từng hoàn cảnh cụ thể. Trong model của cậu, tớ đồng ý việc sử dụng logical deletion không phải lựa chọn tốt.

1 Like

Ý anh là chỉ nên xem database là chỗ chứa data thôi, còn quan hệ giữa data với nhau, ràng buộc data, validate, …. đều do code java, C#, nodejs, PHP…. thực hiện hết đúng không anh?

Mình chỉ đi ngang qua và thấy các bạn comment các công ty dùng soft-delete vs NoFK, trong khi thực tế chắc phải 80% [0] các công ty và lập trình viên làm hard-delete+FK nên m phản hồi thôi.
M cũng đang là SA trong công ty, tụi m áp dụng cả ACID và eventual consistency cho các module khác nhau nên m cũng không có vấn đề gì về FK/NoFK & SQL/NoSQL.

[0] Mình ước lượng thôi, không biết con số chính xác. Nhưng nếu bạn tìm kiếm toàn bộ tutorial+course và nature của các ứng dụng trên internet thì 80% khá conservative.

2 Likes

Ừ cậu. Cơ mà tớ không có ý “chỉ nên”, đó là 1 lựa chọn kỹ thuật có ưu điểm và nhược điểm :smile:
Cơ mà tớ không nghĩ nó liên quan tới việc database first hay code first đâu :smile:

1 Like

Cảm ơn cậu đã phản hồi nhé! :smile:
Tớ nghĩ cậu đúng, thậm chí chắc phải hơn 80% các sản phẩm phần mềm vẫn dùng hard deletion với FK. Nó tự nhiên và dễ hơn trong quá trình thiết kế và cài đặt :smile:

2 Likes
83% thành viên diễn đàn không hỏi bài tập, còn bạn thì sao?