2011-11-15 37 views
8

Tình trạng của tôi là tôi hiện đang lưu trữ một hệ thống phân cấp trong cơ sở dữ liệu SQL nhanh chóng tiếp cận 15000 nút (5000 cạnh). Phân cấp này xác định mô hình bảo mật của tôi dựa trên vị trí của người dùng trong cây, cấp quyền truy cập cho các mục bên dưới. Vì vậy, khi một người dùng yêu cầu một danh sách tất cả các mục được bảo mật, tôi đang sử dụng CTE để recurse nó trong db (và flatten tất cả các mục), được bắt đầu để hiển thị tuổi của nó (chậm).Làm thế nào để lưu trữ và đọc lại một hệ thống phân cấp từ bộ nhớ đệm

Hệ thống phân cấp không thay đổi thường xuyên vì vậy tôi đã cố chuyển nó vào RAM (redis). Lưu ý rằng tôi có nhiều hệ thống con cần điều này cho các cuộc gọi bảo mật và giao diện người dùng để xây dựng cây cho các hoạt động CRUD.

nỗ lực đầu tiên

nỗ lực đầu tiên của tôi là để lưu trữ các mối quan hệ như một cặp giá trị key (đây là cách nó được lưu trữ trong cơ sở dữ liệu)

 
     E 
    / \ 
    F  G 
/\ /\ 
    H I J K 

mapped to: 
    E - [F, G] 
    F - [H, I] 
    G - [J, K] 

Vì vậy, khi tôi muốn E và tất cả những người quá cố của nó, tôi đệ quy lấy con và con của họ bằng cách sử dụng các phím, và nó cho phép tôi bắt đầu ở bất kỳ nút nào để di chuyển xuống. Giải pháp này đã tăng tốc độ tốt nhưng với 15.000 nút, khoảng 5000 lần truy cập bộ nhớ cache để xây dựng lại cây của tôi theo mã (kịch bản trường hợp tồi tệ hơn ... bắt đầu từ hiệu suất E. dựa trên vị trí nút bắt đầu, dẫn đến siêu người dùng nhìn thấy hiệu suất tồi tệ nhất). Điều này vẫn còn khá nhanh nhưng dường như trò chuyện. Tôi thích thực tế là tôi có thể loại bỏ một nút bất cứ lúc nào bằng cách popping nó ra khỏi danh sách phím mà không cần xây dựng lại toàn bộ bộ nhớ cache của tôi. Điều này cũng chiếu sáng nhanh để xây dựng một cây theo yêu cầu một cách trực quan trên giao diện người dùng.

Nỗ lực thứ hai

Idea khác của tôi là để có những cấp bậc từ cơ sở dữ liệu, xây dựng cây và lưu trữ trong RAM (redis) sau đó kéo toàn bộ điều ra khỏi bộ nhớ (đó là khoảng 2 Kích thước MB, được tuần tự hóa). Điều này đã cho tôi một cuộc gọi duy nhất (không phải là trò chuyện) thành redis để kéo toàn bộ cây ra, định vị nút cha của người dùng và xuống để nhận tất cả các mục con. Các cuộc gọi này thường xuyên và giảm xuống 2 MB ở lớp mạng có vẻ lớn. Điều này cũng có nghĩa là tôi không thể dễ dàng thêm/xóa và mục mà không kéo xuống cây và chỉnh sửa và đẩy tất cả trở lại. Ngoài ra trên cây cầu xây dựng thông qua HTTP có nghĩa là mỗi yêu cầu đã phải giảm 2MB để chỉ nhận được con trực tiếp (rất nhỏ bằng cách sử dụng giải pháp đầu tiên).


Vì vậy, giải pháp nào bạn nghĩ là một cách tiếp cận tốt hơn (dài hạn khi tiếp tục phát triển). Cả hai đều nhanh hơn một cách rõ ràng và lấy một số tải ra khỏi cơ sở dữ liệu. Hay là cách tốt hơn để thực hiện điều này mà tôi chưa từng nghĩ đến?

Cảm ơn

+0

Bạn giải quyết vấn đề này như thế nào? – vishal

Trả lời

0

Chúng tôi làm điều tương tự. Chúng ta đọc cây vào bộ nhớ, lưu trữ nó trong bộ nhớ cache của ứng dụng và truy cập nó từ bộ nhớ. Vì những thay đổi của chúng tôi hầu như không bao giờ và các thay đổi không cần phải được phản ánh ngay trong ứng dụng web, chúng tôi thậm chí không bận tâm phát hiện chúng, chỉ cần cho phép tuổi bộ nhớ cache và được làm mới. Nó hoạt động thực sự tốt cho chúng tôi.

1

Nếu thứ bậc không thay đổi thường xuyên, bạn có thể tính toán toàn bộ danh sách các mục bên dưới cho mỗi nút (thay vì chỉ là con trực tiếp). Bằng cách này bạn sẽ cần RAM nhiều hơn đáng kể, nhưng nó sẽ hoạt động cực nhanh cho bất kỳ người dùng nào, bởi vì bạn sẽ có thể đọc toàn bộ danh sách các nút con cháu trong một lần đọc.

Ví dụ của bạn (Tôi sẽ sử dụng định dạng JSON):

E - {"direct" : [F, G], "all" : [F, G, H, I, J, K]} 
F - {"direct" : [H, I], "all" : [H, I]} 
G - {"direct" : [J, K], "all" : [J, K]} 

