2009-12-16 31 views
8

Chúng ta có một số máy chủ đang chèn các khối hàng vào một bảng trong cơ sở dữ liệu quan hệ và một máy chủ đọc dữ liệu mới một lần trong bảng . (Bảng là khái niệm một số loại tệp nhật ký - dữ liệu chỉ được chèn vào nhưng không bao giờ sửa đổi và máy chủ đọc hiển thị đuôi của nhật ký.) Có cách nào để máy chủ đọc chỉ đọc dữ liệu mới không? Chúng tôi tự do cấu trúc (các) bảng như chúng tôi muốn.Chỉ đọc các hàng mới từ một bảng giống như log trong cơ sở dữ liệu

Một số ý kiến ​​cho rằng vượt qua tâm trí của tôi, nhưng không có tác dụng là:

  • Đánh dấu các hàng như đọc không phù hợp với ứng dụng của chúng tôi: máy chủ đọc không nên thay đổi cơ sở dữ liệu. (Viết vào cơ sở dữ liệu để hiển thị mọi thứ không phải là điều tốt để làm và có thể có vài phiên hiển thị nội dung.)

  • Chúng tôi có thể chèn dấu thời gian vào mỗi hàng được lấp đầy với thời gian hệ thống cơ sở dữ liệu. Vấn đề là đây không phải là dấu thời gian của thời gian cam kết, mà là thời gian chèn. Nếu bạn yêu cầu cơ sở dữ liệu "cung cấp cho tôi tất cả các giá trị từ bây giờ-5 phút và bây giờ" bạn không thể dựa vào tất cả các giá trị có mặt, vì có thể có các giao dịch đang được tiến hành. Bạn sẽ phải hỏi lại sau cho các giá trị trong khoảng thời gian này, đó là những gì tôi muốn tránh.

  • Chúng tôi có thể chèn số hàng đang chạy được điền từ một chuỗi. Vấn đề tương tự với các giao dịch đang chạy xảy ra khi sử dụng dấu thời gian.

Có giải pháp nào cho vấn đề không, hoặc tôi phải áp dụng một số phỏng đoán như giả định thời gian giao dịch tối đa và luôn yêu cầu giá trị được ghi sau "giờ - tối đa thời gian giao dịch" và đọc một số dữ liệu hai lần?

Trong trường hợp quan trọng: chúng tôi sử dụng Oracle cho việc này. Nhưng tôi cho rằng các câu trả lời chỉ hoạt động với các cơ sở dữ liệu khác, cũng là mối quan tâm chung.

+0

Khi bạn nói rằng "máy chủ đọc không nên thay đổi cơ sở dữ liệu", bạn có nghĩa là có một bảng trong một db riêng biệt tham chiếu đến bảng bạn quan tâm không phải là một tùy chọn không? Nếu điều đó được cho phép, bạn có thể áp dụng ý tưởng đánh dấu các hàng như được đọc trong bảng được liên kết đó – kristof

+0

@kristof: Tôi nghĩ rằng sẽ khá khó chịu khi viết vào db chỉ để hiển thị nhật ký. 8-} Đó sẽ là một hành động trong tuyệt vọng, và tôi muốn lấy một số dữ liệu hai lần hoặc một cái gì đó - nếu không có câu trả lời thanh lịch cho điều này. –

+1

Đó là một câu hỏi hay - vấn đề là khá khó hiểu nếu bạn cần hỗ trợ đồng thời cho việc viết các giao dịch (không có khóa thô) nhưng không cho phép người đọc duy trì bất kỳ trạng thái nào trong DB. Bạn có thể xem dữ liệu được đưa vào hàng đợi chứ không phải là "nhật ký" - trong trường hợp đó tôi cũng sẽ ghi các ID của các hàng được chèn vào một bảng "các mục đang chờ xử lý" riêng biệt được người tiêu dùng chọn. Nhưng điều đó vi phạm một trong các yêu cầu của bạn. – araqnid

Trả lời

3

Cơ sở dữ liệu đang được sử dụng không được chỉ định nên không rõ liệu giải pháp đó có được đưa vào triển khai hiện tại hay không. Có một số công cụ xếp hàng có thể được cắm vào MySQL có khả năng hoạt động. Một trong số đó là Q4M. Một số cơ sở dữ liệu thương mại như Oracle có chức năng cơ sở dữ liệu thời gian cho phép xác định thời gian giao dịch so với thời gian hợp lệ so với thời gian thực.

