Cách lưu trữ dữ liệu lịch sử


162

Một số đồng nghiệp và tôi đã tranh luận về cách tốt nhất để lưu trữ dữ liệu lịch sử. Hiện tại, đối với một số hệ thống, tôi sử dụng một bảng riêng để lưu trữ dữ liệu lịch sử và tôi giữ một bảng gốc cho bản ghi hoạt động hiện tại. Vì vậy, hãy nói rằng tôi có bảng FOO. Theo hệ thống của tôi, tất cả các hồ sơ hoạt động sẽ đi vào FOO và tất cả các hồ sơ lịch sử sẽ đi trong FOO_Hist. Nhiều người dùng khác nhau trong FOO có thể được cập nhật, vì vậy tôi muốn giữ một tài khoản chính xác về mọi thứ được cập nhật. FOO_Hist giữ các trường chính xác giống như FOO ngoại trừ HIST_ID tự động tăng. Mỗi khi FOO được cập nhật, tôi thực hiện một câu lệnh chèn vào FOO_Hist tương tự như : insert into FOO_HIST select * from FOO where id = @id.

Đồng nghiệp của tôi nói rằng đây là thiết kế tồi vì tôi không nên có một bản sao chính xác của bảng vì lý do lịch sử và chỉ nên chèn một bản ghi khác vào bảng hoạt động với một lá cờ cho biết đó là vì mục đích lịch sử.

Có một tiêu chuẩn để đối phó với lưu trữ dữ liệu lịch sử? Dường như với tôi rằng tôi không muốn làm lộn xộn các hồ sơ hoạt động của mình với tất cả các hồ sơ lịch sử của tôi trong cùng một bảng xem xét rằng nó có thể vượt quá một triệu hồ sơ (tôi đang suy nghĩ lâu dài).

Làm thế nào để bạn hoặc công ty của bạn xử lý này?

Tôi đang sử dụng MS SQL Server 2008, nhưng tôi muốn giữ câu trả lời chung chung và tùy ý cho bất kỳ DBMS nào.

Câu trả lời:


80

Hỗ trợ dữ liệu lịch sử trực tiếp trong một hệ thống vận hành sẽ làm cho ứng dụng của bạn phức tạp hơn nhiều so với cách khác. Nói chung, tôi không khuyên bạn nên làm điều đó trừ khi bạn có yêu cầu khó khăn để thao túng các phiên bản lịch sử của một bản ghi trong hệ thống.

Nếu bạn xem xét kỹ, hầu hết các yêu cầu đối với dữ liệu lịch sử thuộc một trong hai loại:

  • Ghi nhật ký kiểm toán: Điều này tốt hơn là thực hiện với các bảng kiểm toán. Thật dễ dàng để viết một công cụ tạo tập lệnh để tạo bảng nhật ký kiểm toán và kích hoạt bằng cách đọc siêu dữ liệu từ từ điển dữ liệu hệ thống. Loại công cụ này có thể được sử dụng để cải thiện đăng nhập kiểm toán trên hầu hết các hệ thống. Bạn cũng có thể sử dụng hệ thống con này để thay đổi dữ liệu nếu bạn muốn triển khai kho dữ liệu (xem bên dưới).

  • Báo cáo lịch sử: Báo cáo về trạng thái lịch sử, vị trí 'như tại' hoặc báo cáo phân tích theo thời gian. Có thể thực hiện các yêu cầu báo cáo lịch sử đơn giản bằng cách bỏ qua các bảng ghi nhật ký kiểm toán thuộc loại được mô tả ở trên. Nếu bạn có các yêu cầu phức tạp hơn thì việc triển khai một mart dữ liệu cho báo cáo có thể sẽ kinh tế hơn so với việc thử và tích hợp lịch sử trực tiếp vào hệ thống vận hành.

    Thay đổi kích thước chậm là cơ chế đơn giản nhất để theo dõi và truy vấn trạng thái lịch sử và phần lớn theo dõi lịch sử có thể được tự động hóa. Trình xử lý chung không khó để viết. Nói chung, báo cáo lịch sử không phải sử dụng dữ liệu cập nhật từng phút, do đó, một cơ chế làm mới theo đợt thường là tốt. Điều này giữ cho kiến ​​trúc hệ thống và báo cáo cốt lõi của bạn tương đối đơn giản.

