基于Scala和Cats实现更通用的内存缓存
关于Cats Effects内存缓存实现的分析
一、ImmutableMapCache实现分析
1. 是否足够完善?
这个实现不算完善,存在不少设计和使用上的问题,具体分析如下。
2. 能否适配更多效果类型?
完全可以,而且当前的Async约束完全是多余的。因为Map的get和updated都是纯内存的纯函数操作,根本不需要异步或阻塞能力。你可以把效果类型约束降低到Applicative[F],只需要用Applicative[F].pure把纯值包装成F类型即可:
import cats.Applicative case class ImmutableMapCache[F[_]: Applicative, K, V](map: Map[K, V]) extends Cache[F, K, V] { override def get(key: K): F[Option[V]] = Applicative[F].pure(map.get(key)) override def put(key: K, value: V): F[Cache[F, K, V]] = Applicative[F].pure(ImmutableMapCache(map.updated(key, value))) } object ImmutableMapCache { def empty[F[_]: Applicative, K, V]: F[Cache[F, K, V]] = Applicative[F].pure(ImmutableMapCache(Map.empty)) }
这样就能适配所有支持Applicative的效果类型,范围比Async大得多。
3. 潜在问题
- 滥用
Async.blocking:Map的操作都是纯内存计算,没有任何阻塞IO操作,用blocking会把任务提交到阻塞线程池,反而浪费线程资源,完全没必要。 - 状态管理繁琐:每次
put都返回新的Cache实例,使用者必须手动维护最新的实例引用。在并发场景下,多个线程可能持有不同的实例副本,导致数据不一致,使用成本极高。 - 并发安全缺失:因为是不可变实例,更新操作生成新对象,但没有统一的状态共享机制,并发更新会产生多个独立的缓存实例,无法保证全局数据一致性。
二、基于Ref的SimpleCache实现分析
是否更优?
这个实现比ImmutableMapCache好太多,是更合理的选择:
- 状态管理更安全:用
Ref封装了Map的状态,Ref本身提供原子性的更新操作,内部自动处理并发问题,使用者只需要持有一个SimpleCache实例即可,不需要手动管理状态。 - 效果约束更宽松:只需要
Sync[F],比Async适配的效果类型更多(比如IO、SyncIO等都能支持)。 - 无多余阻塞操作:
Ref的get和update都是纯内存的原子操作,不需要blocking,性能更优。 - API更符合常规语义:
put返回F[Unit],符合缓存更新的常见使用场景,不需要返回新实例,使用起来更直观。
不过这个实现也有可以优化的点:
- 避免使用
Ref.unsafe:Ref.unsafe会绕过F的初始化流程,在某些并发场景下可能导致线程安全问题。应该提供安全的构造方法:import cats.effect.{Ref, Sync} import cats.syntax.all._ class SimpleCache[F[_]: Sync, K, V] private (cache: Ref[F, Map[K, V]]) extends Cache[F, K, V] { override def put(key: K, value: V): F[Unit] = cache.update(_.updated(key, value)) override def get(key: K): F[Option[V]] = cache.get.map(_.get(key)) } object SimpleCache { def apply[F[_]: Sync, K, V]: F[SimpleCache[F, K, V]] = Ref.of(Map.empty[K, V]).map(new SimpleCache(_)) } - 兼容原
Cachetrait:原Cachetrait的put方法返回F[Cache[F,K,V]],而当前SimpleCache的put返回F[Unit],方法签名不兼容。可以修改实现来适配trait:override def put(key: K, value: V): F[Cache[F, K, V]] = cache.update(_.updated(key, value)).as(this)
内容的提问来源于stack exchange,提问作者joesan
相关产品推荐
相关产品推荐

