成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

你知道緩存的這個問題到底把多少程序員坑慘了嗎?

開發 前端
如果是比較復雜的項目,甚至能再進一步的優化,也就是借用定時任務和MQ來替代休眠線程,實現異步刪除緩存,達到弱一致性的結果。

引言

你是不是也經歷過這種情況?某個陽光明媚的上午,你正享受著枸杞泡水和敲代碼的美妙時刻,突然接到客服部門的電話。

電話那頭的聲音急切又焦慮:“客戶抱怨說,怎么點擊購買總是失敗?”

作為技術人員,你瞬間腦袋里浮現出無數可能的原因,其中最讓人頭疼的,莫過于緩存與數據庫的不一致問題。

這個問題不僅影響用戶體驗,還可能導致業務數據的混亂。

今兒就來聊聊緩存與數據庫不一致的問題,幫大家理清楚問題所在,并給出推薦方案。

問題剖析

在現代系統中,緩存可以極大地提升性能,減少數據庫的壓力。

然而,一旦緩存和數據庫的數據不一致,就會引發各種詭異的問題。

系統維護的重要性在此凸顯,極端手段比如清空緩存,可能會在短時間內解決問題,但從長遠來看,這些操作往往帶來更大的隱患。

解決方案概覽

我們來看看幾種常見的解決緩存與數據庫不一致的方案,每種方案都有各自的優缺點:

  1. 先更新緩存,再更新數據庫
  2. 先更新數據庫,再更新緩存
  3. 先刪除緩存,后更新數據庫
  4. 先更新數據庫,后刪除緩存

深入探討

先更新緩存,再更新數據庫

這種方案看似簡單,實際上很少被推薦。

原因在于如果在更新數據庫之前發生了錯誤,緩存中的數據將和數據庫中的數據不一致,最終導致更大的問題。

public void updateCacheThenDatabase(String key, String value) {
    cache.put(key, value);
    try {
        database.update(key, value);
    } catch (Exception e) {
        // 數據庫更新失敗,緩存數據可能錯誤
        System.err.println("數據庫更新失敗: " + e.getMessage());
    }
}

先更新數據庫,再更新緩存

這種方法解決了更新緩存失敗的問題,但可能引發另外一個問題:

在高并發場景下,數據庫已經更新,但緩存還沒有更新時,其他請求可能會讀到舊的緩存數據。

public void updateDatabaseThenCache(String key, String value) {
    database.update(key, value);
    cache.put(key, value);
}

先刪除緩存,后更新數據庫

這種方案在高并發下容易產生問題:

在緩存刪除和數據庫更新之間的時間窗口內,其他請求可能會讀取到舊的數據,導致短時間內的數據不一致。

public void deleteCacheThenUpdateDatabase(String key, String value) {
    cache.remove(key);
    try {
        database.update(key, value);
    } catch (Exception e) {
        // 數據庫更新失敗,需要重新設置緩存
        cache.put(key, getOldValueFromDatabase(key));
        System.err.println("數據庫更新失敗: " + e.getMessage());
    }
}

先更新數據庫,后刪除緩存

這是較為推薦的一種方法,但在高并發場景下也有一定的局限性:

如果數據庫更新成功但緩存刪除失敗,可能導致短時間內的數據不一致。

public void updateDatabaseThenDeleteCache(String key, String value) {
    database.update(key, value);
    cache.remove(key);
}

強一致性與最終一致性

在討論一致性的時候,我們常常會提到強一致性和最終一致性。

強一致性保證每次讀取的數據都是最新的,但在分布式系統中實現成本較高。

最終一致性則允許數據在一定時間內不一致,適用于大多數實際業務場景。

根據業務需求權衡這兩者,是緩存策略設計中的重要一步。

后面我會給出一個弱一致性的推薦方案,供大家參考。

SpringCache

SpringCache是一個非常實用的緩存管理框架,能幫助我們簡化緩存操作。