Nếu các yêu cầu của bạn thuộc một trong hai loại này, có lẽ tốt hơn hết là bạn không lưu trữ dữ liệu lịch sử trong hệ thống vận hành của mình. Việc tách chức năng lịch sử thành một hệ thống con khác có thể sẽ ít nỗ lực hơn và tạo ra các cơ sở dữ liệu kiểm toán và báo cáo / giao dịch hoạt động tốt hơn nhiều cho mục đích dự định của chúng.


Tôi nghĩ rằng tôi thấy những gì bạn đang nói. Vì vậy, những gì tôi đã làm với bảng FOO_Hist của mình thực sự tạo ra một bảng kiểm toán. Thay vì sử dụng một kích hoạt để chèn vào bảng kiểm toán khi cập nhật, tôi chỉ chạy một câu lệnh trong chương trình. Đúng không?
Aaron

6
Khá nhiều. Mặc dù vậy, tốt hơn là thực hiện loại ghi nhật ký kiểm toán này với các trình kích hoạt; các kích hoạt đảm bảo rằng mọi thay đổi (bao gồm sửa lỗi dữ liệu thủ công) được ghi lại trong nhật ký kiểm toán. Nếu bạn đã có hơn 10-20 bảng để kiểm tra thì có lẽ sẽ nhanh hơn tất cả để xây dựng một công cụ tạo trình kích hoạt. Nếu lưu lượng đĩa cho nhật ký kiểm toán là một vấn đề, bạn có thể đặt các bảng nhật ký kiểm toán vào một bộ đĩa riêng.
Mối quan tâmOfTunbridgeWells

Vâng, tôi 100% đồng ý với điều đó. Cảm ơn bạn.
Aaron

40

Tôi không nghĩ có một cách tiêu chuẩn cụ thể để làm điều đó nhưng tôi nghĩ tôi sẽ đưa ra một phương pháp khả thi. Tôi làm việc trong Oracle và khung ứng dụng web nội bộ của chúng tôi sử dụng XML để lưu trữ dữ liệu ứng dụng.

Chúng tôi sử dụng một cái gì đó gọi là mô hình Master - Chi tiết đơn giản nhất bao gồm:

Bảng Master ví dụ được gọi Widgetsthường chỉ chứa một ID. Thường sẽ chứa dữ liệu sẽ không thay đổi theo thời gian / không phải là lịch sử.

Bảng chi tiết / lịch sử ví dụ được gọi Widget_Detailscó chứa ít nhất:

  • ID - khóa chính. Chi tiết / ID lịch sử
  • MASTER_ID - ví dụ trong trường hợp này được gọi là 'WIDGET_ID', đây là FK cho bản ghi chính
  • START_DATETIME - dấu thời gian biểu thị sự bắt đầu của hàng cơ sở dữ liệu đó
  • END_DATETIME - dấu thời gian cho biết kết thúc hàng cơ sở dữ liệu đó
  • STATUS_CONTROL - cột char duy nhất cho biết trạng thái của hàng. 'C' cho biết hiện tại, NULL hoặc 'A' sẽ là lịch sử / được lưu trữ. Chúng tôi chỉ sử dụng điều này bởi vì chúng tôi không thể lập chỉ mục trên END_DATETIME là NULL
  • CREATED_BY_WUA_ID - lưu trữ ID của tài khoản khiến hàng được tạo
  • XMLDATA - lưu trữ dữ liệu thực tế

