Phiên bản kiểm soát nội dung của cơ sở dữ liệu


16

Tôi đang làm việc trên một dự án web liên quan đến nội dung có thể chỉnh sửa của người dùng và tôi muốn có thể theo dõi phiên bản của nội dung thực tế, nằm trong cơ sở dữ liệu. Về cơ bản, tôi muốn thực hiện lịch sử thay đổi theo kiểu wiki.

Thực hiện một số nghiên cứu cơ bản, tôi thấy rất nhiều tài liệu về cách phiên bản lược đồ cơ sở dữ liệu của bạn (thực tế tôi đã được kiểm soát), nhưng ít nhất bất kỳ chiến lược nào về cách theo dõi các thay đổi nội dung cơ sở dữ liệu của bạn đều bị mất trong các tìm kiếm của tôi.

Tôi có thể nghĩ ra một vài cách để thực hiện theo dõi thay đổi của riêng mình, nhưng tất cả đều có vẻ khá thô thiển:

  • Lưu toàn bộ hàng trên mỗi thay đổi, liên kết hàng trở lại id nguồn bằng khóa Chính (hiện tại tôi đang nghiêng về phía trước, đây là cách đơn giản nhất). Mặc dù vậy, rất nhiều thay đổi nhỏ có thể tạo ra rất nhiều sự phình to.
  • lưu trước / sau / người dùng / dấu thời gian cho mỗi thay đổi, với tên cột để liên kết thay đổi trở lại cột có liên quan.
  • lưu trước / sau / người dùng / dấu thời gian với một bảng cho mỗi cột (sẽ dẫn đến quá nhiều bảng).
  • lưu diffs / user / timestamp cho mỗi thay đổi bằng một cột (điều này có nghĩa là bạn phải đi bộ toàn bộ lịch sử thay đổi can thiệp để quay lại một ngày nhất định).

Cách tiếp cận tốt nhất ở đây là gì? Có vẻ như tôi có thể phát minh lại cơ sở mã (tốt hơn) của người khác.


Điểm thưởng cho PostgreSQL.


Câu hỏi này đã được thảo luận trên SO: stackoverflow.com/questions/3874199/ ,. Google cho "lịch sử hồ sơ cơ sở dữ liệu" và bạn sẽ tìm thấy một số bài viết khác.
Doc Brown

1
Nghe có vẻ là một ứng cử viên lý tưởng cho Tìm nguồn sự kiện
James

Tại sao không sử dụng nhật ký giao dịch của SQL-Server để thực hiện thủ thuật?
Thomas Junk

Câu trả lời:


11

Kỹ thuật tôi thường sử dụng là lưu bản ghi hoàn chỉnh, với trường end_timestamp. Có một quy tắc kinh doanh là chỉ một hàng có thể có null end_timestamp và tất nhiên đây là nội dung hiện đang hoạt động.

Nếu bạn áp dụng hệ thống này, tôi thực sự khuyên bạn nên thêm một chỉ mục hoặc ràng buộc để thực thi quy tắc. Điều này thật dễ dàng với Oracle, vì một chỉ mục duy nhất có thể chứa một và chỉ một null. Các cơ sở dữ liệu khác có thể là một vấn đề. Có cơ sở dữ liệu thực thi quy tắc sẽ giữ cho mã của bạn trung thực.

Bạn hoàn toàn chính xác rằng rất nhiều thay đổi nhỏ sẽ tạo ra sự phình to, nhưng bạn cần đánh đổi điều này với mã và báo cáo đơn giản.


Lưu ý rằng các công cụ cơ sở dữ liệu khác có thể hoạt động khác nhau, ví dụ: MySQL cho phép nhiều giá trị NULL trong một cột có chỉ mục duy nhất. Điều này làm cho ràng buộc này khó thực thi hơn nhiều.
qbd

Sử dụng dấu thời gian thực tế là không an toàn, nhưng một số cơ sở dữ liệu MVCC hoạt động nội bộ bằng cách lưu trữ số sê-ri giao dịch tối thiểu và tối đa cùng với bộ dữ liệu.
user2313838

