2015-07-02 55 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