Vâng, đối với superusers bạn vẫn sẽ cần phải chuyển rất nhiều dữ liệu theo yêu cầu, nhưng tôi không thấy bất cứ cách nào để làm cho nó ít hơn.

+0

- Nếu RAM là một vấn đề, các phím có thể được thiết lập với một TTL ngắn, mà sẽ tuôn ra người dùng không hoạt động ngay sau khi họ đăng xuất. – Hristo

+0

- Và nếu sử dụng redis đặt như trái ngược với JSON hoặc một số chuỗi khác để biểu diễn các subnodes, nhiều thao tác có thể được tối ưu hóa để kiểm tra đơn giản như SISMEMBER, v.v., để giữ lưu lượng truy cập mạng thấp. http://redis.io/commands#set – Hristo

3

Hãy để tôi đưa ra một ý tưởng ...

Sử dụng phiên bản thứ bậc. Khi một nút trong biểu đồ được sửa đổi, tăng phiên bản của nó (một trường int đơn giản trong cơ sở dữ liệu), nhưng cũng là phiên bản tăng của tất cả tổ tiên của nó.

  • Khi nhận cây con từ cơ sở dữ liệu lần đầu tiên, hãy lưu nó vào RAM. (Bạn có thể tối ưu hóa điều này thông qua CTE đệ quy và thực hiện nó trong một chuyến đi vòng một cơ sở dữ liệu.)
  • Tuy nhiên, lần sau bạn cần truy xuất cùng một cây con, chỉ lấy gốc. Sau đó so sánh phiên bản được lưu trong bộ nhớ cache với phiên bản bạn vừa tìm nạp từ cơ sở dữ liệu.
    • Nếu chúng phù hợp, tuyệt vời, bạn có thể ngừng tìm nạp và chỉ cần sử dụng lại bộ nhớ cache.
    • Nếu không, hãy tìm nạp trẻ em và lặp lại quy trình, hãy làm mới bộ nhớ cache khi bạn di chuyển.

Kết quả cuối cùng là thường xuyên hơn không, bạn sẽ tiêu hủy các lấy từ rất sớm, thường là sau khi chỉ có một nút, và thậm chí bạn sẽ không cần phải cache toàn bộ đồ thị. Sửa đổi là tốn kém, nhưng điều này không phải là một vấn đề vì chúng rất hiếm.

BTW, nguyên tắc tương tự sẽ hoạt động theo hướng ngược lại - tức là khi bạn bắt đầu bằng lá và cần tìm đường dẫn tới gốc. Bạn cần phải cập nhật phân cấp phiên bản theo hướng ngược lại, nhưng phần còn lại sẽ hoạt động theo cách tương tự. Bạn thậm chí có thể có cả hai hướng kết hợp.

--- EDIT ---

Nếu cơ sở dữ liệu và ADO.NET của bạn driver hỗ trợ nó, nó có thể là giá trị xem xét thông báo máy chủ, chẳng hạn như MS SQL Server SqlDependency hoặc OracleDependency.

Về cơ bản, bạn hướng dẫn DBMS theo dõi các thay đổi và thông báo cho bạn khi chúng xảy ra. Điều này lý tưởng để giữ cho bộ nhớ cache phía máy khách của bạn được cập nhật một cách hiệu quả.

+0

So với phương pháp của tôi, điều này đòi hỏi ít công việc hơn khi chúng tôi cập nhật nút và làm việc nhiều hơn khi chúng ta đọc nút từ bộ nhớ cache. Tôi nghĩ điều đó phụ thuộc vào thời điểm bạn muốn thể hiện tác động hiệu quả đối với người dùng. Tôi nghĩ rằng nó hợp lý nhất để thực hiện yêu cầu cập nhật cây lâu hơn để thực hiện các yêu cầu đọc sau nhanh hơn, để truyền bá thêm công việc qua các lần đọc sau. – mephisto123

+0

@ mephisto123 Không nhất thiết.Truy vấn ban đầu đắt hơn trong cách tiếp cận của tôi, nhưng các truy vấn tiếp theo sẽ có xu hướng cực kỳ rẻ, thường chỉ là một hàng. Trong cách tiếp cận của bạn, các truy vấn tiếp theo sẽ vẫn cần tìm nạp toàn bộ cây con, ngay cả khi không có gì thay đổi. Vì vậy, cách tiếp cận của tôi là tốt hơn nếu có nhiều lần đọc lặp lại. BTW, bạn phát nổ kích thước cơ sở dữ liệu - điều này không thể tốt cho bộ nhớ đệm cấp cơ sở dữ liệu, vì vậy ngay cả hiệu suất của truy vấn đầu tiên này đang được đề cập đến - CTE đệ quy trên cơ sở dữ liệu được lưu trữ tốt cũng có thể nhanh hơn tìm nạp BLOB chưa được lưu trong bộ nhớ cache. –

+0

Không, tôi không có ý định lưu toàn bộ cây con trong cơ sở dữ liệu. Tôi có nghĩa là cache danh sách tất cả các nút con cháu (chỉ là mảng đơn giản) vì cấu trúc cây thực tế không cần thiết thường xuyên, hầu hết thời gian chúng ta chỉ cần biết danh sách các nút bên dưới một số nút được chọn và không có gì khác. Vì vậy, nếu thông tin cho nút đã chọn đã được lưu trữ, chúng tôi sẽ chỉ thực hiện một yêu cầu đơn giản từ bộ nhớ cache và chúng tôi đã hoàn tất. – mephisto123

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