Khi sử dụng Oracle, cột giả ora_rowscn hoặc kết hợp hữu ích scn_to_timestamp(ora_rowscn) có thể cung cấp dấu thời gian hiệu quả cho thời điểm một hàng được cam kết (SCN được thực hiện).Ngoài ra, Oracle Workspace Manager cung cấp các bảng kích hoạt phiên bản, về cơ bản nó giống như thế này: Bạn kích hoạt phiên bản trên một bảng với DBMS_WM.EnableVersioning(...), các hàng được chèn với trường WMSYS.WM_PERIOD(...) có điều kiện xác định phạm vi thời gian hợp lệ, đặt phạm vi hợp lệ cho không gian làm việc được đặt người đọc DBMS_WM.SetValidTime(...).

Bạn cũng có thể giả mạo chức năng này ở một mức độ nhất định bằng cách chia lưới ý tưởng dấu thời gian của bạn với thời gian cam kết. Ý tưởng chỉ đơn giản là để lưu trữ "thời gian hợp lệ" như một cột cùng với dữ liệu thay vì sử dụng một delta tùy ý từ bây giờ(). Nói cách khác, cột dấu thời gian thứ hai sẽ chỉ định một số ngày trong tương lai ("thời gian hợp lệ") dựa trên thời gian cam kết + thời gian trễ chấp nhận được (có lẽ thời gian cam kết trung bình + gấp đôi độ lệch chuẩn). Ngoài ra, sử dụng một số ceil() ing của thời gian cam kết trung bình ("ít nhất là thời gian cam kết nhưng làm tròn lên đến, nói, khoảng 30 giây"). Sau này sẽ định lượng hiệu quả (coalesce?) Các bản ghi nhật ký thời gian sẽ được đọc. Nó không có vẻ quá khác nhau nhưng cách này sẽ giúp bạn tiết kiệm từ đọc hàng dư thừa. Nó cũng giải quyết được vấn đề mà ứng dụng đọc không thể biết chính xác thời gian cam kết của ứng dụng viết mà không cần viết nhiều mã hơn.

+0

Ý tưởng rất thú vị. :-) Kể từ khi chúng tôi đang sử dụng oracle anyway: làm thế nào tôi có thể nhận được thời gian giao dịch vào các hàng? Tôi đã không tìm được thứ gì đó trong tài liệu. –

+0

Nhìn vào cột giả được gọi là ora_rowscn và kết hợp hữu ích scn_to_timestamp (ora_rowscn). Đây là dấu thời gian hàng cho khi một hàng được cam kết (SCN đã được thực hiện). Ngoài ra, Oracle Workspace Manager cung cấp các phiên bản cho phép các bảng, về cơ bản nó giống như thế này: Bạn kích hoạt versioning trên một bảng EXECUTE DBMS_WM.EnableVersioning (...), các hàng được chèn vào với trường WMSYS.WM_PERIOD (...) thời gian, đặt phạm vi hợp lệ cho không gian làm việc được đặt trên trình đọc DBMS_WM.SetValidTime (...). – charstar

+1

Cảm ơn! Khéo léo. Một câu hỏi thú vị ở đây là liệu một truy vấn như chọn * từ ...nơi mà ora_rowscn> {lastscn} cần thực hiện quét toàn bộ bảng - nếu bạn muốn tránh điều này. Nhưng có lẽ một sự kết hợp với một chỉ mục trên dấu thời gian và truy vấn trên ora_rowscn * và * dấu thời gian trừ maxtransactiontime đủ nhanh. Khi tôi thu thập từ http://www.dba-oracle.com/oracle_tips_ora_rowscn_10g_pseudo_column.htm, bạn cũng cần khai báo ROWDEPENDENCIES cho bảng. –

0

Tạo một bảng khác LOG_REVISION. Nó chứa một hàng duy nhất (một INTEGER).

Quá trình ghi nhật ký sẽ đọc bảng này và thêm số mà nó tìm thấy ở đó vào mỗi bản ghi nhật ký. Khóa hàng cho đến khi bạn thực hiện giao dịch.

Quy trình đọc trước hết nên cập nhật LOG_REVISION bằng cách tăng số và sau đó đọc tất cả các hàng có số LOG_REVISION cũ.

[EDIT] Có hơn hai cách xung quanh này:

  • có một bàn nơi bạn ghi lại các hàng mà bạn đã xử lý.
  • Người viết đưa dữ liệu vào một bảng trung gian và người đọc sao chép các hàng vào vị trí cuối cùng và xóa các hàng đã xử lý.
