Làm thế nào để xóa nên được xử lý trong cơ sở dữ liệu?


44

Tôi muốn triển khai tính năng "hủy xóa" trong ứng dụng web để người dùng có thể thay đổi ý định và khôi phục bản ghi đã bị xóa. Suy nghĩ về cách thực hiện điều này? Một số tùy chọn tôi đã xem xét thực sự đang xóa bản ghi đang đề cập và lưu trữ các thay đổi trong một bảng kiểm toán riêng hoặc không xóa bản ghi và sử dụng cột "đã xóa" boolean để đánh dấu nó là đã xóa. Giải pháp sau sẽ yêu cầu logic ứng dụng bổ sung để bỏ qua các bản ghi "đã xóa" trong các trường hợp thông thường, nhưng sẽ giúp thực hiện khôi phục các bản ghi ở phía ứng dụng dễ dàng hơn nhiều.


Tôi quên đề cập rằng trong trường hợp thứ hai, các bản ghi được gắn cờ sẽ cần phải bị xóa hoặc di chuyển sau một khoảng thời gian trôi qua hợp lý.
Abie

Bạn đang dùng gói dữ liệu nào vậy?
Evan Carroll

Bảng tạm thời là một giải pháp tốt nhất cho SQL Server 2016 trở lên.
Sameer

Câu trả lời:


37

Vâng, tôi chắc chắn sẽ chọn tùy chọn thứ hai, nhưng tôi sẽ thêm một trường nữa vào trường ngày.

Vì vậy, bạn thêm:

delete       boolean
delete_date  timestamp

Nó sẽ cho phép bạn dành thời gian cho hành động chưa hoàn thành.

Nếu thời gian ít hơn một giờ người ta có thể phục hồi.

Để thực sự xóa mục đã xóa, chỉ cần tạo một quy trình được lưu trữ sẽ xóa mọi mục nhập với phần xóa được đặt thành đúng và thời gian lớn hơn một giờ và đặt nó dưới dạng tab cron chạy mỗi 24 giờ

Giờ chỉ là một ví dụ.


Ngoài ra, bạn có thể có một cờ khác - cleanedhoặc một cái gì đó - cho biết rằng dữ liệu được liên kết với bản ghi này đã được xóa một cách chính xác, toàn diện. Hồ sơ có thể được phục hồi trừ khi cleanedlà đúng, trong trường hợp đó là không thể phục hồi.
Gaurav

14
Đây là cách tiếp cận phổ biến. Tôi thường sử dụng một trường deleted_atchứa cả ngữ nghĩa của deleteboolean và delete_datedấu thời gian. Nếu deleted_atNULLxử lý các trường hợp deleteFALSEdelete_dateNULL, deleted_atcó chứa một tay cầm timestamp trường hợp deleteTRUEdelete_datechứa một dấu thời gian, giúp bạn tiết kiệm thời gian, lưu trữ và ứng dụng logic.
Julien

1
Tôi thích trường boolean và ngày. Tùy thuộc vào cách bạn triển khai logic xóa, bạn thậm chí có thể có một bảng riêng biệt chứa ngày và khóa duy nhất cho bản ghi đã bị "xóa". Thủ tục lưu trữ làm cho điều này dễ dàng. Phải mất thêm không gian cho mỗi hàng yêu cầu xuống còn 1 bit so với 8+. Bạn cũng có thể báo cáo về việc xóa mỗi ngày mà không cần chạm vào bảng nguồn.
AndrewQuery

Lưu ý: xóa là một từ dành riêng trong MySQL.
Jason Rikard

Hãy nhớ rằng một chỉ mục được lọc trên deletedtrường của bạn có thể cải thiện hiệu suất rất nhiều khi bạn truy vấn các hàng không bị xóa
Ross Presser

21

Trong các ứng dụng của chúng tôi, chúng tôi không thực sự xóa bất cứ điều gì theo yêu cầu của người dùng (khách hàng của chúng tôi đang ở trong môi trường được quy định trong đó việc xóa bất cứ điều gì có thể dẫn đến các vấn đề pháp lý).

