2023-11-21 09:31:19 21阅读
缓存(Cache)是计算机领域非常常见的存储机制,它的基本思想是让数据更接近于使用方,在需要时提供快速的数据访问,以减轻系统资源的负担,提升应用程序的性能和可扩展性。
在使用缓存时也需要注意一些问题,它们可能会影响数据准确性或者加剧系统性能的恶化,例如:
随着技术应用经验的沉淀,有一些指导如何使用缓存来优化数据访问和提高系统性能的模式被总结出来,这些模式也称为缓存使用模式,可以根据不同的应用场景和需求采取不同的策略。在后端系统开发中,常见的缓存使用模式可以分为两类:
Cache-Aside是最常用的缓存模式,业务系统分别管理缓存和数据源,决定缓存数据的加载和更新。
在读场景,先从缓存中获取数据,如果数据存在则直接返回,如果没有命中,则回源到记录系统并将数据放入缓存,以供下次读取使用。示例代码如下:
Object value = cache.get(key);
if (value == null) {
value = loadFromSoR(key);
cache.put(key, value);
}
return value;
在写场景,先将数据写到记录系统,写入成功后将数据同步写入缓存,或使缓存数据失效,下次读取时再进行加载。示例代码如下:
boolean r = writeToSoR(key, value);
if (r) {
cache.put(key, value);
// or cache.invalidate(key);
}
这种模式适用于读多写少的场景,比如用户信息、商品信息、新闻报道等,数据一旦写入缓存,很少会再进行修改。在并发更新的时候,Cache-Aside模式可能会出现缓存和数据库双写不一致的情况,例如:
对于该模式下数据不一致的问题,业务系统一般可以采取如下几种应对方式:
Cache-Aside也可以说是最基本的缓存使用模式,在缓存和数据源的读写框架下,给予业务系统足够的自由,去定制自己的管理逻辑,满足编码复杂度、系统性能和数据一致性的需求。相比之下,Cache-As-SoR模式的三种实现,更多的是提供特定场景的策略封装,原理上并没有太大差别。
Read-Through模式与Cache-Aside模式很相似,不同点在于应用系统不关注数据实际是从哪里读取的(缓存还是记录系统),也不关注数据是如何加载到缓存的,应用系统只面向缓存读取数据,而缓存中的数据从哪里来由缓存系统决定。
读穿透模式一般需要配置一个CacheLoader组件用来回源加载数据,如果缓存没有命中,则委托给CacheLoader,以Guava Cache为例,实现如下:
LoadingCache<String, Object> cache = CacheBuilder.newBuilder()
.softValues()
.maximumSize(1024)
.expireAfterWrite(10, TimeUnit.MINUTES)
.build(key -> loadFromSoR(key)); // new CacheLoader<String, Object>
使用CacheLoader可以将缓存数据加载逻辑收敛到同一个地方,于业务系统开发有几个好处:
在Write-Though模式下,应用系统调用缓存的写操作接口来变更数据,由缓存系统负责同时更新缓存数据和后端记录系统数据。
写穿透模式一般需要配置一个CacheWriter组件用来将数据回写到记录系统,以Ehcache为例,实现如下:
CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder().build(true);
Cache<String, Object> cache = cacheManager.createCache(
"myCache",
CacheConfigurationBuilder.newCacheConfigurationBuilder(String.class, Object.class)
.withExpiry(Expirations.timeToLiveExpiration(Duration.of(10, TimeUnit.MINUTES)))
.withLoaderWriter(new DefaultCacheLoaderWriter<String, Object>() {
// 以下方法省略
// write
// writeAll
// delete
// deleteAll
}).build());
这种模式实现上隐含对数据一致性的保障,但可能会影响系统的性能,适用于交易系统这类对缓存数据要求强一致的需求场景,一般不在高频写场景使用。
Write-Behind也称为Write-Back(回写)模式,与Write-Though同步写缓存和记录系统不同,它是一种异步写入模式。在代码层面,应用系统也是只面向缓存的写操作接口,但在执行写操作时,缓存系统只更新缓存数据,后续再异步地更新到后端记录系统当中。
这种模式可以提高系统的性能,也可以增加更多的写入策略,例如批量写、合并写、延时写和写限流等等,但也很可能增加数据不一致的风险,比较适用于单点读但高频写的场景。
2023-11-21 09:31:19 21阅读
缓存(Cache)是计算机领域非常常见的存储机制,它的基本思想是让数据更接近于使用方,在需要时提供快速的数据访问,以减轻系统资源的负担,提升应用程序的性能和可扩展性。
在使用缓存时也需要注意一些问题,它们可能会影响数据准确性或者加剧系统性能的恶化,例如:
随着技术应用经验的沉淀,有一些指导如何使用缓存来优化数据访问和提高系统性能的模式被总结出来,这些模式也称为缓存使用模式,可以根据不同的应用场景和需求采取不同的策略。在后端系统开发中,常见的缓存使用模式可以分为两类:
Cache-Aside是最常用的缓存模式,业务系统分别管理缓存和数据源,决定缓存数据的加载和更新。
在读场景,先从缓存中获取数据,如果数据存在则直接返回,如果没有命中,则回源到记录系统并将数据放入缓存,以供下次读取使用。示例代码如下:
Object value = cache.get(key);
if (value == null) {
value = loadFromSoR(key);
cache.put(key, value);
}
return value;
在写场景,先将数据写到记录系统,写入成功后将数据同步写入缓存,或使缓存数据失效,下次读取时再进行加载。示例代码如下:
boolean r = writeToSoR(key, value);
if (r) {
cache.put(key, value);
// or cache.invalidate(key);
}
这种模式适用于读多写少的场景,比如用户信息、商品信息、新闻报道等,数据一旦写入缓存,很少会再进行修改。在并发更新的时候,Cache-Aside模式可能会出现缓存和数据库双写不一致的情况,例如:
对于该模式下数据不一致的问题,业务系统一般可以采取如下几种应对方式:
Cache-Aside也可以说是最基本的缓存使用模式,在缓存和数据源的读写框架下,给予业务系统足够的自由,去定制自己的管理逻辑,满足编码复杂度、系统性能和数据一致性的需求。相比之下,Cache-As-SoR模式的三种实现,更多的是提供特定场景的策略封装,原理上并没有太大差别。
Read-Through模式与Cache-Aside模式很相似,不同点在于应用系统不关注数据实际是从哪里读取的(缓存还是记录系统),也不关注数据是如何加载到缓存的,应用系统只面向缓存读取数据,而缓存中的数据从哪里来由缓存系统决定。
读穿透模式一般需要配置一个CacheLoader组件用来回源加载数据,如果缓存没有命中,则委托给CacheLoader,以Guava Cache为例,实现如下:
LoadingCache<String, Object> cache = CacheBuilder.newBuilder()
.softValues()
.maximumSize(1024)
.expireAfterWrite(10, TimeUnit.MINUTES)
.build(key -> loadFromSoR(key)); // new CacheLoader<String, Object>
使用CacheLoader可以将缓存数据加载逻辑收敛到同一个地方,于业务系统开发有几个好处:
在Write-Though模式下,应用系统调用缓存的写操作接口来变更数据,由缓存系统负责同时更新缓存数据和后端记录系统数据。
写穿透模式一般需要配置一个CacheWriter组件用来将数据回写到记录系统,以Ehcache为例,实现如下:
CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder().build(true);
Cache<String, Object> cache = cacheManager.createCache(
"myCache",
CacheConfigurationBuilder.newCacheConfigurationBuilder(String.class, Object.class)
.withExpiry(Expirations.timeToLiveExpiration(Duration.of(10, TimeUnit.MINUTES)))
.withLoaderWriter(new DefaultCacheLoaderWriter<String, Object>() {
// 以下方法省略
// write
// writeAll
// delete
// deleteAll
}).build());
这种模式实现上隐含对数据一致性的保障,但可能会影响系统的性能,适用于交易系统这类对缓存数据要求强一致的需求场景,一般不在高频写场景使用。
Write-Behind也称为Write-Back(回写)模式,与Write-Though同步写缓存和记录系统不同,它是一种异步写入模式。在代码层面,应用系统也是只面向缓存的写操作接口,但在执行写操作时,缓存系统只更新缓存数据,后续再异步地更新到后端记录系统当中。
这种模式可以提高系统的性能,也可以增加更多的写入策略,例如批量写、合并写、延时写和写限流等等,但也很可能增加数据不一致的风险,比较适用于单点读但高频写的场景。