Xử lý người dùng bị xóa - tách hoặc cùng bảng?


19

Kịch bản là tôi đã có một nhóm người dùng mở rộng và khi thời gian trôi qua, người dùng sẽ hủy tài khoản của họ mà chúng tôi hiện đánh dấu là 'đã xóa' (có cờ) trong cùng một bảng.

Nếu người dùng có cùng địa chỉ email (đó là cách người dùng đăng nhập) muốn tạo tài khoản mới, họ có thể đăng ký lại, nhưng tài khoản MỚI được tạo. (Chúng tôi có id duy nhất cho mọi tài khoản, vì vậy địa chỉ email có thể được sao chép giữa các tài khoản trực tiếp và bị xóa).

Điều tôi nhận thấy là tất cả các hệ thống của chúng tôi, trong quá trình bình thường, chúng tôi liên tục truy vấn bảng người dùng kiểm tra người dùng không bị xóa, trong khi điều tôi nghĩ là chúng tôi không cần phải làm điều đó ... ! [Làm rõ1: bằng cách 'truy vấn liên tục', ý tôi là chúng ta có các truy vấn giống như: '... TỪ người dùng WHERE isdelatted = "0" VÀ ...'. Ví dụ: chúng tôi có thể cần tìm nạp tất cả người dùng đã đăng ký cho tất cả các cuộc họp vào một ngày cụ thể, vì vậy trong truy vấn THAT, chúng tôi cũng có TỪ người dùng WHERE isdelatted = "0" - điều này có làm cho quan điểm của tôi rõ ràng hơn không?]

(1) continue keeping deleted users in the 'main' users table
(2) keep deleted users in a separate table (mostly required for historical
    book-keeping)

Những ưu và nhược điểm của một trong hai cách tiếp cận là gì?


Vì lý do gì bạn giữ người dùng?
keppla

2
Điều này được gọi là mềm-xóa. Xem thêm Xóa hồ sơ cơ sở dữ liệu unpermenantley (xóa mềm)
Sjoerd

@keppla - ông đề cập rằng: "lưu giữ sách lịch sử".
ChrisF

@ChrisF: tôi quan tâm đến phạm vi: anh ấy có muốn giữ sách chỉ cho người dùng hay vẫn còn một số dữ liệu được đính kèm (nhận xét eG, thanh toán, v.v.)
keppla

Có thể giúp ngừng suy nghĩ về việc họ đã bị xóa (điều này không đúng) và bắt đầu nghĩ tài khoản của họ bị hủy (điều này là đúng).
Mike Sherrill 'Nhớ lại mèo'

Câu trả lời:


13

(1) tiếp tục giữ người dùng bị xóa trong bảng người dùng 'chính'

  • Ưu điểm: truy vấn đơn giản hơn trong mọi trường hợp
  • Nhược điểm: có thể làm giảm hiệu suất theo thời gian, nếu có nhiều người dùng

(2) giữ người dùng bị xóa trong một bảng riêng biệt (chủ yếu là bắt buộc đối với việc ghi sổ lịch sử)

Bạn có thể sử dụng ví dụ như một trình kích hoạt để tự động chuyển người dùng đã xóa sang bảng lịch sử.

  • Ưu điểm: bảo trì đơn giản hơn cho bảng người dùng hoạt động, hiệu suất ổn định
  • Nhược điểm: cần các truy vấn khác nhau cho bảng lịch sử; tuy nhiên vì hầu hết các ứng dụng không được quan tâm đến điều đó, nên hiệu ứng tiêu cực này có thể bị hạn chế

11
Một bảng phân vùng (trên IsDelatted) sẽ loại bỏ các vấn đề về hiệu năng khi sử dụng một bảng duy nhất.
Ian

1
@Ian trừ khi mọi truy vấn được cung cấp với IsDelatted làm tiêu chí truy vấn (có vẻ như không có trong câu hỏi ban đầu), phân vùng thậm chí có thể làm giảm hiệu suất.
Adrian Shum

1
@Adrian, tôi đã giả sử rằng các truy vấn phổ biến nhất sẽ có tại thời điểm đăng nhập và chỉ những người dùng bị xóa mới được phép đăng nhập.
Ian