+0

Một ý tưởng hay, nhưng điều này ngăn chặn việc viết đồng thời một số máy chủ vào cơ sở dữ liệu. Nó phụ thuộc vào ứng dụng cho dù điều này là chấp nhận được, và tôi sẽ không làm điều đó ở đây. –

+0

Tôi đã thêm hai giải pháp nữa. –

+0

Cảm ơn bạn, nhưng đối với cả hai người trong số họ, tôi sẽ cần phải viết vào cơ sở dữ liệu, mà tôi không muốn vì tôi chỉ hiển thị dữ liệu với các cập nhật gia tăng. Việc sửa đổi cơ sở dữ liệu không thích hợp cho một tác vụ như vậy, và có thể không hoạt động chút nào nếu bạn đang hiển thị dữ liệu cho nhiều người dùng cùng một lúc, ví dụ trong một ứng dụng web nhóm. –

0

Tôi có thể nói ý tưởng của bạn cho timestamps là hợp lệ, nhưng thay vì yêu cầu một phạm vi, chỉ cần hỏi cho tất cả các giá trị sau một thời gian nhất định. Bạn sẽ nhận được tất cả các giá trị có sẵn trong cơ sở dữ liệu trong khoảng thời gian gần đây nhất mà bạn đã chọn. Rõ ràng, nó sẽ không hoạt động đối với bất kỳ giao dịch nào vẫn đang được tiến hành mà chưa được đăng nhập ... nhưng bạn chỉ phải thực hiện một truy vấn đơn giản.

Chỉnh sửa:
Bạn phải đảm bảo giá trị dấu thời gian là duy nhất cho mỗi hàng. Trong trường hợp đó, bạn chỉ cần theo dõi giá trị dấu thời gian mới nhất mà bạn đã đọc từ cơ sở dữ liệu. Truy vấn tiếp theo tới cơ sở dữ liệu có tất cả các giá trị sau. Bạn sẽ không bỏ lỡ bất kỳ dữ liệu nào, bạn cũng sẽ không đọc các bản sao. Bất kỳ giao dịch nào đang diễn ra sẽ không được lưu trữ trong cơ sở dữ liệu trong một truy vấn để bạn bỏ lỡ, và bạn được bảo đảm nhận được nó vào lần tiếp theo bạn truy vấn cơ sở dữ liệu.

+0

Đó là kế hoạch ban đầu của tôi. Nhưng trong truy vấn tiếp theo: nếu tôi chỉ yêu cầu các giá trị của timeperiod tiếp theo, tôi sẽ không bao giờ nhận được các giá trị có giao dịch vẫn đang được tiến hành. Tôi sẽ cần phải yêu cầu ít nhất một phần của timeperiod cuối cùng là tốt, và sẽ đọc bản sao tôi sẽ phải lọc ra. –

+0

@hstoerr Điều này phụ thuộc vào cơ sở dữ liệu bạn đang sử dụng. Một số cơ sở dữ liệu (như Oracle) có thể duy trì dữ liệu đa nhiệm này, cụ thể là thời gian giao dịch và thời gian hợp lệ. – charstar

+0

@loan: điểm mà ý tưởng của bạn thất bại giống như trong bình luận của tôi cho câu trả lời của Arthur Thomas - ngoại trừ nếu bạn thực sự có thể chèn thời gian giao dịch vào cột, như charstar có vẻ hàm ý. –

0

Đây là một giải pháp khả thi, tùy thuộc vào tình hình của bạn, vv

có một cột gọi là "read_timestamp" mà là null, một lần liên tiếp được đọc, quá trình đọc sẽ cập nhật nó với một dấu thời gian không null.

Trình đọc truy vấn bảng này với "where read_timestamp is null".