Chúng tôi giữ các phiên bản cũ hơn trong một bảng kiểm toán riêng (vì vậy đối với bảng some_table cũng là một bảng có tên some_table_audit) giống hệt với việc có một định danh phiên bản bổ sung (dấu thời gian nếu DB của bạn cung cấp giá trị thời gian đủ chi tiết, số phiên bản số nguyên hoặc UUID là khóa ngoại đối với bảng kiểm toán chung, v.v.) và tự động cập nhật bảng kiểm toán bằng trình kích hoạt (vì vậy chúng tôi không cần phải tạo tất cả các mã cập nhật các hồ sơ nhận biết về yêu cầu kiểm toán).

Cách này:

  • thao tác xóa chỉ là một thao tác xóa đơn giản - không cần thêm bất kỳ mã nào vào đó (mặc dù bạn có thể muốn ghi lại ai đã yêu cầu những hàng nào sẽ bị xóa, ngay cả khi chúng không thực sự bị xóa)
  • chèn và cập nhật tương tự đơn giản
  • bạn có thể thực hiện khôi phục hoặc hoàn nguyên bằng cách chỉ trả lại hàng "bình thường" cho phiên bản cũ (trình kích hoạt kiểm toán sẽ kích hoạt lại để bảng theo dõi kiểm toán cũng phản ánh thay đổi này)
  • bạn có thể cung cấp cơ hội để xem xét hoặc hoàn nguyên về bất kỳ phiên bản nào trong quá khứ, không chỉ phục hồi phiên bản cuối cùng
  • bạn không phải thêm "được đánh dấu là đã xóa?" kiểm tra mọi điểm mã liên quan đến bảng đang đề cập hoặc logic "cập nhật bản sao kiểm toán" cho mọi điểm mã xóa / cập nhật các hàng (mặc dù bạn cần quyết định phải làm gì với các hàng bị xóa trong bảng kiểm toán: chúng tôi có một đã xóa / không gắn cờ cho mỗi phiên bản ở đó để không có lỗ hổng trong lịch sử nếu các bản ghi bị xóa và sau đó không bị xóa)
  • giữ các bản sao kiểm toán trong một bảng riêng biệt có nghĩa là bạn có thể phân chia chúng thành các nhóm khác nhau một cách dễ dàng.

Nếu sử dụng dấu thời gian thay vì (hoặc cũng như) số phiên bản số nguyên, bạn có thể sử dụng số này để xóa các bản sao cũ sau một khoảng thời gian đã đặt nếu cần. Nhưng dung lượng ổ đĩa tương đối rẻ trong những ngày này vì vậy trừ khi chúng tôi có lý do để bỏ dữ liệu cũ (tức là các quy định bảo vệ dữ liệu nói rằng bạn nên xóa dữ liệu khách hàng sau X tháng / năm), chúng tôi sẽ không.


Câu trả lời này đã được khoảng một vài năm và một vài điều quan trọng có thể ảnh hưởng đến loại kế hoạch này đã thay đổi kể từ đó. Tôi sẽ không đi sâu vào chi tiết lớn, nhưng thật vui vì lợi ích của những người đọc nó ngày hôm nay:

  • SQL Server 2016 đã giới thiệu "các bảng tạm thời được phiên bản hệ thống", thực hiện rất nhiều công việc này cho bạn và hơn thế nữa, khi một số đường cú pháp đẹp được cung cấp để làm cho các truy vấn lịch sử dễ dàng hơn để xây dựng & duy trì và chúng phối hợp một tập hợp con các thay đổi lược đồ giữa bảng cơ sở và lịch sử. Họ không phải không có sự cẩn thận của họ, nhưng họ là một công cụ mạnh mẽ cho mục đích này. Các tính năng tương tự cũng có sẵn trong các hệ thống DB khác.

  • Những thay đổi về luật bảo vệ dữ liệu, cụ thể là việc giới thiệu GDPR, có thể thay đổi đáng kể vấn đề khi nào dữ liệu sẽ bị xóa cứng. Bạn phải cân nhắc sự cân bằng của việc không xóa dữ liệu có thể hữu ích (hoặc, thực sự, được yêu cầu về mặt pháp lý) cho mục đích kiểm toán vào một ngày sau đó, không cần phải tôn trọng quyền của người dân (nói chung và như được quy định cụ thể trong luật pháp liên quan) khi xem xét thiết kế của bạn. Đây có thể là một vấn đề với các bảng tạm thời được phiên bản hệ thống vì bạn không thể sửa đổi lịch sử để lọc dữ liệu cá nhân mà không thay đổi lược đồ ngắn để tắt theo dõi lịch sử trong khi bạn thực hiện thay đổi.


