2015-07-02 84 views
3

我想提高函數的性能,以返回調整大小的圖像。所請求的圖像大小不應該有很大的變化(依賴於設備),所以以某種方式緩存結果是有意義的。Clojure - 在磁盤上記憶

我當然可以在電腦上保存的,並檢查調整後的圖像存在,並確保如果原始圖像被刪除,調整大小的版本太...

或者,我可以用一個memoized函數。但是由於結果可能相當大(我認爲圖像大約是5 - 10 MB),因此將它們存儲在內存中是沒有意義的(幾十GB圖像及其修改後的版本會很快填滿內存)。

那麼,有沒有一種方法可以獲得像常規Clojure defmemo一樣的memoized函數,但是由本地磁盤而不是內存中的文件夾支持?然後,我可以使用ttl策略來確保圖像不會長時間不同步。

crache類似的東西,但由文件系統支持?

+0

您可以將調整大小的圖像存儲在Redis或Memcached中,並具有一定的合理到期時間以自動清理未使用的記錄。 –

+0

只是看着你的'crache'鏈接,它看起來非常適合你的任務。 –

+0

@LeonidBeschastny好的crache由Redis支持,這是一個內存數據存儲。不幸的是,我沒有那麼多的RAM,這就是爲什麼我試圖避免這種情況並改爲使用文件系統。 – nha

回答

3

不要過度掛鉤它。您的文件系統作爲緩存是正確的想法。如果一個文件變得流行,並且文件被訪問了很多,那麼你的操作系統將確保它在RAM中。這與許多數據庫使用的策略相同。例如Elasticsearch要求你留下足夠的RAM以將Lucene索引文件放入RAM中。

不要修改你的文件!做它的功能性方式:將它們視爲不可變的數據。你的輸入文件不應該改變。如果確實如此,那麼這是一個新文件。硬盤空間非常便宜。不要害怕有許多文件在鋪設。如果必須的話,你可以做一個垃圾收集,在一段時間後刪除舊的/被標記的文件。

要查看文件是否在緩存中,只需檢查文件是否存在。如果不是:你只寫一次。

所以總結:

  • 讓你的O/S工作
  • 不要編輯文件的緩存。將它們視爲不可變的數據。一次寫入
  • 您的操作系統將釋放未使用的文件的RAM。硬盤空間非常便宜。
+0

你能擴展在「不變的文件」上有點? – nha

+0

圖像文件是一個圖像文件,不應該改變。調整大小的文件將始終是輸入圖像文件的調整大小的文件。除非您的調整大小算法改變,否則沒有理由重寫它。在磁盤上擁有多個不同大小沒有任何問題。維度的命名約定可能是最簡單的。文件名==緩存密鑰 – ClojureMostly

+0

對,這就是計劃(我甚至沒有考慮過不可變的,因爲它可能會刪除它們),但是這將通過一致的命名機制來實現,對。 +1 :)最後,我將使用文件系統實現core.cache。 – nha

1

你需要什麼聽起來像是Datomic的完美使用。它很容易從Clojure中使用,效率很高,並且像任何好的數據庫一樣,它在內存中擁有最近最少使用的(LRU)緩存。它還可以使用各種各樣的支持數據庫作爲基底,從嚴格的內存(最適合測試&實驗)到Postgres,Redis,DynamoDB,Riak等。還有一種「開發」模式,使用本地文件所有的存儲。

見這裏所有的細節:

有一個標準版的免費,永久授權,適合於大多數用途。對於高級功能,可以使用付費版本。

+0

那麼這會增加相當多的運營開銷不是嗎?並糾正我,如果我錯了,但緩存是一個內存中的緩存,這是我想要避免的? – nha

+0

開銷將非常小。 Datomic使用像Postgres這樣的備份數據庫,將大部分數據存儲在磁盤上。只有最近使用的項目纔會在內存中。 –

+0

嗯,但現在我必須部署Datomic + Postgres ..令人驚歎的技術,但聽起來像過度保存圖像給我。 – nha

2

爲什麼不從clojure.core.cache實現一個TTL-Cache,用它包裝你需要的功能?您的密鑰可以是任何標識您調整大小的圖像,並且該值將是其在磁盤上的位置。然後你可以實現某種獲取或設置!函數,並將其傳遞給函數,以便在圖像不存在時調用該函數以生成圖像。 例如

(def Cache (atom (cache/ttl-cache-factory {} :ttl 20000))) 

(defn get-or-update! 
    "wraps the recommended has-hit-get pattern 
    https://github.com/clojure/core.cache/wiki/Using" 
    [key fn] 
    (if (cache/has? @Cache key) 
    (get (swap! Cache #(cache/hit % key)) key) 
    (get (swap! Cache #(cache/miss % key (fn))) key))) 
+0

+1不錯的片段。那麼這或多或少是我最初的想法(我有些驚訝,沒有人這樣做,但這應該足夠簡單以實現)。現在,在服務器重新啓動/更新之後,「ttl」信息會持久嗎?它與原子無關,但會與磁盤上的持久數據保持一致。 – nha

+1

我更新了片段。不,在這種情況下,您可能還想編寫一個函數,從磁盤上的現有映像生成緩存的初始種子。如果通過重新啓動來維護'ttl'信息很重要,它會變得更復雜一點,您可能需要考慮擴展core.cache或使用其他解決方案。 – Nick

+2

這個實現是錯誤的。緩存原子的狀態可以在'cache/has'和'cache/hit'或'cache/miss'函數調用之間改變。你的實現應該看起來像這樣:'(get(swap/Cache#)(if(cache/has?%key)(cache/hit%key)(cache/miss%(fn)key))))' – Palesz