近段時(shí)間在學(xué)習(xí)緩存相關(guān)知識(shí)的時(shí)候,看到了緩存更新策略,于是就根據(jù)自己的理解,寫下這篇文章
分類
- Cache Aside
- Read / Write Though
- Write Behind
Cache Aside
-
步驟
- 讀請(qǐng)求未命中緩存,取數(shù)據(jù)庫數(shù)據(jù),并回寫緩存
- 寫請(qǐng)求先更新數(shù)據(jù)庫,再讓緩存失效
-
優(yōu)點(diǎn)
- 實(shí)現(xiàn)簡(jiǎn)單,調(diào)用者可控制數(shù)據(jù)持久化的細(xì)節(jié)
-
缺點(diǎn)
- 上層需要同時(shí)管理緩存與持久化,調(diào)用較復(fù)雜
- 寫請(qǐng)求與讀請(qǐng)求并發(fā),讀請(qǐng)求持續(xù)時(shí)間比寫請(qǐng)求長,可能會(huì)覆蓋舊數(shù)據(jù)到緩存中
-
使用場(chǎng)景
- 允許緩存數(shù)據(jù)不準(zhǔn)確的場(chǎng)景
- 因?yàn)椴l(fā)情況下,可能造成臟數(shù)據(jù)的情況,所以 QPS 較低場(chǎng)景也可以適用
代碼示例
public class CacheAside<T, K> implements CacheUpdate<T, K>{
private Map<K, T> map;
@Override
public T getData(K key) {
//if cache has data, return
return map.get(key);
}
@Override
public boolean updateData(K key, T data) {
map.remove(key, data);
return true;
}
@Override
public boolean addData(K key, T data) {
return Objects.nonNull(map.put(key, data));
}
@Override
public boolean removeData(K key) {
map.remove(key);
return true;
}
public CacheAside() {
map = new HashMap<>();
}
}
- 調(diào)用示例
public class CacheAsideClient<T, K> implements CacheUpdateClient<T, K>{
public CacheUpdateFactory<T, K> factory = CacheUpdateFactory.getInstance();
private CacheUpdate<T, K> cacheUpdate;
private DatabaseOperation<T, K> databaseOperation;
@Override
public T getData(K key){
//get data from cache
T dataFromCache = cacheUpdate.getData(key);
//if cache haven't, get from database and put to cache
if(Objects.nonNull(dataFromCache)){
return dataFromCache;
}
T dataFromDatabase = databaseOperation.getData(key);
cacheUpdate.addData(key, dataFromDatabase);
return dataFromDatabase;
}
@Override
public boolean updateData(K key, T data){
//update data to database
boolean updateToDatabaseRes = databaseOperation.updateData(key, data);
if(updateToDatabaseRes){
//invalid cache data
return cacheUpdate.removeData(key);
}
return false;
}
@Override
public boolean addData(K key, T data){
//add data to database
return databaseOperation.addData(key, data);
}
@Override
public boolean removeData(K key){
//remove from database
boolean removeFromDatabaseRes = databaseOperation.removeData(key);
if(removeFromDatabaseRes){
//invalid cache data
return cacheUpdate.removeData(key);
}
return false;
}
public CacheAsideClient() {
cacheUpdate = factory.getObject(CacheUpdateEnum.CACHE_ASIDE);
databaseOperation = (DatabaseOperation<T, K>) new MockDatabaseOperation<T>();
}
}
Read / Write Though
-
步驟
- 讀/寫請(qǐng)求都只依賴緩存
- 緩存數(shù)據(jù)同步持久化
-
優(yōu)點(diǎn)
- 上層對(duì)數(shù)據(jù)是否持久化/持久化實(shí)現(xiàn)無感
-
缺點(diǎn)
- 同步持久化性能較低,但能有效保證數(shù)據(jù)一致性
-
使用場(chǎng)景
- 性能要求不高的場(chǎng)景
代碼示例
public class ReadOrWriteThough<T, K> implements CacheUpdate<T, K>{
private DatabaseOperation<T, K> databaseOperation;
private Map<K, T> map;
@Override
public T getData(K key) {
//if cache has data, return
if(map.containsKey(key)){
return map.get(key);
}
//get data from database and write to cache
T data = databaseOperation.getData(key);
map.put(key, data);
return data;
}
@Override
public boolean updateData(K key, T data) {
map.put(key, data);
return databaseOperation.updateData(key, data);
}
@Override
public boolean addData(K key, T data) {
map.put(key, data);
return databaseOperation.addData(key, data);
}
@Override
public boolean removeData(K key) {
map.remove(key);
return databaseOperation.removeData(key);
}
public ReadOrWriteThough() {
databaseOperation = (DatabaseOperation<T, K>) new MockDatabaseOperation<>();
map = new HashMap<>();
}
}
- 調(diào)用示例
public class ReadOrWriteThoughClient<T, K> implements CacheUpdateClient<T, K>{
private CacheUpdateFactory<T, K> factory = CacheUpdateFactory.getInstance();
private CacheUpdate<T, K> cacheUpdate;
@Override
public T getData(K key) {
return cacheUpdate.getData(key);
}
@Override
public boolean updateData(K key, T data) {
return cacheUpdate.updateData(key, data);
}
@Override
public boolean addData(K key, T data) {
return cacheUpdate.addData(key, data);
}
@Override
public boolean removeData(K key) {
return cacheUpdate.removeData(key);
}
public ReadOrWriteThoughClient() {
cacheUpdate = factory.getObject(CacheUpdateEnum.READ_WRITE_THOUGH);
}
}
Write Behind
-
步驟
- 讀/寫請(qǐng)求都只依賴緩存
- 緩存數(shù)據(jù)異步批量持久化
-
優(yōu)點(diǎn)
- 上層對(duì)數(shù)據(jù)是否持久化/持久化實(shí)現(xiàn)無感
- 異步持久化,性能較 Read /Write Though 提高
-
缺點(diǎn)
- 異步持久化可能會(huì)導(dǎo)致數(shù)據(jù)丟失
-
使用場(chǎng)景
- 性能要求較高的場(chǎng)景
- 允許持久化數(shù)據(jù)丟失場(chǎng)景
代碼示例
public class WriteBehind<T, K> implements CacheUpdate<T, K> {
private Map<K, T> map;
private DatabaseOperation<T, K> databaseOperation;
private ThreadPoolExecutor threadPoolExecutor;
@Override
public T getData(K key) {
if(map.containsKey(key)){
return map.get(key);
}
T data = databaseOperation.getData(key);
map.put(key, data);
return data;
}
@Override
public boolean updateData(K key, T data) {
map.put(key, data);
threadPoolExecutor.execute(() -> databaseOperation.updateData(key, data));
return true;
}
@Override
public boolean addData(K key, T data) {
map.put(key, data);
threadPoolExecutor.execute(() -> databaseOperation.addData(key, data));
return true;
}
@Override
public boolean removeData(K key) {
map.remove(key);
threadPoolExecutor.execute(() -> databaseOperation.removeData(key));
return true;
}
public WriteBehind() {
map = new HashMap<>();
databaseOperation = (DatabaseOperation<T, K>) new MockDatabaseOperation<>();
threadPoolExecutor = new ThreadPoolExecutor(5, 10, 1000, TimeUnit.SECONDS, new ArrayBlockingQueue<>(1000), new ThreadPoolExecutor.CallerRunsPolicy());
}
}
- 調(diào)用示例
public class WriteBehindClient<T, K> implements CacheUpdateClient<T, K>{
private CacheUpdateFactory<T, K> cacheUpdateFactory = CacheUpdateFactory.getInstance();
private CacheUpdate<T, K> cacheUpdate;
@Override
public T getData(K key) {
return cacheUpdate.getData(key);
}
@Override
public boolean updateData(K key, T data) {
return cacheUpdate.updateData(key, data);
}
@Override
public boolean addData(K key, T data) {
return cacheUpdate.addData(key, data);
}
@Override
public boolean removeData(K key) {
return cacheUpdate.removeData(key);
}
public WriteBehindClient() {
cacheUpdate = cacheUpdateFactory.getObject(CacheUpdateEnum.WRITE_BEHIND);
}
}
總結(jié)
| 分類 | 優(yōu)點(diǎn) | 缺點(diǎn) | 使用場(chǎng)景 |
|---|---|---|---|
| Cache Aside | 1. 實(shí)現(xiàn)簡(jiǎn)單,調(diào)用者可控制數(shù)據(jù)持久化的細(xì)節(jié) | 1. 寫請(qǐng)求與讀請(qǐng)求并發(fā),讀請(qǐng)求持續(xù)時(shí)間比寫請(qǐng)求長,可能會(huì)覆蓋舊數(shù)據(jù)到緩存中<br />2. 上層需要同時(shí)管理緩存與持久化,調(diào)用較復(fù)雜 | 1. 允許緩存數(shù)據(jù)不準(zhǔn)確的場(chǎng)景<br />2. 因?yàn)椴l(fā)情況下,可能造成臟數(shù)據(jù)的情況,所以 QPS 較低場(chǎng)景也可以適用 |
| Read / Write Though | 1. 上層對(duì)數(shù)據(jù)是否持久化/持久化實(shí)現(xiàn)無感 | 1. 同步持久化性能較低,但能有效保證數(shù)據(jù)一致性 | 1. 性能要求不高的場(chǎng)景 |
| Write Behind | 1. 上層對(duì)數(shù)據(jù)是否持久化/持久化實(shí)現(xiàn)無感<br />2. 異步持久化,性能較 Read /Write Though 提高 | 1. 異步持久化可能會(huì)導(dǎo)致數(shù)據(jù)丟失 | 1. 性能要求較高的場(chǎng)景<br />2. 允許持久化數(shù)據(jù)丟失場(chǎng)景 |
本文首發(fā)于cartoon的博客
轉(zhuǎn)載請(qǐng)注明出處:https://cartoonyu.github.io