Vì vậy, về cơ bản, một thực thể bắt đầu bằng cách có 1 hàng trong tổng thể và 1 hàng chi tiết. Chi tiết có ngày kết thúc NULL và STATUS_CONTROL của 'C'. Khi cập nhật xảy ra, hàng hiện tại được cập nhật để có END_DATETIME của thời gian hiện tại và status_control được đặt thành NULL (hoặc 'A' nếu được ưu tiên). Một hàng mới được tạo trong bảng chi tiết, vẫn được liên kết với cùng một chủ, với status_control 'C', id của người thực hiện cập nhật và dữ liệu mới được lưu trữ trong cột XMLDATA.

Đây là cơ sở của mô hình lịch sử của chúng tôi. Logic Tạo / Cập nhật được xử lý trong gói Oracle PL / SQL để bạn chỉ cần truyền chức năng ID hiện tại, ID người dùng và dữ liệu XML mới và bên trong nó thực hiện tất cả việc cập nhật / chèn các hàng để thể hiện điều đó trong mô hình lịch sử . Thời gian bắt đầu và kết thúc cho biết khi nào hàng đó trong bảng được kích hoạt.

Dung lượng lưu trữ rẻ, chúng tôi thường không XÓA dữ liệu và muốn theo dõi kiểm toán. Điều này cho phép chúng tôi xem dữ liệu của chúng tôi trông như thế nào tại bất kỳ thời điểm nào. Bằng cách lập chỉ mục status_control = 'C' hoặc sử dụng Chế độ xem, việc lộn xộn không chính xác là một vấn đề. Rõ ràng các truy vấn của bạn cần phải tính đến, bạn nên luôn luôn sử dụng phiên bản hiện tại (NULL end_datetime và status_control = 'C') của một bản ghi.


Xin chào Chris, nếu bạn làm điều đó, ID (khóa chính) phải được thay đổi phải không? Làm thế nào về mối quan hệ với một bảng khác nếu nó được sử dụng bởi một bảng khác?
projo

@projo ID trên bảng chính của bạn là PK và về mặt khái niệm là "PK" cho bất kỳ khái niệm nào bạn đang xử lý. ID trên bảng chi tiết là PK để xác định phiên bản lịch sử cho bản gốc (là một cột khác trên chi tiết). Khi hình thành các mối quan hệ, bạn thường tham chiếu PK thực sự của khái niệm của mình (ví dụ: ID trên bảng chính hoặc cột MASTER_ID trên chi tiết của bạn) và sử dụng STATUS_CONTROL = 'C' để đảm bảo bạn sẽ có phiên bản hiện tại. Ngoài ra, bạn có thể tham chiếu ID chi tiết để liên hệ một cái gì đó với một thời điểm cụ thể.
Chris Cameron-Mills

+1 Tôi đã thực hiện mô hình này rất thành công trên một số dự án lớn.
Ba giá trị logic

Chúng tôi đang sử dụng cùng một aproach. Nhưng bây giờ tôi tự hỏi có tốt hơn không khi chỉ lưu trữ START_DATETIME và không lưu trữ END_DATETIME
bat_ventzi

Vài biến thể trong kinh nghiệm của tôi. Nếu thực thể của bạn là "đã kết thúc", tức là được lưu trữ hoặc bị xóa thì bạn có thể không có hồ sơ chi tiết với kiểm soát trạng thái 'C', tức là không có hàng hiện tại, mặc dù bạn sẽ không biết khi nào điều đó xảy ra. Ngoài ra, bạn có thể đặt thời gian kết thúc trên hàng cuối cùng và sự hiện diện của hàng 'đã kết thúc' 'C' có thể cho biết thực thể hiện đã bị xóa / lưu trữ. Cuối cùng, bạn có thể thể hiện điều này thông qua một cột khác, TÌNH TRẠNG mà bạn có thể đã có.
Chris Cameron-Mills

15

Tôi nghĩ rằng bạn tiếp cận là chính xác. Bảng lịch sử phải là bản sao của bảng chính không có chỉ mục, hãy đảm bảo bạn cũng cập nhật dấu thời gian trong bảng.

