何时对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层上使用的。
3941 0
|
6月前
|
机器学习/深度学习 SQL 关系型数据库
【MySQL进阶之路丨第十一篇】一文带你精通MySQL NULL值处理、正则表达式
【MySQL进阶之路丨第十一篇】一文带你精通MySQL NULL值处理、正则表达式
85 0
|
6月前
|
SQL 关系型数据库 MySQL
总结 vue3 的一些知识点:MySQL NULL 值处理
总结 vue3 的一些知识点:MySQL NULL 值处理
|
6月前
|
SQL 关系型数据库 MySQL
MySQL NULL 值处理
MySQL NULL 值处理
|
6月前
|
SQL 关系型数据库 MySQL
实时计算 Flink版产品使用合集之从MySQL同步数据到Doris时,历史数据时间字段显示为null,而增量数据部分的时间类型字段正常显示的原因是什么
实时计算Flink版作为一种强大的流处理和批处理统一的计算框架,广泛应用于各种需要实时数据处理和分析的场景。实时计算Flink版通常结合SQL接口、DataStreamAPI、以及与上下游数据源和存储系统的丰富连接器,提供了一套全面的解决方案,以应对各种实时计算需求。其低延迟、高吞吐、容错性强的特点,使其成为众多企业和组织实时数据处理首选的技术平台。以下是实时计算Flink版的一些典型使用合集。
|
6月前
|
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`不应加引号,否则更新会失败。
146 2
|
3月前
|
SQL 关系型数据库 MySQL
在 MySQL 中使用 IS NULL
【8月更文挑战第12天】
627 0
在 MySQL 中使用 IS NULL
|
3月前
|
SQL 关系型数据库 MySQL
mysql不等于<>取特定值反向条件的时候字段有null值或空值读取不到数据
对于数据库开发的专业人士来说,理解NULL的特性并知道如何正确地在查询中处理它们是非常重要的。以上所介绍的技巧和实例可以帮助你更精准地执行数据库查询,并确保数据的完整性和准确性。在编写代码和设计数据库结构时,牢记这些细节将有助于你避免许多常见的错误,提高数据库应用的质量与性能。
105 0
|
4月前
|
SQL 存储 索引
MySQL设计规约问题之为什么应该把字段定义为NOT NULL并且提供默认值
MySQL设计规约问题之为什么应该把字段定义为NOT NULL并且提供默认值
|
5月前
|
SQL 关系型数据库 MySQL
MySQL外键约束行为解析:CASCADE, NO ACTION, RESTRICT, SET NULL
MySQL外键约束行为解析:CASCADE, NO ACTION, RESTRICT, SET NULL
254 0