Một giải pháp dễ dàng hơn là đi với ước tính (tức là hàng này có thể đã được xem là loại báo trước). Do đó, bạn sẽ bất kỳ lúc nào hiển thị "50 hàng cuối cùng" hoặc "hàng đến trong 10 phút qua" (với sự thiếu chính xác mà một người xem nhật ký khác có thể đã lấy những người đó). để nạp các hàng này bằng cách sử dụng một quá trình back-end cho một hàng đợi: Mỗi lần đọc của một hàng làm cho hàng biến mất khỏi hàng đợi (vì nó là một hoạt động 'pop'), do đó một hàng chỉ có thể được xem một lần (đầu tiên đến trước

+0

OP xác định rằng ứng dụng đọc không thể sửa đổi cơ sở dữ liệu. – charstar

0

tạo một chuỗi id cho bảng nhật ký để mỗi nhật ký có một id duy nhất, sau đó khi người đọc đọc nhật ký, nó sẽ ghi lại một nơi nào đó id được đọc cao nhất. sau khi id được ghi lại lần cuối, mọi giao dịch liên tục sẽ không thành vấn đề vì bạn không nhận được chúng trong resultset. chạy.

vì vậy nếu bạn có:

id | log 
1 | blah 
2 | blah again 
3 | more blah 
* transaction to insert row '4' in progress 

sau đó bạn sẽ phải lấy tất cả những bản ghi và ghi 3 id như cuối cùng được tìm thấy. Và trên đường chạy trốn tiếp theo:

chọn id, đăng nhập từ các bản ghi nơi id> last_recorded trật tự id bằng id #ID sẽ 3

4 | yet again some blah 
5 | does this blah never end 
6 | omg blah 

và kỷ lục 6 như mới id ghi cuối cùng của bạn. Tôi vẫn nghĩ rằng nó là tốt để giữ cho ngày của khi đăng nhập được thực hiện là tốt.

EDIT ok để bắt mọi thứ như thế bạn sẽ phải giữ một bộ tất cả các bản ghi được đọc ở một vị trí thay thế và sau đó nhận được sự khác biệt của bộ đọc đối với bảng nhật ký hoạt động. Nếu bạn không thể chạm vào bảng đăng nhập thì bạn chỉ đang xử lý các bộ và tìm thấy những gì không nằm trong một bộ.

+1

-1 Cùng một vấn đề như dấu thời gian: Nếu giao dịch chưa được cam kết, chuỗi sẽ đã tăng lên nhưng người đọc sẽ bỏ lỡ hàng. Nếu một giao dịch khác được cam kết, hàng đầu tiên sẽ không được xử lý. –

+0

Để đi với ví dụ của bạn: điểm mà câu trả lời của bạn không hoạt động là khi giao dịch cho 3 đang được tiến hành, nhưng 4 được đọc. Vấn đề là các ID được tạo khi chèn, chứ không phải trên cam kết. Nếu một cơ sở dữ liệu cho phép điều này, vấn đề sẽ được giải quyết. –

+0

@Aaron: Có thể là MS SQL cụ thể nhưng khi cả người đọc và nhà văn sử dụng mức cô lập giao dịch ReadCommitted, người đọc thực hiện bất cứ điều gì như 'SELECT * WHERE ID> 2' trong khi chèn hàng có id '4' vẫn đang được tiến hành sẽ không nhận được bất kỳ kết quả nào cho đến khi cam kết (và nói chung nó sẽ đợi cho đến khi tất cả các giao dịch chèn được cam kết). – Regent

4

MS SQL có giải pháp cụ thể của nó:

Bạn có thể thêm một cột rowversion kiểu dữ liệu cho bảng. Cột này sẽ được cập nhật tự động trên các hàng liên quan theo công cụ trên bất kỳ câu lệnh cập nhật/chèn nào.

Nếu sử dụng nhà văn ReadCommitted cô lập mức sau đó người đọc có thể sử dụng ReadUncommitted cô lập mức (vì vậy nó không cần phải chờ đợi cho tất cả các giao dịch để chấm dứt trước khi trả lại bất kỳ kết quả) nhưng với các truy vấn như thế này:

SELECT * FROM [Log] 
WHERE Version > @LastKnownVersion 
    AND Version < MIN_ACTIVE_ROWVERSION() 

Trong đó @LastKnownVersion là phiên bản hàng tối đa được người đọc xử lý và MIN_ACTIVE_ROWVERSION() là hàm MS SQL được tích hợp trả về số phiên bản hàng tối thiểu vẫn còn trong giao dịch. Vì vậy, với giải pháp này ngay cả khi bạn có ID = 4 cam kết nhưng ID = 3 chưa, nó sẽ chỉ trả lại hàng thay đổi trước ID = 3 vì phiên bản của nó sẽ chính xác là MIN_ACTIVE_ROWVERSION().

Ưu điểm của phương pháp này là không cần người đọc phải chờ giao dịch được cam kết trước khi nhận được bất kỳ kết quả nào có thể rất quan trọng nếu có nhiều người viết. (Reader có thể bị khóa mãi mãi.)

Các vấn đề liên quan