Làm thế nào để bạn đối phó với việc xóa và đổi tên cột? Đặt mọi thứ thành nullable?
Stijn

1
@Stijn: Không thường xuyên thay đổi cấu trúc để không xuất hiện nhiều. Colunms thường không bao giờ bị xóa sau khi chúng tồn tại trong sản xuất - nếu chúng không được sử dụng, chỉ cần bỏ bất kỳ ràng buộc nào sẽ ngăn chặn NULL (hoặc thêm mặc định để xử lý các ràng buộc bằng cách sử dụng "giá trị ma thuật", mặc dù điều đó cảm thấy bẩn hơn) và ngừng đề cập đến chúng trong mã khác. Để đổi tên: thêm mới, ngừng sử dụng dữ liệu cũ và sao chép dữ liệu từ cũ sang mới nếu cần. Nếu bạn đổi tên các cột, chỉ cần đảm bảo cùng một thay đổi được thực hiện cho cả bảng cơ sở và bảng kiểm toán cùng một lúc.
David Spillett

9

Với cột đã xóa boolean, bạn sẽ bắt đầu gặp vấn đề nếu bảng của bạn bắt đầu phát triển và trở nên thực sự lớn. Tôi đề nghị bạn di chuyển các cột bị xóa mỗi tuần một lần (nhiều hay ít tùy thuộc vào thông số kỹ thuật của bạn) sang một bảng khác. Bằng cách đó, bạn có một bảng hoạt động nhỏ đẹp và một bảng lớn chứa tất cả các bản ghi được thu thập theo thời gian.


7

Tôi sẽ đi với bảng riêng. Ruby on Rails có một acts_as_versionedplugin, về cơ bản lưu một hàng vào một bảng khác với hậu tố _versiontrước khi cập nhật nó. Mặc dù bạn không cần hành vi chính xác đó, nhưng nó cũng sẽ hoạt động cho trường hợp của bạn (sao chép trước khi xóa).

Giống như @Spredzy, tôi cũng khuyên bạn nên thêm một delete_datecột để có thể xóa các bản ghi theo chương trình chưa được khôi phục sau X giờ / ngày / bất cứ điều gì.


4

Giải pháp chúng tôi sử dụng nội bộ cho vấn đề này là có một cột trạng thái với một số giá trị được mã hóa cứng cho một số trạng thái cụ thể của đối tượng: Đã xóa, Hoạt động, Không hoạt động, Mở, Đóng, Bị chặn - mỗi trạng thái có ý nghĩa được sử dụng trong ứng dụng. Từ quan điểm db, chúng tôi không xóa đối tượng, chúng tôi chỉ thay đổi trạng thái và giữ lịch sử cho từng thay đổi trong bảng đối tượng.


3

Khi bạn nói rằng "Giải pháp sau sẽ yêu cầu logic ứng dụng bổ sung để bỏ qua các bản ghi 'đã xóa", giải pháp đơn giản là có chế độ xem lọc chúng ra.


Đó không chỉ là vấn đề quan điểm. Bất kỳ thao tác nào đang được thực hiện trên tập hợp sẽ phải loại trừ các bản ghi "đã xóa".
Abie

2

Tương tự như những gì Spredzy đề xuất, chúng tôi sử dụng trường dấu thời gian để xóa trong tất cả các ứng dụng của mình. Boolean là không cần thiết, vì dấu thời gian được đặt cho biết rằng bản ghi đã bị xóa. Bằng cách này, PDO của chúng tôi luôn thêm AND (deleted IS NULL OR deleted = 0)vào các câu lệnh được chọn, trừ khi mô hình yêu cầu rõ ràng các bản ghi bị xóa được đưa vào.

Chúng tôi hiện không thu gom rác trên bất kỳ bảng nào ngoại trừ các bảng có đốm màu hoặc văn bản; không gian là không đáng kể nếu các bản ghi được chuẩn hóa tốt và việc lập chỉ mục deletedtrường làm cho tác động hạn chế đến tốc độ chọn.


0