1
Sử dụng chế độ xem được lập chỉ mục trên đã bị xóa nếu nó trở thành vấn đề về hiệu năng và bạn muốn lợi ích của một bảng duy nhất.
JeffO

10

Tôi thực sự khuyên bạn nên sử dụng cùng một bảng. Lý do chính là tính toàn vẹn dữ liệu. Nhiều khả năng sẽ có nhiều bảng có mối quan hệ tùy thuộc vào người dùng. Khi người dùng bị xóa, bạn không muốn để những hồ sơ đó mồ côi.
Có những hồ sơ mồ côi vừa làm cho việc thực thi các ràng buộc trở nên khó khăn hơn, vừa khiến việc tìm kiếm thông tin lịch sử trở nên khó khăn hơn. Các hành vi khác cần xem xét khi người dùng cung cấp email đã sử dụng nếu bạn muốn họ khôi phục tất cả các hồ sơ cũ của họ. Điều này sẽ hoạt động tự động bằng cách sử dụng xóa mềm. Theo như mã hóa, chẳng hạn trong ứng dụng c # linq hiện tại của tôi, mệnh đề ở đâu đã xóa = 0 được tự động thêm vào cuối tất cả các truy vấn


7

"Điều tôi nhận thấy là tất cả trên toàn hệ thống của chúng tôi, trong quá trình bình thường, chúng tôi liên tục truy vấn bảng người dùng để kiểm tra người dùng không bị xóa"

Điều này mang lại cho tôi một mùi hôi của thiết kế. Bạn nên ẩn một loại logic như vậy. Ví dụ: bạn nên có một UserServicephương thức cung cấp phương thức isValidUser(userId)sử dụng "trên toàn hệ thống của bạn", thay vì làm một việc như:

"lấy bản ghi người dùng, kiểm tra xem người dùng có bị đánh dấu là đã xóa không".

Cách của bạn để lưu trữ người dùng đã xóa sẽ không ảnh hưởng đến logic kinh doanh.

Với kiểu đóng gói như vậy, lập luận trên sẽ không còn ảnh hưởng đến cách tiếp cận của bạn. Sau đó, bạn có thể tập trung nhiều hơn vào những ưu và nhược điểm liên quan đến bản thân sự kiên trì.

Những điều cần xem xét bao gồm:

  • Bao lâu thì bản ghi bị xóa thực sự bị thanh trừng?
  • Tỷ lệ hồ sơ bị xóa là gì?
  • Sẽ có một vấn đề cho tính toàn vẹn tham chiếu (ví dụ: người dùng được giới thiệu từ bảng khác) nếu bạn thực sự xóa nó khỏi bảng?
  • Bạn đang xem xét mở lại người dùng?

Thông thường tôi sẽ có một cách kết hợp:

  1. Đánh dấu bản ghi là đã xóa (để giữ nó cho yêu cầu chức năng, như mở lại ac hoặc kiểm tra ac đã đóng gần đây).
  2. Sau một khoảng thời gian được xác định trước, di chuyển bản ghi đã xóa vào bảng lưu trữ (cho mục đích ghi sổ).
  3. Làm sạch nó sau một số thời gian lưu trữ được xác định trước.

1
[Làm rõ1: bằng cách 'truy vấn liên tục', ý tôi là chúng ta có các truy vấn giống như: '... TỪ người dùng WHERE isdelatted = "0" VÀ ...'. Ví dụ: chúng tôi có thể cần tìm nạp tất cả người dùng đã đăng ký cho tất cả các cuộc họp vào một ngày cụ thể, vì vậy trong truy vấn THAT, chúng tôi cũng có TỪ người dùng WHERE isdelatted = "0" - điều này có làm cho quan điểm của tôi rõ ràng hơn không?] @Adrian
Alan Beats