以下是一個簡單的SpringCache配置示例:

@Configuration
@EnableCaching
public class CacheConfig {
    @Bean
    public CacheManager cacheManager() {
        return new ConcurrentMapCacheManager("entities");
    }
}

SpringCache 的優點:

  • 簡化緩存管理:SpringCache 提供了簡潔的 API,能夠輕松集成到現有項目中。
  • 注解驅動:使用注解如 @Cacheable、@CachePut 和 @CacheEvict,使得緩存操作更直觀。
  • 靈活配置:支持多種緩存實現,能夠根據需求選擇合適的緩存方案。

SpringCache 的缺點:

  • 限制性:默認的緩存管理器在一些復雜場景下可能不夠靈活。
  • 性能瓶頸:在高并發場景下,內存緩存可能會成為性能瓶頸,需要合理設計和優化。

常用的注解包括@Cacheable、@CachePut和@CacheEvict:

@Cacheable("entities")
public Entity findById(Long id) {
    // 如果緩存中存在,則直接返回緩存中的數據
    // 否則調用方法查詢數據庫,并將結果存入緩存
    return repository.findById(id).orElse(null);
}

@CachePut(value = "entities", key = "#entity.id")
public Entity updateEntity(Entity entity) {
    // 更新數據庫,同時更新緩存中的數據
    return repository.save(entity);
}

@CacheEvict(value = "entities", allEntries = true)
public void clearCache() {
    // 清空緩存
}

緩存預熱策略

緩存預熱的重要性不言而喻,上線后瞬時大流量可能導致緩存擊穿。

以下是幾種常見的緩存預熱方案:

  1. 啟動時加載:系統啟動時加載常用數據到緩存。
@Component
public class CachePreloader {
    @Autowired
    private CacheManager cacheManager;

    @PostConstruct
    public void preload() {
        Cache cache = cacheManager.getCache("entities");
        if (cache != null) {
            // 假設我們有一個服務可以獲取需要預熱的數據
            List<Entity> entities = fetchEntitiesForPreloading();
            for (Entity entity : entities) {
                cache.put(entity.getId(), entity);
            }
        }
    }

    private List<Entity> fetchEntitiesForPreloading() {
        // 獲取需要預熱的數據
        return repository.findAll();
    }
}
  1. 定時加載:定時任務定期加載數據。
@Component
public class ScheduledCachePreloader {
    @Autowired
    private CacheManager cacheManager;

    @Scheduled(fixedRate = 60000) // 每分鐘執行一次
    public void preload() {
        Cache cache = cacheManager.getCache("entities");
        if (cache != null) {
            // 獲取需要預熱的數據并加載到緩存
            List<Entity> entities = fetchEntitiesForPreloading();
            for (Entity entity : entities) {
                cache.put(entity.getId(), entity);
            }
        }
    }

    private List<Entity> fetchEntitiesForPreloading() {
        // 獲取需要預熱的數據
        return repository.findAll();
    }
}
  1. 手動加載:手動執行預熱腳本。
@Component
public class ManualCachePreloader {
    @Autowired
    private CacheManager cacheManager;

    public void preload() {
        Cache cache = cacheManager.getCache("entities");
        if (cache != null) {
            // 獲取需要預熱的數據并加載到緩存
            List<Entity> entities = fetchEntitiesForPreloading();
            for (Entity entity : entities) {
                cache.put(entity.getId(), entity);
            }
        }
    }

    private List<Entity> fetchEntitiesForPreloading() {
        // 獲取需要預熱的數據
        return repository.findAll();
    }
}

推薦方案

綜合考慮各種方案的優缺點,我給大家一種工作中真正常用的方案,也是我待過的互聯網公司中實踐過的方案。

基本策略:刪除Redis中緩存 -> 更新數據庫 -> 最新數據set到Redis

