Soft delete và các vấn đề liên quan

MỖI BÀI VIẾT, MỘT ĐIỀU HỌC ĐƯỢC
← Tất cả bài viết

Soft delete và các vấn đề liên quan

Trao đổi về bài viết ↓
Trong bài viết này

Giới thiệu

Đôi khi bạn muốn xoá một cái gì đó trong DB của bạn nhưng bạn không thật sự xoá nó. điều này sẽ giúp bạn dễ dàng khôi phục lại dữ liệu bị xoá và cách làm này được gọi là xoá mềm - soft delete.
Khác với xoá cứng - hard delelte bạn sẽ khó phục hồi dữ liệu ngay mà cần có một bản backup dữ liệu định kỳ để restore, thì soft delete lại tiện dụng hơn khi có thể restore lại những sai lầm trong quá trình xoá dữ liệu ngay lập tức.
Để làm được điều này, bạn chỉ cần thêm một trường để đánh dấu giá trị đó đã bị xoá bằng cờ is_deleted hoặc deleted_at cho mỗi row.

Những điều nên tránh khi sài soft delete.

  • Điều đầu tiên gây khó chịu khi sài soft delete là bất kể một câu query nào, bạn cũng phải filter với điều kiện deleted_at is null hoặc is_delete == false, chưa kể đến việc gây nhầm lẫn giữa update và delete.
  • Việc thứ hai là nó không thực sự được xoá trong database, mà đúng nghĩa hơn nó là cập nhật lại giá trị deleted_at hoặc is_delete, điều này gây ra sự thiếu logic, vì trong một bảng sẽ lẫn lộn giữa các giá trị xoá và giá trị valid. Điều này làm cho bảng của chúng ta không khác gì một bãi rác.
  • Điều tiếp theo là, các truy vấn tới các bảng liên kết thông qua khoá ngoại sẽ tăng chi phí filter dữ liệu.
  • Các vấn đề về ràng buộc dữ liệu ví dụ dữ liệu unique, nếu trong bảng của bạn có một trường là unique sẽ ra sao nếu một giá trị nào đó không thực sự xoá khỏi bảng. Sẽ phát sinh lỗi key ... already exitsts.
  • Và một vấn đề nữa khi CSDL của bạn lớn dần lên, các chi phí cho filter là cực kì lớn chưa kể đến vấn đề tốn dung lượng.

Giải pháp cho soft delete.

  • Để giải quyết vấn đề unique:
    • Chúng ta có thể bỏ hẳn trường unique.
    • Viết một trigger, xét unique_together(trường_unique,deleted_at) để loại bỏ lỗi  key ... already exitsts.
  • Còn về vấn đề các dữ liệu đã xoá và valid còn trong CSDL thì chúng ta:
    • Clone một Table hard delete có schemas tương tự với Table mà ta soft delete. Nhìn chung cách này chúng ta phải đảm bảo giữa bản xoá cứng và xoá mềm phải có cùng schemas các trường dữ liệu.
    •  Tạo một bảng ghi lại lịch sự hoạt động của các tiến trình thêm sửa xoá trên Database.
  • Các vấn đề về tăng chi phí filter, bắt buộc ta phải đánh index hoặc index_together để làm giảm thời lượng tìm kiếm. Mặc khác sẽ làm tăng thời gian insert và update.
  • Để hạn chế việc nhầm lẫn giữa update và delete, hay bất kể câu query nào cũng phải filter ta nên phát triển BaseModels và các action liên quan cho nó sẽ được đề cập sau.

Xây dựng BaseModels cho soft delete.

Tạo class SoftDeleteModel:
    class SoftDeleteModel(models.Model):
        deleted_at = models.DateTimeField(null=True, default=None)
        objects = SoftDeleteManager()
        default_objects = models.Manager()
        def soft_delete(self): 
           self.deleted_at = timezone.now() 
           self.save() 
        def restore(self): 
           self.deleted_at = None 
           self.save() 
        class Meta: 
           abstract = True 
    
    Việc sử dụng abstract, cho django biết rằng đây chỉ là một lớp ảo và sẽ không thực thi.tạo bảng trên database.
    Đề hạn chế việc lúc nào cũng phải bổ sung điều kiện filter, ta sẽ ghi đè lại trình quản lý objects để tương tác với database.
    class SoftDeleteManager(models.Manager): 
        def get_queryset(self):
           return super().get_queryset().filter(deleted_at__isnull=True)
    Bổ sung thêm điều kiện query deleted_at is Null cho tất cả điều kiện filter. Tương tự ta cũng có thể overriding lại các phương thức update và delete cho soft delete thông qua trình quản lý, mà không cần thông qua phương thức soft_delete hoặc restore.

    Chúng ta sẽ tạo bảng sử dụng soft delete:
    class Todo(SoftDeleteModel):
        user = models.ForeignKey(User, on_delete=models.CASCADE, related_name="todos")     title = models.CharField(max_length=32)
    class User(SoftDeleteModel): 
        name = models.CharField(max_length=255)
    Như vậy khi ta tạo một đối tượng ToDo và user:
    sample_user = User.objects.create_user('sample_user') 
    todo_1 = Todo.objects.create(user=sample_user, title="quet nha") todo_2 = Todo.objects.create(user=sample_user, title="nau com")
    Ta thực hiện xoá todo_1

    todo_1.soft_delete()
    Khi thực hiện truy vấn hay truy vấn trên todo.
    sample_user.todos.all()
    todo.objects.all()
    Ta chỉ thấy hiển thị todo_2.

    Tổng kết.

    • Nhìn chung soft delete có rất nhiều vấn đề bất cập theo thời gian.
    • Từ những người mới đọc code sẽ rất khó hình dung ra ngữ cảnh ràng buộc dữ liệu.
    • Rất gây dễ hiểu nhầm cho vấn đề update hoặc delete.
    • Hầu như tất cả các quá trình lọc dữ liệu đều phải thêm trường deleted_at is null để lấy ra dữ liệu chưa xoá. Rất dễ nhầm lẫn và gây khó hiểu nếu không xây dựng một baseModel chung để xử lý cho soft delete.
    • Nhưng ưu điểm của nó cũng rất lớn, với khả năng khôi phục dữ liệu cực kì nhanh. bạn cũng không cần phải backup dữ liệu một cách thường xuyên. Vì cơ bản, bạn không có khả năng xoá bất cứ thứ gì khi thao tác với database trên phần mềm.
    Cảm ơn bạn đã ghé đọc. Nếu có một góc nhìn khác, hãy chia sẻ ở phần bình luận nhé.

    Cùng trao đổi

    Bình luận bằng tài khoản Google qua Blogger.

    Nhận xét