何时对null值做Cache

简介:

前几天发现某个系统对某个远程调用接口的调用量大幅上升,涨幅不可思议。根据接口调用上升的时间点和发布记录,查看SVN提交记录,发现是在系统主路径中添加了这个接口的调用,难道这个接口没有做Cache吗?仔细一看,倒是也做了Cache,并且这个RPC对应的DB表的数据量非常小,按理说是能全部被缓存起来的。那么为什么会反复调用,看起来仿佛没有Cache一样呢?

直觉是缓存被不存在的数据击穿了,马上验证。

通过对系统方法的追踪,发现每次调用传入的参数都是0,再去DB里面查,0对应的结果确实为空。

所以这是一个典型的因为空记录导致的缓存被击穿的案例。

解决方法很简单,对不存在的记录做一个null的Cache,下次就不会落到远端了。不过这里结合业务的特定场景,我只是加了一个判断,当值大于0才会去查询,这样连一次查询Cache的开销也省掉了。

这个简单的问题可以衍生出一些思考。

一、何时做put

通常的缓存put策略有两种:

1、查询时put:先查Cache,若不命中,则查存储(例如DB),查到后put进Cache。

2、写入时put:当数据被插入或修改时,主动put一份到Cache。

实践中其实第一种用法更普遍,只有当数据被用到了才会进入Cache。

二、如果DB没查到,是否要put null

这个就跟具体的业务场景相关了。

如果你的数据变化不频繁,那么put一个null,就可以有效起到用Cache减轻后端查询压力的作用。

但如果你的数据变化很频繁,那么put null的结果很可能导致业务上的不一致性,此时就不该做null的Cache。

即便是数据变化不频繁的情况下,如果在null的Cache失效之前,DB中又写入了新的值造成了非null的情况,这时的不一致也是不能接受的。所以在做了null的Cache后,写入的时候应该做到主动失效。

三、异常情况的处理

如果在查询DB的时候抛出了异常,例如连接拿不到、超时等等异常的时候,不应该做null的Cache。

因为此时你并不知道DB中究竟是否存在你要查的数据,如果放了一个null,当DB恢复后,就造成了数据不一致。

实践中这个问题更常见的场景在于,有时候我们的DAO没有把该抛的Exception抛出来,而是直接return null。这时外界的调用方就无法区分,你到底是没查到还是查的过程中出了异常。所以说,该抛的异常应该抛出去,不要什么情况都自己吃掉了。

四、How to put null

存放null的方法有很多,鉴于很多Cache不允许value为null,可以直接放一个跟正常对象不一致的对象,例如boolean, int这种基本类型,然后用instanceof去判断。

不过这种方式总显得不那么优雅,我觉得这里用存放Option的方式就不错。

Option本身可以认为是一个存放对象的小容器,它有两种状态:

1、存放了一个非null的对象

2、null

获取Option的时候判断是否为null就很方便了。

以Guava的库为例子,实现如下:


public Object get(String key) {
        Optional<Object> result = getFromCache(key);
        if (result.isPresent()) { // 如果不为null
                return result.get();
        } else {
                Object obj = queryFromDB(key);
                putToCache(key, Optional.fromNullable(obj)); // Option可能有值可能为null
                return obj;
        }
}

public Optional<Object> getFromCache(String key) {
        return cache.get(key);
}

public void putToCache(String key, Optional<Object> value) {
        cache.put(key, value);
}

public abstract Object queryFromDB(String key);


目录
相关文章
|
缓存 NoSQL Java
【redis】5.spring boot项目中,直接在spring data jpa的Repository层使用redis +redis注解@Cacheable直接在Repository层使用,报错问题处理Null key returned for cache operation
spring boot整合redis:http://www.cnblogs.com/sxdcgaq8080/p/8028970.html 首先,明确一下问题的场景 之前在spring boot整合redis,关于redis的使用都是在repository层上再封装一层service层,在service层上使用的。
3886 0
|
1月前
|
机器学习/深度学习 SQL 关系型数据库
【MySQL进阶之路丨第十一篇】一文带你精通MySQL NULL值处理、正则表达式
【MySQL进阶之路丨第十一篇】一文带你精通MySQL NULL值处理、正则表达式
47 0
|
1月前
|
SQL 关系型数据库 MySQL
总结 vue3 的一些知识点:MySQL NULL 值处理
总结 vue3 的一些知识点:MySQL NULL 值处理
|
1月前
|
SQL 关系型数据库 MySQL
MySQL NULL 值处理
MySQL NULL 值处理
|
24天前
|
SQL 关系型数据库 MySQL
实时计算 Flink版产品使用合集之从MySQL同步数据到Doris时,历史数据时间字段显示为null,而增量数据部分的时间类型字段正常显示的原因是什么
实时计算Flink版作为一种强大的流处理和批处理统一的计算框架,广泛应用于各种需要实时数据处理和分析的场景。实时计算Flink版通常结合SQL接口、DataStreamAPI、以及与上下游数据源和存储系统的丰富连接器,提供了一套全面的解决方案,以应对各种实时计算需求。其低延迟、高吞吐、容错性强的特点,使其成为众多企业和组织实时数据处理首选的技术平台。以下是实时计算Flink版的一些典型使用合集。
|
1月前
|
SQL 关系型数据库 MySQL
python在mysql中插入或者更新null空值
这段代码是Python操作MySQL数据库的示例。它执行SQL查询从表`a_kuakao_school`中选取`id`,`university_id`和`grade`,当`university_id`大于0时按升序排列。然后遍历结果,根据`row[4]`的值决定`grade`是否为`NULL`。若不为空,`grade`被格式化为字符串;否则,设为`NULL`。接着构造UPDATE语句更新`university`表中对应`id`的`grade`值,并提交事务。重要的是,字符串`NULL`不应加引号,否则更新会失败。
50 2
|
30天前
|
关系型数据库 MySQL 数据处理
实时计算 Flink版产品使用合集之如果在 MySQL 表中为某个字段设置了默认值,并且在插入数据时指定了该字段为 NULL,那么 MySQL 是否会使用默认值来填充这个字段
实时计算Flink版作为一种强大的流处理和批处理统一的计算框架,广泛应用于各种需要实时数据处理和分析的场景。实时计算Flink版通常结合SQL接口、DataStream API、以及与上下游数据源和存储系统的丰富连接器,提供了一套全面的解决方案,以应对各种实时计算需求。其低延迟、高吞吐、容错性强的特点,使其成为众多企业和组织实时数据处理首选的技术平台。以下是实时计算Flink版的一些典型使用合集。
|
1月前
|
存储 关系型数据库 MySQL
Flink CDC中mysql cdc 抽取这个时间字段的值为null 有什么好的解决方案吗 ?
Flink CDC中mysql cdc 抽取这个时间字段的值为null 有什么好的解决方案吗 ?
127 0
|
1月前
|
SQL 关系型数据库 MySQL
mysql查询语句的访问方法const、ref、ref_or_null、range、index、all
mysql查询语句的访问方法const、ref、ref_or_null、range、index、all
|
1月前
|
关系型数据库 MySQL
mysql中判断NULL和空字符串
mysql中判断NULL和空字符串
16 0