【问题标题】:How to cache results in scala?如何在scala中缓存结果?
【发布时间】:2011-04-08 18:08:24
【问题描述】:

This page有Map的getOrElseUpdate使用方法的描述:

object WithCache{
  val cacheFun1 = collection.mutable.Map[Int, Int]()
  def fun1(i:Int) = i*i
  def catchedFun1(i:Int) = cacheFun1.getOrElseUpdate(i, fun1(i))
}

所以你可以使用catchedFun1 来检查cacheFun1 是否包含与之关联的键和返回值。否则会调用fun1,然后将fun1的结果缓存到cacheFun1,然后返回fun1的结果。

我可以看到一个潜在的危险 - cacheFun1 可以变得很大。所以cacheFun1 必须由垃圾收集器以某种方式清理?

附: scala.collection.mutable.WeakHashMap and java.lang.ref.* 呢?

【问题讨论】:

标签: scala caching


【解决方案1】:

参见上述论文的Memo patternScalaz implementation

还可以查看 STM 实现,例如 Akka

并不是说这只是本地缓存,因此您可能需要查看分布式缓存或STM,例如CCSTMTerracottaHazelcast

【讨论】:

  • 只有 Memo Pattern 使用WeahHashMap,因此不是一个很好的缓存。
  • @Debilski 取决于“缓存”要求。在这种情况下,“弱缓存”可以提高发布者对“cacheFun1 可能 [变得太大] 的担忧”..
【解决方案2】:

看看spray缓存(超级简单好用)

http://spray.io/documentation/1.1-SNAPSHOT/spray-caching/

使工作变得简单,并具有一些不错的功能

例如:

      import spray.caching.{LruCache, Cache}

      //this is using Play for a controller example getting something from a user and caching it
      object CacheExampleWithPlay extends Controller{

        //this will actually create a ExpiringLruCache and hold data for 48 hours
        val myCache: Cache[String] = LruCache(timeToLive = new FiniteDuration(48, HOURS))

        def putSomeThingInTheCache(@PathParam("getSomeThing") someThing: String) = Action {
          //put received data from the user in the cache
          myCache(someThing, () => future(someThing))
          Ok(someThing)
        }

        def checkIfSomeThingInTheCache(@PathParam("checkSomeThing") someThing: String) = Action {
          if (myCache.get(someThing).isDefined)
            Ok(s"just $someThing found this in the cache")
          else
            NotFound(s"$someThing NOT found this in the cache")
        }
      }

【讨论】:

    【解决方案3】:

    在 scala 邮件列表中,sometimes 指向 Google collections library 中的 MapMaker。你可能想看看那个。

    【讨论】:

    【解决方案4】:

    对于简单的缓存需求,我还在 Scala 中使用Guava cache solution。 轻巧且经过实战考验。

    如果它符合您通常在下面概述的要求和限制,它可能是一个不错的选择:

    • 愿意花费一些内存来提高速度。
    • 预计键有时会被多次查询。
    • 您的缓存不需要存储比 RAM 容量更多的数据。 (Guava 缓存在应用程序的单次运行中是本地的。 它们不会将数据存储在文件中或外部服务器上。)

    使用示例如下:

      lazy val cachedData = CacheBuilder.newBuilder()
        .expireAfterWrite(60, TimeUnit.MINUTES)
        .maximumSize(10)
        .build(
          new CacheLoader[Key, Data] {
            def load(key: Key): Data = {
              veryExpansiveDataCreation(key)
            }
          }
        )
    

    要从中读取,您可以使用以下内容:

      def cachedData(ketToData: Key): Data = {
        try {
          return cachedData.get(ketToData)
        } catch {
          case ee: Exception => throw new YourSpecialException(ee.getMessage);
        }
      }
    

    【讨论】:

      【解决方案5】:

      由于之前没有提到过,让我把灯 Spray-Caching 放在桌面上,它可以独立于 Spray 使用,并提供预期的大小、生存时间、空闲时间驱逐策略。

      【讨论】:

        【解决方案6】:

        我们使用的是 Scaffine (Scala + Caffeine),您可以通过 here 了解它与其他框架相比的优缺点。

        你添加你的 sbt,

        "com.github.blemale" %% "scaffeine" % "4.0.1"
        

        建立你的缓存

        import com.github.blemale.scaffeine.{Cache, Scaffeine}
        import scala.concurrent.duration._
        
        val cachedItems: Cache[String, Int] = 
          Scaffeine()
            .recordStats()
            .expireAtferWrite(60.seconds)
            .maximumSize(500)
            .build[String, Int]()
        
        cachedItems.put("key", 1) // Add items
        
        cache.getIfPresent("key") // Returns an option
        

        【讨论】:

        • .build.buid的错字
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-01-01
        相关资源
        最近更新 更多