Bạn có thể thay thế đặt onus cho người dùng (và nhà phát triển) và đi theo một chuỗi 'Bạn có chắc không?', 'Bạn có chắc chắn không?' và 'Bạn có dứt khoát, tốt và thực sự chắc chắn?' câu hỏi trước khi hồ sơ bị xóa. Nhẹ nhàng nhưng đáng để xem xét.


0

Tôi đã quen nhìn thấy các hàng của bảng có các cột như 'DeletingDate' trong đó và tôi không thích chúng. Khái niệm 'bị xóa' là mục nhập không nên được thực hiện ngay từ đầu. Thực tế, chúng không thể bị xóa khỏi cơ sở dữ liệu nhưng tôi không muốn chúng vào với dữ liệu nóng của tôi. Theo định nghĩa, các hàng bị xóa là dữ liệu lạnh trừ khi có người đặc biệt muốn xem dữ liệu bị xóa.

Hơn nữa, mọi truy vấn được viết phải loại trừ chúng một cách cụ thể và các chỉ mục cũng cần phải xem xét chúng.

Những gì tôi muốn thấy là một sự thay đổi ở cấp kiến ​​trúc cơ sở dữ liệu và cấp độ ứng dụng: tạo một lược đồ có tên là 'đã xóa'. Mỗi bảng do người dùng xác định có một tương đương giống hệt nhau trong lược đồ 'đã xóa' với một siêu dữ liệu giữ trường bổ sung - người dùng đã xóa nó và khi nào. Khóa ngoại được yêu cầu phải được tạo.

Tiếp theo, xóa sẽ trở thành chèn-xóa. Đầu tiên, hàng cần xóa sẽ được chèn vào đối tác lược đồ 'đã xóa. Hàng trong câu hỏi trong bảng chính sau đó có thể bị xóa. Logic bổ sung, tuy nhiên, cần phải được thêm vào ở đâu đó dọc theo dòng. Vi phạm khóa nước ngoài có thể được xử lý.

Khóa ngoại phải được xử lý đúng. Đó là một thực tế xấu khi có một hàng bị xóa một cách hợp lý nhưng có chính / duy nhất có các cột trong các bảng khác đề cập đến nó. Điều này không nên xảy ra. Một công việc thông thường có thể loại bỏ các hàng góa phụ (các hàng có khóa chính không có tham chiếu trong các bảng khác mặc dù có khóa ngoại. Tuy nhiên, đây là logic nghiệp vụ.

Lợi ích tổng thể là giảm siêu dữ liệu trong bảng và cải thiện hiệu suất mà nó mang lại. Cột 'removeDate' nói rằng hàng này thực sự không nên ở đây, nhưng để thuận tiện, chúng tôi để nó ở đó và để truy vấn SQL xử lý nó. Nếu một bản sao của hàng đã xóa được giữ trong lược đồ 'đã xóa, thì bảng chính có dữ liệu nóng có tỷ lệ dữ liệu nóng cao hơn (giả sử nó được lưu trữ theo cách kịp thời) và ít cột siêu dữ liệu không cần thiết. Chỉ mục & truy vấn không còn cần phải xem xét lĩnh vực này. Kích thước hàng càng ngắn, càng có nhiều hàng được trang bị trên một trang, SQL Server có thể hoạt động nhanh hơn.

Nhược điểm chính là kích thước của hoạt động. Bây giờ có hai hoạt động thay vì một cũng như xử lý logic và xử lý lỗi bổ sung. Nó có thể dẫn đến khóa nhiều hơn so với cập nhật một cột nếu không sẽ mất. Giao dịch giữ các khóa trên bàn lâu hơn và có hai bảng liên quan. Xóa dữ liệu sản xuất, ít nhất là theo kinh nghiệm của tôi, là điều hiếm khi được thực hiện. Thậm chí, trong một trong các bảng chính, 7,5% trong số gần 100 triệu mục có một mục trong cột 'DeletingDate'.

Để trả lời cho câu hỏi, ứng dụng sẽ phải nhận thức được 'không phục hồi. Đơn giản chỉ cần thực hiện tương tự theo thứ tự ngược lại: chèn hàng từ lược đồ 'đã xóa' vào bảng chính và sau đó xóa hàng khỏi 'lược đồ đã xóa. Một lần nữa, một số xử lý logic & lỗi bổ sung là cần thiết để đảm bảo tránh các lỗi, sự cố với khóa ngoại và tương tự.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.