Yup rõ ràng hơn nhiều. :) Nếu tôi đang làm điều đó, tôi thà làm cho nó thay đổi trạng thái của người dùng, thay vì xem nó như là xóa vật lý / logic. Mặc dù số lượng mã sẽ không giảm ("và isDelatted = '0'" so với 'và "state <>' TERMINATED '") nhưng mọi thứ sẽ có vẻ hợp lý hơn nhiều và cũng bình thường khi có trạng thái người dùng khác. Cũng có thể thực hiện thanh lọc định kỳ người dùng TERMINATED, như được đề xuất trong câu trả lời trước của tôi)
Adrian Shum

5

Để trả lời đúng câu hỏi này, trước tiên bạn cần quyết định: "Xóa" nghĩa là gì trong ngữ cảnh của hệ thống / ứng dụng này?

Để trả lời câu hỏi đó , bạn cần trả lời một câu hỏi khác: Tại sao hồ sơ bị xóa?

Có một số lý do chính đáng tại sao người dùng có thể cần xóa dữ liệu. Thông thường tôi thấy rằng có chính xác một lý do (mỗi bảng) tại sao việc xóa có thể cần thiết. Một số ví dụ:

  • Để lấy lại không gian đĩa;
  • Xóa cứng cần thiết theo chính sách bảo mật / quyền riêng tư;
  • Dữ liệu bị hỏng / vô vọng không chính xác, dễ xóa và tái tạo hơn là sửa chữa.
  • Phần lớn các hàng bị xóa, ví dụ: bảng nhật ký giới hạn ở các bản ghi X / ngày.

Ngoài ra còn có một số lý do rất kém cho việc xóa cứng (thêm về lý do cho những điều này sau):

  • Để sửa một lỗi nhỏ. Điều này thường nhấn mạnh sự lười biếng của nhà phát triển và giao diện người dùng thù địch.
  • Để "hủy" một giao dịch (ví dụ: hóa đơn không bao giờ được lập hóa đơn).
  • Bởi vì bạn có thể .

Tại sao, bạn hỏi, nó thực sự là một vấn đề lớn như vậy? Có chuyện gì với ole tốt DELETE?

  • Trong bất kỳ hệ thống nào thậm chí từ xa gắn liền với tiền, việc xóa cứng vi phạm tất cả các loại kỳ vọng kế toán, ngay cả khi được chuyển sang bảng lưu trữ / bia ​​mộ. Cách chính xác để xử lý điều này là một sự kiện hồi tố .
  • Các bảng lưu trữ có xu hướng phân kỳ khỏi lược đồ trực tiếp. Nếu bạn quên ngay cả một cột hoặc tầng mới được thêm vào, bạn đã mất dữ liệu đó vĩnh viễn.
  • Xóa cứng có thể là một hoạt động rất tốn kém, đặc biệt là với các tầng . Rất nhiều người không nhận ra rằng xếp tầng nhiều hơn một cấp (hoặc trong một số trường hợp, bất kỳ tầng nào , tùy thuộc vào DBMS) sẽ dẫn đến các hoạt động ở mức kỷ lục thay vì các hoạt động được thiết lập.
  • Lặp đi lặp lại, xóa cứng thường xuyên làm tăng tốc quá trình phân mảnh chỉ mục.

Vì vậy, xóa mềm là tốt hơn, phải không? Không thật sự lắm:

  • Thiết lập thác trở nên vô cùng khó khăn. Bạn hầu như luôn luôn kết thúc với những gì xuất hiện cho khách hàng dưới dạng các hàng mồ côi.
  • Bạn chỉ có thể theo dõi một lần xóa. Điều gì nếu hàng bị xóa và không bị xóa nhiều lần?
  • Đọc hiệu suất bị ảnh hưởng, mặc dù điều này có thể được giảm nhẹ phần nào với phân vùng, khung nhìn và / hoặc các chỉ mục được lọc.
  • Như được gợi ý trước đó, nó thực sự có thể là bất hợp pháp trong một số tình huống / khu vực pháp lý.

Sự thật là cả hai cách tiếp cận này đều sai. Xóa là sai. Nếu bạn thực sự hỏi câu hỏi này thì có nghĩa là bạn đang lập mô hình trạng thái hiện tại thay vì các giao dịch. Đây là một thực tiễn xấu, xấu trong cơ sở dữ liệu-đất đai.

Udi Dahan đã viết về điều này trong Không xóa - Chỉ cần đừng . Có luôn một số loại công việc, giao dịch, hoạt động , hoặc (ưu tiên hạn của tôi) sự kiện đó thực sự đại diện cho "delete". Sẽ ổn nếu sau đó bạn muốn chuẩn hóa thành bảng "trạng thái hiện tại" để thực hiện, nhưng hãy làm điều đó sau khi bạn đóng đinh mô hình giao dịch, chứ không phải trước đó.

Trong trường hợp này, bạn có "người dùng". Người dùng thực chất là khách hàng. Khách hàng có mối quan hệ kinh doanh với bạn. Mối quan hệ đó không chỉ đơn giản tan biến trong không khí mỏng manh vì họ đã hủy tài khoản của họ. Điều thực sự xảy ra là:

  • Khách hàng tạo tài khoản
  • Khách hàng hủy tài khoản
  • Khách hàng gia hạn tài khoản
  • Khách hàng hủy tài khoản
  • ...

Trong mọi trường hợp, đó là cùng một khách hàng và có thể là cùng một tài khoản (tức là mỗi lần gia hạn tài khoản là một thỏa thuận dịch vụ mới). Vậy tại sao bạn lại xóa hàng? Đây là mô hình rất dễ dàng:

+-----------+       +-------------+       +-----------------+
| Account   | --->* | Agreement   | --->* | AgreementStatus |
+-----------+       +-------------+       +----------------+
| Id        |       | Id          |       | AgreementId     |
| Name      |       | AccountId   |       | EffectiveDate   |
| Email     |       | ...         |       | StatusCode      |
+-----------+       +-------------+       +-----------------+

Đó là nó. Thats tất cả để có nó. Bạn không bao giờ cần phải xóa bất cứ điều gì. Trên đây là một thiết kế khá phổ biến có khả năng linh hoạt tốt nhưng bạn có thể đơn giản hóa nó một chút; bạn có thể quyết định rằng bạn không cần cấp độ "Thỏa thuận" và chỉ cần có "Tài khoản" vào bảng "AccountStatus".

Nếu một nhu cầu thường xuyên trong ứng dụng của bạn là có được một danh sách các thỏa thuận / tài khoản đang hoạt động thì đó là một truy vấn khó (hơi), nhưng đó là những lượt xem dành cho:

CREATE VIEW ActiveAgreements AS
SELECT agg.Id, agg.AccountId, acc.Name, acc.Email, s.EffectiveDate, ...
FROM AgreementStatus s
INNER JOIN Agreement agg
    ON agg.Id = s.AgreementId
INNER JOIN Account acc
    ON acc.Id = agg.AccountId
WHERE s.StatusCode = 'ACTIVE'
AND NOT EXISTS
(
    SELECT 1
    FROM AgreementStatus so
    WHERE so.AgreementId = s.AgreementId
    AND so.EffectiveDate > s.EffectiveDate
)

Và bạn đã hoàn thành. Bây giờ bạn có một cái gì đó với tất cả các lợi ích của xóa mềm nhưng không có nhược điểm nào:

  • Hồ sơ mồ côi là một vấn đề không phải là vì tất cả các hồ sơ có thể nhìn thấy mọi lúc; bạn chỉ cần chọn từ một góc nhìn khác bất cứ khi nào cần thiết.
  • "Xóa" thường là một hoạt động cực kỳ rẻ - chỉ cần chèn một hàng vào bảng sự kiện.
  • Không bao giờ có bất kỳ cơ hội để mất bất kỳ lịch sử, bao giờ , cho dù bạn làm hỏng việc như thế nào.
  • Bạn vẫn có thể xóa tài khoản nếu cần (ví dụ vì lý do riêng tư) thoải mái với kiến ​​thức rằng việc xóa sẽ diễn ra sạch sẽ và không can thiệp vào bất kỳ phần nào khác của ứng dụng / cơ sở dữ liệu.

Vấn đề duy nhất còn lại để giải quyết là vấn đề hiệu suất. Trong nhiều trường hợp, nó thực sự không thành vấn đề vì chỉ số được nhóm lại AgreementStatus (AgreementId, EffectiveDate)- có rất ít I / O đang tìm kiếm ở đó. Nhưng nếu nó là một vấn đề, có nhiều cách để giải quyết vấn đề đó, bằng cách sử dụng các kích hoạt, các khung nhìn được lập chỉ mục / cụ thể hóa, các sự kiện ở cấp ứng dụng, v.v.

Đừng lo lắng về hiệu suất quá sớm - điều quan trọng hơn là phải thiết kế đúng và "đúng" trong trường hợp này có nghĩa là sử dụng cơ sở dữ liệu theo cách sử dụng cơ sở dữ liệu, như một hệ thống giao dịch .


1

Tôi hiện đang làm việc với một hệ thống hiện tại nơi mỗi bảng đều có cờ Đã xóa để xóa mềm. Nó là nguyên nhân của mọi sự tồn tại. Nó hoàn toàn phá vỡ tính toàn vẹn quan hệ khi người dùng có thể "xóa" một bản ghi khỏi một bảng, nhưng các bản ghi con mà FK quay lại bảng đó không bị xóa mềm. Thực sự làm cho dữ liệu rác sau khi thời gian trôi qua.

Vì vậy, tôi đề nghị các bảng lịch sử riêng biệt.


Chắc chắn không có sự thay đổi lịch sử theo tầng, bạn có vấn đề chính xác như vậy không?
glenatron

Không có trong bảng ghi hoạt động của bạn, không.
Jesse C. Choper

Vì vậy, điều gì xảy ra với các bản ghi con FK khỏi bảng người dùng sau khi người dùng đã được gửi vào bảng lịch sử?
glenatron

Trình kích hoạt của bạn (hoặc logic nghiệp vụ) cũng sẽ ký gửi các bản ghi con vào các bảng lịch sử tương ứng của chúng. Vấn đề là, bạn không thể xóa bản ghi gốc (để chuyển sang lịch sử) mà không có cơ sở dữ liệu cho bạn biết rằng bạn đã phá RI. Vì vậy, bạn buộc phải thiết kế nó. Cờ đã xóa không bắt buộc xóa tầng mềm.
Jesse C. Choper

3
Phụ thuộc vào việc xóa mềm của bạn thực sự có nghĩa là gì. Nếu đó chỉ là một cách để hủy kích hoạt chúng, thì không cần điều chỉnh các hồ sơ liên quan đến tài khoản bị vô hiệu hóa. Có vẻ như chỉ là dữ liệu cho tôi. Và vâng, tôi cũng phải đối phó với nó trong một hệ thống mà tôi không thiết kế. Không có nghĩa là bạn phải thích nó.
JeffO

1

Phá vỡ bàn trong hai sẽ là điều tồi tệ nhất có thể tưởng tượng.

Đây là hai bước rất đơn giản mà tôi muốn giới thiệu:

  1. Đổi tên bảng 'người dùng thành' người ám chỉ '.
  2. Tạo chế độ xem được gọi là 'người dùng' dưới dạng 'select * từ allusers đã xóa = false'.

PS Xin lỗi vì đã chậm trễ vài tháng trong việc trả lời!


0

Nếu bạn đang khôi phục các tài khoản đã bị xóa khi ai đó quay lại với cùng một địa chỉ email thì tôi đã đi với việc giữ tất cả người dùng trong cùng một bảng. Điều này sẽ làm cho quá trình phục hồi tài khoản trở nên tầm thường.

Tuy nhiên, khi bạn tạo tài khoản mới thì việc di chuyển các tài khoản bị xóa sang một bảng riêng sẽ đơn giản hơn. Hệ thống trực tiếp không cần thông tin này vì vậy đừng phơi bày nó. Như bạn nói, nó làm cho các truy vấn đơn giản hơn và có thể nhanh hơn trên các bộ dữ liệu lớn hơn. Mã đơn giản hơn cũng dễ bảo trì hơn.


0

Bạn không đề cập đến DBMS đang sử dụng. Nếu bạn có Oracle với giấy phép phù hợp, bạn có thể xem xét phân vùng bảng người dùng thành hai phân vùng: người dùng hoạt động và người dùng đã xóa.


Sau đó, bạn phải di chuyển các hàng từ phân vùng này sang phân vùng khác khi xóa người dùng, đây chắc chắn không phải là cách phân vùng được sử dụng.
Péter Török

@ Péter: Hả? Bạn có thể phân vùng theo bất kỳ tiêu chí nào bạn muốn, bao gồm cả cờ đã bị xóa.
Aaronaught

@Aaronaught, OK, tôi nói sai rồi. DBMS có thể thực hiện công việc cho bạn, nhưng nó vẫn là công việc phụ (vì hàng phải được di chuyển vật lý từ vị trí này sang vị trí khác, có thể sang một tệp khác) và nó có thể làm giảm sự phân phối dữ liệu vật lý.
Péter Török
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.