2013-01-23 28 views
7

Làm cách nào để ngăn tải giá trị không có trong bộ nhớ cache nhiều lần cùng một lúc, trong cách hiệu quả cách?Làm thế nào để ngăn tải giá trị không được lưu trữ đồng thời nhiều lần?

Cách sử dụng bộ nhớ cache điển hình là giả sau đây:

Object get(Object key) { 
Object value = cache.get(key); 
if (value == null) { 
    value = loadFromService(key); 
    cache.set(key,value); 
} 
return value; 
} 

Vấn đề: trước khi giá trị được nạp từ dịch vụ (cơ sở dữ liệu, WebService, RemoteEJB hoặc bất cứ điều gì khác) một cuộc gọi thứ hai có thể được thực hiện trong cùng một thời , sẽ làm cho giá trị được tải lại một lần nữa. Ví dụ: khi tôi lưu vào bộ nhớ cache tất cả các mục cho người dùng X và người dùng này thường được xem và có nhiều mục, khả năng gọi đồng thời tất cả các mục của anh ấy là cao, dẫn đến tải nặng trên máy chủ .

Tôi có thể làm cho get chức năng đồng bộ, nhưng điều này sẽ buộc các tìm kiếm khác phải chờ đợi, không có ý nghĩa nhiều. Tôi có thể tạo khóa mới cho mỗi khóa nhưng tôi không biết liệu có nên quản lý số lượng khóa lớn như vậy trong Java (phần này là ngôn ngữ cụ thể không, lý do tôi đã gắn thẻ khóa đó là java) .

Hoặc có cách tiếp cận khác mà tôi có thể sử dụng? Nếu vậy, điều gì sẽ hiệu quả nhất?

+1

Bạn đang overthinking này, nghiêm túc. Trừ khi thời gian để tải dữ liệu từ dịch vụ là khủng khiếp lâu, điều này sẽ không bao giờ là một vấn đề. – pablochan

+0

Tôi có một số mã EJB ngoài hành tinh có thể mất đến 20 giây trong môi trường thử nghiệm, vì vậy tôi sợ điều gì sẽ xảy ra với 10 hoặc 20 yêu cầu đồng thời –

Trả lời

3

Không phát minh lại bánh xe, sử dụng số LoadingCache hoặc memoizing supplier của ổi.

Nếu bạn đang sử dụng Ehcache, hãy đọc khoảng read-through, đây là mẫu bạn đang yêu cầu. Bạn phải triển khai giao diện CacheEntryFactory để hướng dẫn bộ nhớ cache cách đọc các đối tượng trên bộ nhớ cache, và bạn phải quấn ví dụ Ehcache với phiên bản SelfPopulatingCache.

+0

Như tôi đã biết, CacheLoader đang làm những gì tôi mong đợi, với quản lý nội bộ của việc đồng bộ hóa cần thiết ? –

+0

Có, và cung cấp nhiều hơn nữa - đuổi, nghe người nghe, vv – mindas

+0

Tôi thấy, thú vị, tôi đã sử dụng ehcache nhưng tôi có thể xem xét sử dụng ổi, tuy nhiên ehcache hỗ trợ tràn vào đĩa, và vẫn còn, cách làm thế nào để thực hiện nó là chính nó hấp dẫn. –

7

Một cái gì đó bạn có thể làm một cách tổng quát là sử dụng hashCode của đối tượng.

Bạn có thể có một loạt các khóa được sử dụng dựa trên hashCode để giảm nguy cơ va chạm. Hoặc là một hack, bạn có thể sử dụng thực tế là các ô tự động đóng gói luôn trả về cùng một đối tượng.

Object get(Object key) { 
    Object value = cache.get(key); 
    if (value == null) { 
     // every possible Byte is cached by the JLS. 
     Byte b = Byte.valueOf((byte) key.hashCode()); 
     synchronized (b) { 
      value = cache.get(key); 
      if (value == null) { 
       value = loadFromService(key); 
       cache.set(key, value); 
      } 
     } 
    } 
    return value; 
} 
+0

Ý tưởng tuyệt vời với tính năng khóa tổng hợp dựa trên hashCode! Nhưng sau khi nhận được khóa, bạn có thể tìm thấy giá trị của bạn được lưu trữ bởi quá trình khác, vì vậy bạn cũng nên kiểm tra xem nó có được tải không :) –

+2

Wow, tôi sẽ không bao giờ sử dụng byte theo cách này! –

+0

Hơn nữa, đây là lần đầu tiên tôi thấy việc sử dụng thực tế các giá trị Byte gộp theo valueOf :) –

1

Trong thời gian tải, chèn một đối tượng trung gian vào bản đồ thay vì kết quả để cho biết rằng quá trình tải đã bắt đầu nhưng chưa kết thúc. Dưới đây java.util.concurrent.FutureTask được sử dụng cho các đối tượng trung gian:

Object get(final Object key) throws Exception { 
    boolean doRun = false; 
    Object value; 
    synchronized (cache) { 
     value = cache.get(key); 
     if (value == null) { 
      value = new FutureTask(new Callable() { 
       @Override 
       public Object call() throws Exception { 
        Object loadedValue = loadFromService(key); 
        synchronized (cache) {cache.put(key, loadedValue);}; 
        return loadedValue; 
       } 

      }); 
      cache.put(key, value); 
      doRun=true; 
     } 
    } 
    if (value instanceof FutureTask) { 
     FutureTask task = (FutureTask) value; 
     if (doRun) { 
      task.run(); 
     } 
     return task.get(); 
    } 
    return value; 
}` 
+0

Hmm giải pháp của bạn yêu cầu luôn đồng bộ hóa trên toàn bộ bộ nhớ cache, nhưng phần được đồng bộ hóa khá nhanh. Bạn nghĩ gì về việc ban đầu có được, và chạy một phần đồng bộ chỉ khi giá trị là null? –

+0

Tùy thuộc vào số lượng yêu cầu cho bộ nhớ cache mỗi giây bạn mong đợi. Phần đồng bộ kéo dài dưới 1 micro giây, vì vậy nếu tỷ lệ của bạn nhỏ hơn 100000 yêu cầu mỗi giây, cơ hội va chạm là không đáng kể, vì vậy mọi biến chứng sẽ không có hiệu lực. Nếu tỷ lệ cao hơn, thì có một câu chuyện khác và bạn phải tính đến nhiều thứ khác nhau, bao gồm bộ nhớ đệm của bộ xử lý, chuyển đổi luồng và bộ thu gom rác, trong đó quyền truy cập vào bộ nhớ cache của bạn có thể không ở vị trí đầu tiên từ điểm hiệu suất xem. –

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