Nếu bạn thử phương pháp khác sớm, bạn sẽ gặp phải vấn đề:

  • chi phí bảo trì
  • nhiều cờ hơn trong các lựa chọn
  • truy vấn chậm
  • tăng trưởng của bảng, chỉ số

7

Trong SQL Server 2016 trở lên , có một tính năng mới gọi là Bảng tạm thời nhằm giải quyết thách thức này với nỗ lực tối thiểu từ nhà phát triển . Khái niệm về bảng thời gian tương tự như Thay đổi dữ liệu (CDC), với sự khác biệt là bảng tạm thời đã trừu tượng hóa hầu hết những điều bạn phải làm thủ công nếu bạn đang sử dụng CDC.


2

Thay đổi dữ liệu chụp: https://docs.microsoft.com/en-us/sql/relational-database/track-changes/about-change-data-capture-sql-server?view=sql-server-2017

Nó được hỗ trợ trong SQL Server 2008 R2, nó có thể đã được hỗ trợ trong SQL Server 2008.


Lưu ý rằng Change Data Capture chỉ dành cho việc lưu trữ ngắn gọn lịch sử dữ liệu. Xem Bảng tạm thời của máy chủ SQL và Thay đổi dữ liệu thu thập so với theo dõi thay đổi .
Edward Brey


1

Chỉ muốn thêm một tùy chọn mà tôi đã bắt đầu sử dụng vì tôi sử dụng Azure SQL và nhiều thứ trong bảng quá phức tạp đối với tôi. Tôi đã thêm một trình kích hoạt chèn / cập nhật / xóa trên bảng của mình và sau đó chuyển đổi thay đổi trước / sau thành json bằng tính năng "FOR JSON AUTO".

 SET @beforeJson = (SELECT * FROM DELETED FOR JSON AUTO)
SET @afterJson = (SELECT * FROM INSERTED FOR JSON AUTO)

Điều đó trả về một đại diện JSON cho bản ghi trước / sau khi thay đổi. Sau đó, tôi lưu trữ các giá trị đó trong bảng lịch sử với dấu thời gian khi thay đổi xảy ra (Tôi cũng lưu trữ ID cho bản ghi quan tâm hiện tại). Sử dụng quy trình tuần tự hóa, tôi có thể kiểm soát cách dữ liệu được lấp đầy trong trường hợp thay đổi lược đồ.

Tôi đã học về điều này từ liên kết này ở đây


0

Bạn chỉ có thể phân vùng các bảng không?

"Chiến lược bảng và chỉ mục được phân vùng bằng SQL Server 2008 Khi bảng cơ sở dữ liệu tăng kích thước lên hàng trăm gigabyte trở lên, việc tải dữ liệu mới, xóa dữ liệu cũ và duy trì chỉ mục trở nên khó khăn hơn. làm cho các hoạt động đó mất nhiều thời gian hơn. Ngay cả dữ liệu phải được tải hoặc xóa cũng có thể rất lớn, làm cho các hoạt động INSERT và DELETE trên bảng trở nên không thực tế. Phần mềm cơ sở dữ liệu Microsoft SQL Server 2008 cung cấp phân vùng bảng để dễ quản lý hơn. "


Có, tôi có thể phân vùng các bảng, nhưng đó có phải là tiêu chuẩn khi xử lý dữ liệu lịch sử không? Dữ liệu lịch sử có nên được đưa vào cùng bảng với dữ liệu đang hoạt động không? Đây là những câu hỏi mà tôi muốn thảo luận. Điều này cũng không phải là tùy tiện vì nó liên quan đến SQL Server 2008
Aaron

0

