Local Cache要存对象还是序列化数据?

序言

偶然间登录掘金账号,想起了周末去咖啡店写文章的我,现在改为了健身(健康为主,力争满头秀发),还是觉得惭愧,既然路过就写一段吧。深度不深,只是通过一个小案例引发的一些思考,不喜勿喷~

背景

在日常工作中,我们经常会遇到需要使用缓存的场景,例如存一些临时数据,减少下游存储介质访问等。

首先,我们要抉择remote cache还是local cache, remote cache通常以中间件的形式在其mem中存储,服务集群中的各个节点都可以通过相同的key访问remote cache拿到数据;local cache则采用该服务的内存资源,服务集群的各个节点存储自己所需要的数据,各个节点的key分布各不相同(随着业务而定)。

其次,该以什么形式存储缓存数据,remote cache比较简单,像redis会规定存序列化数据;而local cache属于自由发挥,我们可以直接set一个对象进去,get时也不用反序列化。

那local cache存对象不是一劳永逸吗?既节省耗时又省序列化开销。

Local Cache存对象

简单用golang来释义(省略了error等其他逻辑),实际工作当中,看到了如下代码:

scss 复制代码
// lru cache实现
func (l *lruCache) Set(key string, val *Data) {
    l.lock.Lock()
    defer l.lock.Unlock()
    
    l.Set(key, val) 
}

func (l *lruCache) Get(key string) *Data {
    l.lock.Lock()
    defer l.lock.Unlock()
    
    return l.Get(key)
}

// 业务
data := l.Get(key)
// 拿到data,继续写逻辑
...

可以看到,local cache存了对象,看起来没啥问题,因为从cache里拿到对象继续写业务逻辑,也对并发场景用mutex进行了处理。

注意点:怎么保证从cache里拿到的对象是immutable?

如果仔细看就会发现,我虽然在cache实现里用锁做了控制,但我拿到的data指针是不可控的,其他人很可能就改了里面的数据。

kotlin 复制代码
// 业务
data := l.Get(key)
// 拿到data,我要改里面的value
...
data.value = b
...

这样的情况下,cache里的数据也被更改,在并发环境中甚至会热key会被频繁更改,导致线上问题。 当然,像C++/Java为对象和成员变量提供了些immutable语义,但这种设计是否把"锅"甩到了cache方法之外呢? 如果必须要改数据(很有可能过了一阵子有一个新需求),后续业务逻辑根据场景处理浅拷贝/深拷贝,保证cache里的数据不被变更。

Local Cache存序列化数据

存序列化数据就和remote cache实现方式差不多了(error处理等逻辑略过)。

scss 复制代码
// lru cache实现
func (l *lruCache) Set(key string, val *Data) {

    bytes, _ := marshal(&val)
    
    l.lock.Lock()
    defer l.lock.Unlock()
    
    l.Set(key, bytes) 
}

func (l *lruCache) Get(key string) *Data {
    l.lock.Lock()
    bytes, _ := l.Get(key)
    l.lock.Unlock()
    
    d, _:= unmarshal(bytes)
    return d
}

// 业务
data := l.Get(key)
// 拿到data,继续写逻辑
...

这样,后续业务逻辑就不会担心对对象做任何操作,因为相当于进行了一次深拷贝,拿到的就是新的对象。

总结

从上述小的例子可以看出,使用local cache也要注意场景,特别是关注cache中取出来的数据有无变更,导致影响业务逻辑。

相关推荐
武子康1 小时前
Java-72 深入浅出 RPC Dubbo 上手 生产者模块详解
java·spring boot·分布式·后端·rpc·dubbo·nio
椰椰椰耶3 小时前
【Spring】拦截器详解
java·后端·spring
brzhang4 小时前
我操,终于有人把 AI 大佬们 PUA 程序员的套路给讲明白了!
前端·后端·架构
wan_da_ren6 小时前
JVM监控及诊断工具-GUI篇
java·开发语言·jvm·后端
【本人】6 小时前
Django基础(一)———创建与启动
后端·python·django
lifallen6 小时前
Kafka 时间轮深度解析:如何O(1)处理定时任务
java·数据结构·分布式·后端·算法·kafka
mCell7 小时前
Webhook:连接、自动化与系统集成的新范式
ci/cd·go·github
你的人类朋友7 小时前
【✈️速通】什么是SIT,什么是UAT?
后端·单元测试·测试
程序无bug9 小时前
后端3行代码写出8个接口!
java·后端
绝无仅有9 小时前
使用LNMP一键安装包安装PHP、Nginx、Redis、Swoole、OPcache
后端·面试·github