延遲雙刪:刪除Redis中緩存 -> 更新數據庫 -> 休眠500ms -> 再次刪除Redis中緩存 -> 最新數據set到Redis

延遲三刪:刪除Redis中緩存 -> 更新數據庫 -> 休眠500ms -> 再次刪除Redis中緩存 -> 休眠500ms -> 再次刪除Redis中緩存 -> 最新數據set到Redis

這是一種非常簡單且成本很低的操作,但能解決絕大多數的緩存與數據庫不一致問題。

原理很好理解,就是更新數據庫之后設置合理的休眠時間,然后再次刪除掉其他線程請求進來導致的舊緩存,最終達到緩存和數據庫都是最新數據的目的。

其中休眠時間要根據自身業務的平均耗時來決定,而延遲雙刪其實就夠了,延遲三刪只是為了開闊大家的思路,因為真有些公司刪除三次來保證一些極端情況的不一致,但我覺得沒必要,太極端就不是弱一致性了。

如果是比較復雜的項目,甚至能再進一步的優化,也就是借用定時任務和MQ來替代休眠線程,實現異步刪除緩存,達到弱一致性的結果。

總結與思考

緩存與數據庫一致性是一個復雜的問題,需要根據具體業務場景選擇合適的策略。

大家需要記住一點,高可用性是系統的最優先選擇,所以弱一致性就必然成為數據不一致性的最優解,這是一種良性閉環。

因為系統不能用,那是直接虧損,但數據出現低量的不一致,完全可以接受。

責任編輯:武曉燕 來源: Java分享客棧
相關推薦

2024-03-14 10:30:05

緩存場景DEMO

2021-09-29 09:07:22

Docker 日志容器

2020-12-08 10:35:29

程序員IT數據分析

2021-12-27 07:25:13

項目軟件開發

2025-02-24 08:30:00

thisJavaScript函數

2021-05-12 14:10:17

程序員IT互聯網

2023-11-08 08:58:58

GPT-4神經網絡智能

2017-12-12 17:00:20

程序員面試失敗原因

2025-04-09 08:25:00

JavaScript數組解構賦值

2019-07-12 15:28:41

緩存數據庫瀏覽器

2019-07-15 12:40:02

Linux基礎知識程序員

2018-02-06 08:36:02

簡歷程序員面試

2009-05-21 15:58:12

程序員工作經驗職場

2020-09-14 08:47:46

緩存程序員存儲

2012-08-12 23:34:47

回顧

2018-11-22 10:53:30

程序員技能開發者

2013-04-22 09:15:20

2019-08-21 13:40:50

2021-09-23 14:44:24

程序員計算機開發

2018-01-31 22:31:49

大數據程序員編程
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 黑人一级黄色大片 | 中文字幕第一页在线 | 欧美日韩一区在线 | 欧美日韩一二区 | 成人在线中文字幕 | 成人视屏在线观看 | 91精品国产综合久久久动漫日韩 | 日韩视频在线免费观看 | 亚洲精品一区中文字幕乱码 | 国产午夜精品视频 | 99久久影院 | 精品国产乱码久久久久久图片 | 欧美三级三级三级爽爽爽 | h在线免费观看 | 韩日av片 | 国产丝袜av | 色播99| 天天拍天天操 | 成人免费一区二区三区视频网站 | 偷拍自拍在线观看 | 黄网站涩免费蜜桃网站 | 久久夜视频 | 国产一级免费在线观看 | 青青草视频网 | 91亚洲精选 | 欧美一级观看 | 日韩高清一区 | 一区二区三区高清不卡 | 久久久亚洲一区 | 国产激情视频网站 | 欧美黄在线观看 | 影音先锋欧美资源 | 国产高清一二三区 | 五十女人一级毛片 | 精品国产色 | 亚洲国产成人精品女人久久久 | 久久r免费视频 | 在线成人福利 | 日本久久久一区二区三区 | 色婷婷综合久久久中文字幕 | 91av免费观看 |