Câu hỏi thực sự là bạn có cần sử dụng dữ liệu lịch sử và dữ liệu hoạt động cùng nhau để báo cáo không? Nếu vậy hãy giữ chúng trong một bảng, phân vùng và tạo chế độ xem cho các bản ghi hoạt động để sử dụng trong các truy vấn hoạt động. Nếu bạn chỉ cần thỉnh thoảng nhìn vào chúng (để nghiên cứu các vấn đề về cờ hoặc một số thứ như vậy) thì hãy đặt chúng vào một bảng riêng.


2
Có khó khăn hơn đối với JOINhai bảng trong một vài báo cáo lịch sử hay khó khăn hơn khi sửa đổi mỗi lần chèn / cập nhật / xóa bảng để nhận biết các mối quan tâm lịch sử? Trên thực tế, nhật ký kiểm toán sẽ bao gồm cả dữ liệu hiện tại trong bảng lịch sử, do đó, bảng hiện tại thậm chí không cần thiết trong báo cáo.

0

Một tùy chọn khác là lưu trữ dữ liệu vận hành trên cơ sở [hàng ngày | hàng giờ | bất cứ thứ gì]. Hầu hết các công cụ cơ sở dữ liệu hỗ trợ trích xuất dữ liệu vào kho lưu trữ .

Về cơ bản, ý tưởng là tạo ra một công việc Windows hoặc CRON theo lịch trình

  1. xác định các bảng hiện tại trong cơ sở dữ liệu hoạt động
  2. chọn tất cả dữ liệu từ mỗi bảng vào tệp CSV hoặc XML
  3. nén dữ liệu đã xuất thành tệp ZIP, tốt nhất là với dấu thời gian của thế hệ trong tên tệp để lưu trữ dễ dàng hơn.

Nhiều công cụ cơ sở dữ liệu SQL đi kèm với một công cụ có thể được sử dụng cho mục đích này. Ví dụ: khi sử dụng MySQL trên Linux, lệnh sau có thể được sử dụng trong công việc CRON để lên lịch trích xuất:

mysqldump --all-databases --xml --lock-tables=false -ppassword | gzip -c | cat > /media/bak/servername-$(date +%Y-%m-%d)-mysql.xml.gz

2
Điều này hoàn toàn không phù hợp với dữ liệu lịch sử bởi vì nếu ai đó thay đổi một giá trị và thay đổi lại trong chu kỳ lưu trữ, các cập nhật đó sẽ bị mất. Cũng không có cách nào dễ dàng để xem xét các thay đổi đối với một thực thể theo thời gian hoặc khôi phục một phần thực thể.
Sgoettschkes

0

Tôi biết bài cũ này nhưng chỉ muốn thêm vài điểm. Tiêu chuẩn cho các vấn đề như vậy là những gì hoạt động tốt nhất cho tình huống. hiểu nhu cầu lưu trữ như vậy và tiềm năng sử dụng dữ liệu theo dõi lịch sử / kiểm toán / thay đổi là rất quan trọng.

Audit (mục đích bảo mật) : Sử dụng một bảng chung cho tất cả các bảng có thể nghe được của bạn. xác định cấu trúc để lưu trữ tên cột, trước các giá trị và sau các trường giá trị.

Lưu trữ / Lịch sử : đối với các trường hợp như theo dõi địa chỉ trước đó, số điện thoại, v.v ... tạo bảng riêng FOO_HIST sẽ tốt hơn nếu lược đồ bảng giao dịch hoạt động của bạn không thay đổi đáng kể trong tương lai (nếu bảng lịch sử của bạn phải có cùng cấu trúc). nếu bạn dự đoán chuẩn hóa bảng, bổ sung / loại bỏ các cột dữ liệu, lưu trữ dữ liệu lịch sử của bạn ở định dạng xml. định nghĩa một bảng với các cột sau (ID, ngày, Phiên bản lược đồ, XMLData). điều này sẽ dễ dàng xử lý các thay đổi lược đồ. nhưng bạn phải đối phó với xml và điều đó có thể gây ra mức độ phức tạp cho việc truy xuất dữ liệu.



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.