"Điều này thật dễ dàng với Oracle, vì một chỉ mục duy nhất có thể chứa một và chỉ một null". Sai lầm. Oracle hoàn toàn không bao gồm các giá trị null trong các chỉ mục. Không có giới hạn về số lượng null trong một cột có chỉ mục duy nhất.
Gerrat

@Gerrat Đã một số năm kể từ khi tôi thiết kế một cơ sở dữ liệu có yêu cầu này và tôi không còn có quyền truy cập vào cơ sở dữ liệu đó nữa. Bạn đúng rằng một chỉ mục duy nhất tiêu chuẩn có thể hỗ trợ nhiều null, nhưng tôi nghĩ rằng chúng tôi đã sử dụng một ràng buộc duy nhất hoặc có thể là một chỉ mục chức năng.
kiwiron

8

Lưu ý rằng nếu bạn sử dụng Microsoft SQL Server, đã có một tính năng được gọi là Thay đổi dữ liệu . Bạn vẫn sẽ cần phải viết mã để truy cập các phiên bản trước đó sau (CDC tạo các chế độ xem cụ thể cho điều đó), nhưng ít nhất bạn không phải thay đổi lược đồ của các bảng, cũng như không thực hiện theo dõi thay đổi.

Dưới mui xe , những gì xảy ra là:

  • CDC tạo một bảng bổ sung có chứa các bản sửa đổi,

  • Bảng gốc của bạn được sử dụng như trước đây, đó là bất kỳ cập nhật nào được phản ánh trực tiếp trong bảng này,

  • Bảng CDC chỉ lưu trữ các giá trị đã thay đổi, nghĩa là sao chép dữ liệu được giữ ở mức tối thiểu.

Thực tế là các thay đổi được lưu trữ trong một bảng khác nhau có hai hậu quả chính:

  • Các lựa chọn từ bảng gốc nhanh như không có CDC. Nếu tôi nhớ rõ, CDC sẽ xảy ra sau khi cập nhật, vì vậy các bản cập nhật cũng nhanh không kém (mặc dù tôi không nhớ rõ CDC quản lý tính nhất quán của dữ liệu như thế nào).

  • Một số thay đổi đối với lược đồ của bảng gốc dẫn đến loại bỏ CDC. Chẳng hạn, nếu bạn thêm một cột, CDC không biết cách xử lý. Mặt khác, thêm một chỉ mục hoặc một ràng buộc sẽ tốt. Điều này nhanh chóng trở thành một vấn đề nếu bạn kích hoạt CDC trên một bảng có thể thay đổi thường xuyên. Có thể có một giải pháp cho phép thay đổi lược đồ mà không mất CDC, nhưng tôi chưa tìm kiếm nó.


6

Giải quyết vấn đề "triết học" và mã đầu tiên. Và sau đó "thương lượng" với mã và cơ sở dữ liệu để thực hiện.

Ví dụ , nếu bạn đang xử lý các bài viết chung chung, một khái niệm ban đầu cho một bài viết có thể giống như thế này:

class Article {
  public Int32 Id;
  public String Body;
}

Và ở cấp độ cơ bản nhất tiếp theo, tôi muốn giữ một danh sách các bản sửa đổi:

class Article {
  public Int32 Id;
  public String Body;
  public List<String> Revisions;
}

Và tôi có thể nhận ra rằng cơ thể hiện tại chỉ là phiên bản mới nhất. Và điều đó có nghĩa là hai điều: Tôi cần mỗi Bản sửa đổi được ghi ngày hoặc đánh số:

class Revision {
  public Int32 Id;
  public Article ParentArticle;
  public DateTime Created;
  public String Body;
}

Và ... và cơ thể hiện tại của bài viết không cần phải khác biệt với phiên bản mới nhất:

class Article {
  public Int32 Id;
  public String Body {
    get {
      return (Revisions.OrderByDesc(r => r.Created))[0];
    }
    set {
      Revisions.Add(new Revision(value));
    }
  }
  public List<Revision> Revisions;
}

