【问题标题】:Clojure - memoize on diskClojure - 在磁盘上记忆
【发布时间】:2015-09-20 02:44:02
【问题描述】:

我想提高返回调整大小图像的函数的性能。请求的图像大小不应有很大差异(取决于设备),因此以某种方式缓存结果是有意义的。

我当然可以将它存储在磁盘上,并检查调整后的图像是否存在,并确保如果原始图像被删除,调整后的版本也......

或者,我可以使用记忆函数。但是由于结果可能非常大(我认为图像大约 5 - 10 MB),将它们存储在内存中是没有意义的(几十 GB 的图像及其修改版本会很快填满内存)。

那么,有没有办法让记忆函数像常规的 Clojure defmemo,但由本地磁盘中的文件夹而不是内存支持?然后我可以使用ttlstrategy 来确保图像不会长时间不同步。

类似于crache,但由文件系统支持?

【问题讨论】:

  • 您可以将调整大小的图像存储在 Redis 或 Memcached 中并设置合理的过期时间,以自动清理未使用的记录。
  • 刚刚查看了您的 crache 链接,它看起来非常适合您的任务。
  • @LeonidBeschastny Well crache 由 Redis 支持,这是一个内存数据存储。不幸的是,我没有那么多 RAM,这就是为什么我试图避免这种情况并改用文件系统。

标签: caching clojure memoization


【解决方案1】:

为什么不从 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)))

【讨论】:

  • +1 不错的 sn-p。嗯,这或多或少是我最初的想法(我有点惊讶没有人这样做,但这应该足够简单地实现)。现在ttl信息会在服务器重启/更新后保持不变吗?原子无关紧要,但磁盘上的持久数据会影响。
  • 我更新了sn-p。不,在这种情况下,您可能还想编写一个函数,从磁盘上的现有图像生成缓存的初始种子。如果通过重启维护 ttl 信息很重要,它开始变得有点复杂,您可能需要考虑扩展 core.cache 或使用其他解决方案。
  • 这个实现是错误的。缓存原子的状态可以在cache/hascache/hitcache/miss 函数调用之间变化。您的实现应如下所示:(get (swap! Cache #(if (cache/has? % key) (cache/hit % key) (cache/miss % (fn) key))))
【解决方案2】:

不要想太多。您的文件系统作为缓存是正确的想法。如果一个文件变得流行并且该文件被大量访问,那么您的操作系统将确保它在 RAM 中。这与许多数据库使用的策略相同。例如,Elasticsearch 要求您留出足够的 RAM 以在 RAM 中保存 Lucene 索引文件。

也不要修改您的文件!以功能性方式进行:将它们视为不可变数据。您的输入文件不应更改。如果是,那么它是一个新文件。硬盘空间非常便宜。不要害怕周围有很多文件。如果必须,您可以进行垃圾回收,在一段时间后删除旧的/标记的文件。

要查看文件是否在缓存中,您只需检查文件是否存在。如果不是:你只写一次。

总结一下:

  • 让您的操作系统处理缓存
  • 不要编辑您的文件。将它们视为不可变数据。写一次
  • 您的操作系统将释放未使用文件的 RAM。硬盘空间超级便宜。

【讨论】:

  • 你能扩展一下“不可变文件”吗?
  • 图像文件是图像文件,不应发生变异。调整大小的文件将始终是输入图像文件的调整大小的文件。除非您的调整大小算法发生变化,否则没有理由重新编写它。磁盘上有多个不同大小并没有错。维度的命名约定可能是最简单的。文件名 == 缓存键
  • 对,这就是计划(我什至没有考虑不可变,因为可以删除它们),但这将通过一致的命名方案来实现,对吧。 +1 :) 最后,我将使用文件系统实现 core.cache。
【解决方案3】:

你所需要的听起来像是 Datomic 的完美用途。它很容易从 Clojure 中使用,非常高效,并且像任何好的数据库一样,它在内存中具有最近最少使用 (LRU) 缓存。它还可以使用各种各样的后备数据库作为基础,从严格的内存(最适合测试和实验)到 Postgres、Redis、DynamoDB、Riak 等。还有一个“开发”模式使用本地文件所有存储空间。

在此处查看所有详细信息:

有一个标准版本,具有适用于大多数用途的免费永久许可证。对于高级功能,可以使用付费版本。

【讨论】:

  • 那会增加相当多的运营开销,不是吗?如果我错了,请纠正我,但缓存是内存中的缓存,这是我试图避免的事情?
  • 开销会很小。 Datomic 使用像 Postgres 这样的后备数据库,它将大部分数据存储在磁盘上。只有最近使用的项目才会在内存中。
  • 好吧,但现在我必须部署 Datomic + Postgres.. 令人惊叹的技术,但对于我来说存储图像听起来有点矫枉过正。
  • 为上面的本地文件存储添加了“dev”模式的链接。
  • 非常感谢我没有想到的链接。我仍然有点不愿意介绍 datomic(例如,我可以只使用 postgres),但我一定会记住这一点,以后可能会派上用场。所以 +1,但我仍在寻找一个简单的缓存建议,如果有人有的话。
猜你喜欢
  • 2013-05-04
  • 2014-12-28
  • 2011-02-26
  • 2016-01-04
  • 2022-01-22
  • 1970-01-01
  • 2011-04-23
  • 2013-09-06
  • 2011-05-17
相关资源
最近更新 更多