Một vài chi tiết bị thiếu; nhưng nó minh họa rằng bạn có thể muốn hai thực thể . Một đại diện cho bài viết (hoặc loại tiêu đề khác), và cái còn lại là một danh sách các sửa đổi (nhóm bất kỳ lĩnh vực nào có ý nghĩa "triết học" tốt để nhóm). Ban đầu, bạn không cần các ràng buộc cơ sở dữ liệu đặc biệt, bởi vì mã của bạn không quan tâm đến bất kỳ sửa đổi nào trong bản thân chúng - chúng là các thuộc tính của một bài viết biết về các sửa đổi.

Vì vậy, bạn không cần phải lo lắng về việc sửa đổi cờ theo bất kỳ cách đặc biệt nào hoặc dựa vào ràng buộc cơ sở dữ liệu để đánh dấu bài viết "hiện tại". Bạn chỉ cần đánh dấu thời gian cho họ (ngay cả ID tự động sẽ ổn), làm cho chúng liên quan đến Điều cha mẹ của họ và để bài viết chịu trách nhiệm biết "cái mới nhất" là cái có liên quan nhất.

Và bạn để một ORM xử lý các chi tiết ít triết lý hơn - hoặc bạn ẩn chúng trong một lớp tiện ích tùy chỉnh nếu bạn không sử dụng ORM ngoài luồng.

Rất lâu sau, sau khi bạn thực hiện một số thử nghiệm căng thẳng, bạn có thể nghĩ về việc làm cho thuộc tính sửa đổi đó trở nên lười biếng hoặc tải thuộc tính Body của bạn chỉ tải phiên bản cao nhất. Nhưng, cấu trúc dữ liệu của bạn trong trường hợp này không cần phải thay đổi để phù hợp với những tối ưu hóa đó.


2

Có một trang wiki PostgreQuery để kích hoạt theo dõi kiểm toán hướng dẫn bạn cách thiết lập nhật ký kiểm toán sẽ làm những gì bạn cần.

Nó theo dõi toàn bộ dữ liệu gốc của một thay đổi, cũng như danh sách các giá trị mới để cập nhật (để chèn và xóa, chỉ có một giá trị). Nếu bạn muốn khôi phục phiên bản cũ, bạn có thể lấy bản sao của dữ liệu gốc từ hồ sơ kiểm toán. Lưu ý rằng nếu dữ liệu của bạn liên quan đến khóa ngoại, những hồ sơ đó cũng có thể phải được khôi phục để duy trì tính nhất quán.

Nói chung, nếu ứng dụng cơ sở dữ liệu của bạn dành phần lớn thời gian cho dữ liệu hiện tại, tôi nghĩ bạn nên theo dõi các phiên bản thay thế trong một bảng riêng biệt với dữ liệu hiện tại. Điều này sẽ giữ cho các chỉ số bảng hoạt động của bạn dễ quản lý hơn.

Nếu các hàng bạn đang theo dõi rất lớn và không gian là một mối quan tâm nghiêm trọng, bạn có thể cố gắng phá vỡ các thay đổi và lưu trữ các khác biệt / bản vá tối thiểu, nhưng điều đó chắc chắn sẽ có nhiều công việc hơn để bao gồm tất cả các loại dữ liệu của bạn. Tôi đã làm điều này trước đây và thật khó khăn khi xây dựng lại các phiên bản dữ liệu cũ bằng cách duyệt qua tất cả các thay đổi về sau, từng thay đổi một lần.


1

Chà, tôi chỉ sử dụng tùy chọn đơn giản nhất, một trình kích hoạt sao chép phiên bản cũ của một hàng vào nhật ký lịch sử trên mỗi bảng.

Nếu tôi kết thúc với quá nhiều cơ sở dữ liệu, tôi có thể xem xét khả năng thu gọn một số thay đổi lịch sử nhỏ, nếu cần.

Giải pháp xử lý khá lộn xộn, vì tôi muốn tự động tạo các chức năng kích hoạt. Tôi là SQLAlchemy, vì vậy tôi đã có thể tạo bảng lịch sử bằng cách thực hiện một số trò trốn tìm thừa kế, điều này thật tuyệt, nhưng các hàm kích hoạt thực tế đã yêu cầu một số chuỗi kết hợp để tạo đúng các hàm PostgreQuery và ánh xạ các cột từ một bảng sang khác chính xác.

Dù sao, đó là tất cả trên github ở đây .

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.