最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java并發(fā)編程數(shù)據(jù)庫(kù)與緩存數(shù)據(jù)一致性方案解析

 更新時(shí)間:2022年04月02日 16:16:20   作者:Java知識(shí)圖譜  
這篇文章主要為大家介紹了Java并發(fā)編程中數(shù)據(jù)庫(kù)與緩存數(shù)據(jù)一致性解決方案,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步早日升職加薪

一、序言

在分布式并發(fā)系統(tǒng)中,數(shù)據(jù)庫(kù)與緩存數(shù)據(jù)一致性是一項(xiàng)富有挑戰(zhàn)性的技術(shù)難點(diǎn)。本文將討論數(shù)據(jù)庫(kù)與緩存數(shù)據(jù)一致性問(wèn)題,并提供通用的解決方案。

假設(shè)有完善的工業(yè)級(jí)分布式事務(wù)解決方案,那么數(shù)據(jù)庫(kù)與緩存數(shù)據(jù)一致性便迎刃而解,實(shí)際上,目前分布式事務(wù)不成熟。

二、不同的聲音

在數(shù)據(jù)庫(kù)與緩存數(shù)據(jù)一致解決方式中,有各種聲音。

  • 先操作數(shù)據(jù)庫(kù)后緩存還是先緩存后數(shù)據(jù)庫(kù)
  • 緩存是更新還是刪除

1、操作的先后順序

在并發(fā)系統(tǒng)中,數(shù)據(jù)庫(kù)與緩存雙寫(xiě)場(chǎng)景下,為了追求更大的并發(fā)量,操作數(shù)據(jù)庫(kù)與緩存顯而易見(jiàn)不會(huì)同步進(jìn)行。前者操作成功后者以異步的方式進(jìn)行。

關(guān)系型數(shù)據(jù)庫(kù)作為成熟的工業(yè)級(jí)數(shù)據(jù)存儲(chǔ)方案,有完善的事務(wù)處理機(jī)制,數(shù)據(jù)一旦落盤(pán),不考慮硬件故障,可以負(fù)責(zé)任的說(shuō)數(shù)據(jù)不會(huì)丟失。

所謂緩存,無(wú)非是存儲(chǔ)在內(nèi)存中的數(shù)據(jù),服務(wù)一旦重啟,緩存數(shù)據(jù)全部丟失。既然稱(chēng)之為緩存,那么時(shí)刻做好了緩存數(shù)據(jù)丟失的準(zhǔn)備。盡管Redis有持久化機(jī)制,是否能夠保證百分之百持久化?Redis將數(shù)據(jù)異步持久化到磁盤(pán)有不可,緩存是緩存,數(shù)據(jù)庫(kù)是數(shù)據(jù)庫(kù),兩個(gè)不同的東西。把緩存當(dāng)數(shù)據(jù)庫(kù)使用是一件極其危險(xiǎn)的事情。

從數(shù)據(jù)安全的角度來(lái)講,先操作數(shù)據(jù)庫(kù),然后以異步的方式操作緩存,響應(yīng)用戶(hù)請(qǐng)求。

2、處理緩存的態(tài)度

緩存是更新還是刪除,對(duì)應(yīng)懶漢式和飽漢式,從處理線(xiàn)程安全實(shí)踐來(lái)講,刪除緩存操作相對(duì)難度低一些。如果在刪除緩存的前提下滿(mǎn)足了查詢(xún)性能,那么優(yōu)先選擇刪除緩存。

更新緩存盡管能夠提高查詢(xún)效率,然后帶來(lái)的線(xiàn)程并發(fā)臟數(shù)據(jù)處理起來(lái)較麻煩,序言引入MQ等其它消息中間件,因此非必要不推薦。

三、線(xiàn)程并發(fā)分析

理解線(xiàn)程并發(fā)所帶來(lái)問(wèn)題的關(guān)鍵是先理解系統(tǒng)中斷,操作系統(tǒng)在任務(wù)調(diào)度時(shí),中斷隨時(shí)都在發(fā)生,這是線(xiàn)程數(shù)據(jù)不一致產(chǎn)生的根源。以4和8線(xiàn)程CPU為例,同一時(shí)刻最多處理8個(gè)線(xiàn)程,然而操作系統(tǒng)管理的線(xiàn)程遠(yuǎn)遠(yuǎn)超過(guò)8個(gè),因此線(xiàn)程們以一種看似并行的方式進(jìn)行。

查詢(xún)數(shù)據(jù)

1、非并發(fā)環(huán)境

在非并發(fā)環(huán)境中,使用如下方式查詢(xún)數(shù)據(jù)并無(wú)不妥:先查詢(xún)緩存,如果緩存數(shù)據(jù)不存在,查詢(xún)數(shù)據(jù)庫(kù),更新緩存,返回結(jié)果。

public BuOrder getOrder(Long orderId) {
    String key = ORDER_KEY_PREFIX + orderId;
    BuOrder buOrder = RedisUtils.getObject(key, BuOrder.class);
    if (buOrder != null) {
        return buOrder;
    }
    BuOrder order = getById(orderId);
    RedisUtils.setObject(key, order, 5, TimeUnit.MINUTES);
    return order;
}

如果在高并發(fā)環(huán)境中有一個(gè)嚴(yán)重缺陷:當(dāng)緩存失效時(shí),大量查詢(xún)請(qǐng)求涌入,瞬間全部打到DB上,輕則數(shù)據(jù)庫(kù)連接資源耗盡,用戶(hù)端響應(yīng)500錯(cuò)誤,重則數(shù)據(jù)庫(kù)壓力過(guò)大服務(wù)宕機(jī)。

2、并發(fā)環(huán)境

因此在并發(fā)環(huán)境中,需要對(duì)上述代碼進(jìn)行修改,使用分布式鎖。大量請(qǐng)求涌入時(shí),獲得鎖的線(xiàn)程有機(jī)會(huì)訪(fǎng)問(wèn)數(shù)據(jù)庫(kù)查詢(xún)數(shù)據(jù),其余線(xiàn)程阻塞。當(dāng)查詢(xún)完數(shù)據(jù)并更新緩存,然后釋放鎖。等待的線(xiàn)程重新檢查緩存,發(fā)現(xiàn)能夠獲取到數(shù)據(jù),直接將緩存數(shù)據(jù)響應(yīng)。

這里提到分布式鎖,那么使用???????表鎖還是行鎖呢?使用分布式行鎖提高并發(fā)量;使用二次檢查機(jī)制,確保等待獲得鎖的線(xiàn)程能夠快速返回結(jié)果

@Override
public BuOrder getOrder(Long orderId) {
    /* 如果緩存不存在,則添加分布式鎖更新緩存 */
    String key = ORDER_KEY_PREFIX + orderId;
    BuOrder order = RedisUtils.getObject(key, BuOrder.class);
    if (order != null) {
        return order;
    }
    String orderLock = ORDER_LOCK + orderId;
    RLock lock = redissonClient.getLock(orderLock);
    if (lock.tryLock()) {
        order = RedisUtils.getObject(key, BuOrder.class);
        if (order != null) {
            LockOptional.ofNullable(lock).ifLocked(RLock::unlock);
            return order;
        }
        BuOrder buOrder = getById(orderId);
        RedisUtils.setObject(key, buOrder, 5, TimeUnit.MINUTES);
        LockOptional.ofNullable(lock).ifLocked(RLock::unlock);
    }
    return RedisUtils.getObject(key, BuOrder.class);
}

更新數(shù)據(jù)

1、非并發(fā)環(huán)境

非并發(fā)環(huán)境中,如下代碼盡管可能會(huì)產(chǎn)生數(shù)據(jù)不一致問(wèn)題(數(shù)據(jù)被覆蓋)。盡管使用數(shù)據(jù)庫(kù)層面樂(lè)觀鎖能夠解決數(shù)據(jù)被覆蓋問(wèn)題,然而無(wú)效更新流量依舊會(huì)流向數(shù)據(jù)庫(kù)。

public Boolean editOrder(BuOrder order) {
    /* 更新數(shù)據(jù)庫(kù) */
    updateById(order);
    /* 刪除緩存 */
    RedisUtils.deleteObject(OrderServiceImpl.ORDER_KEY_PREFIX + order.getOrderId());
    return true;
}

2、并發(fā)環(huán)境

上面分析中使用數(shù)據(jù)庫(kù)樂(lè)觀鎖能夠解決并發(fā)更新中數(shù)據(jù)被覆蓋的問(wèn)題,然而當(dāng)同一行記錄被修改后,版本號(hào)發(fā)生改變,后續(xù)并發(fā)流向數(shù)據(jù)庫(kù)的請(qǐng)求為無(wú)效流量。減小數(shù)據(jù)庫(kù)壓力的首要策略是將無(wú)效流量攔截在數(shù)據(jù)庫(kù)之前。

使用分布式鎖能夠保證并發(fā)流量有序訪(fǎng)問(wèn)數(shù)據(jù)庫(kù),考慮到數(shù)據(jù)庫(kù)層面已經(jīng)使用了樂(lè)觀鎖,第二個(gè)及以后獲得鎖的線(xiàn)程操作數(shù)據(jù)庫(kù)為無(wú)效流量。

線(xiàn)程在獲得鎖時(shí)采用超時(shí)退出的策略,等待獲得鎖的線(xiàn)程超時(shí)快速退出,快速響應(yīng)用戶(hù)請(qǐng)求,重試更新數(shù)據(jù)操作。

public Boolean editOrder(BuOrder order) {
    String orderLock = ORDER_LOCK + order.getOrderId();
    RLock lock = redissonClient.getLock(orderLock);
    try {
        /* 超時(shí)未獲取到鎖,快速失敗,用戶(hù)端重試 */
        if (lock.tryLock(1, TimeUnit.SECONDS)) {
            /* 更新數(shù)據(jù)庫(kù) */
            updateById(order);
            /* 刪除緩存 */
            RedisUtils.deleteObject(OrderServiceImpl.ORDER_KEY_PREFIX + order.getOrderId());
            /* 釋放鎖 */
            LockOptional.ofNullable(lock).ifLocked(RLock::unlock);
            return true;
        }
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    return false;
}

依賴(lài)環(huán)境

上述代碼使用了封裝鎖的工具類(lèi)。

<dependency>
  <groupId>xin.altitude.cms</groupId>
  <artifactId>ucode-cms-common</artifactId>
  <version>1.4.3.2</version>
</dependency>

LockOptional根據(jù)鎖的狀態(tài)執(zhí)行后續(xù)操作。

四、先數(shù)據(jù)庫(kù)后緩存

數(shù)據(jù)一致性

1、問(wèn)題描述

接下來(lái)討論先更新數(shù)據(jù)庫(kù),后刪除緩存是否存在并發(fā)問(wèn)題。

(1)緩存剛好失效
(2)請(qǐng)求A查詢(xún)數(shù)據(jù)庫(kù),得一個(gè)舊值
(3)請(qǐng)求B將新值寫(xiě)入數(shù)據(jù)庫(kù)
(4)請(qǐng)求B刪除緩存
(5)請(qǐng)求A將查到的舊值寫(xiě)入緩存

上述并發(fā)問(wèn)題出現(xiàn)的關(guān)鍵是第5步比第3、4步后發(fā)生,由操作系統(tǒng)中斷不確定因素可知,此種情況卻有發(fā)生的可能。

2、解決方式

從實(shí)際情況來(lái)看,將數(shù)據(jù)寫(xiě)入Redis遠(yuǎn)比將數(shù)據(jù)寫(xiě)入數(shù)據(jù)庫(kù)耗時(shí)要短,盡管發(fā)生的概率較低,但仍會(huì)發(fā)生。

  • (1)增加緩存過(guò)期時(shí)間

增加緩存過(guò)期時(shí)間允許一定時(shí)間范圍內(nèi)臟數(shù)據(jù)存在,直到下一次并發(fā)更新出現(xiàn),可能會(huì)出現(xiàn)臟數(shù)據(jù)。臟數(shù)據(jù)會(huì)周期性存在。

  • (2)更新和查詢(xún)共用一把行鎖

更新和查詢(xún)共用一把行分布式鎖,上述問(wèn)題不復(fù)存在。當(dāng)讀請(qǐng)求獲取到鎖時(shí),寫(xiě)請(qǐng)求處于阻塞狀態(tài)(超時(shí)會(huì)快速失敗返回),能夠保證步驟5在步驟3之前進(jìn)行。

  • (3)延遲刪除緩存

使用RabbitMQ延遲刪除緩存,去除步驟5的影響。使用異步的方式進(jìn)行,幾乎不影響性能。

特殊情況

數(shù)據(jù)庫(kù)有事務(wù)機(jī)制保證操作成功與否;Redis單條指令具有原子性,然后組合起來(lái)卻不具備原子特征,具體來(lái)說(shuō)是數(shù)據(jù)庫(kù)操作成功,然后應(yīng)用異常掛掉,導(dǎo)致Redis緩存未刪除。Redis服務(wù)網(wǎng)絡(luò)連接超時(shí)出現(xiàn)此問(wèn)題。

如果設(shè)置有緩存過(guò)期時(shí)間,那么在緩存尚未過(guò)期前,臟數(shù)據(jù)一直存在。如果未設(shè)置過(guò)期時(shí)間,那么直到下一次修改數(shù)據(jù)前,臟數(shù)據(jù)一直存在。(數(shù)據(jù)庫(kù)數(shù)據(jù)已經(jīng)發(fā)生改變,緩存尚未更新)

解決方式

在操作數(shù)據(jù)庫(kù)前,向RabbitMQ寫(xiě)入一條延遲刪除緩存的消息,然后執(zhí)行數(shù)據(jù)庫(kù)操作,執(zhí)行緩存刪除操作。不管代碼層面緩存是否刪除成功,MQ刪除緩存作為保底操作。

五、小結(jié)

上述方式提供的數(shù)據(jù)庫(kù)與緩存數(shù)據(jù)一致性解決方式,屬于耦合版,當(dāng)然還有訂閱binlog日志的解耦版。解耦版由于增加了訂閱binlog組件,對(duì)系統(tǒng)穩(wěn)定性提出更高的要求。

數(shù)據(jù)庫(kù)與緩存一致性問(wèn)題看似是解決數(shù)據(jù)問(wèn)題,實(shí)質(zhì)上解決并發(fā)問(wèn)題:在盡可能保證更多并發(fā)量的前提下,在保證數(shù)據(jù)庫(kù)安全的前提下,保證數(shù)據(jù)庫(kù)與緩存數(shù)據(jù)一致。

以上就是數(shù)據(jù)庫(kù)與緩存數(shù)據(jù)一致性方案解析的詳細(xì)內(nèi)容,更多關(guān)于數(shù)據(jù)庫(kù)緩存數(shù)據(jù)一致性的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Java 添加、替換、刪除PDF中的圖片的示例代碼

    Java 添加、替換、刪除PDF中的圖片的示例代碼

    這篇文章主要介紹了Java 添加、替換、刪除PDF中的圖片,本文通過(guò)示例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-02-02
  • MyBatis?在使用上的注意事項(xiàng)及其辨析(最新最全整理)

    MyBatis?在使用上的注意事項(xiàng)及其辨析(最新最全整理)

    這篇文章主要介紹了MyBatis的在使用上的注意事項(xiàng)及其辨析,本文內(nèi)容比較長(zhǎng),是小編用心給大家整理的,圖文實(shí)例代碼相結(jié)合給大家講解的非常詳細(xì),需要的朋友參考下吧
    2024-06-06
  • SpringBoot創(chuàng)建maven多模塊項(xiàng)目實(shí)戰(zhàn)代碼

    SpringBoot創(chuàng)建maven多模塊項(xiàng)目實(shí)戰(zhàn)代碼

    本篇文章主要介紹了SpringBoot創(chuàng)建maven多模塊項(xiàng)目實(shí)戰(zhàn)代碼,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-09-09
  • springcloud 熔斷監(jiān)控Hystrix Dashboard和Turbine

    springcloud 熔斷監(jiān)控Hystrix Dashboard和Turbine

    這篇文章主要介紹了springcloud 熔斷監(jiān)控Hystrix Dashboard和Turbine,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-08-08
  • 如何獲取java類(lèi)中的屬性注釋

    如何獲取java類(lèi)中的屬性注釋

    在開(kāi)發(fā)中,有時(shí)需要獲取Java類(lèi)的屬性注釋,尤其是當(dāng)JPA生成的表缺少注釋時(shí),可以通過(guò)jdk自帶的tools.jar工具包來(lái)實(shí)現(xiàn),方法類(lèi)似于生成javadoc文檔,需要在pom.xml文件中導(dǎo)入tools.jar的依賴(lài),該jar文件一般位于JAVA_HOME/lib目錄下
    2024-09-09
  • Java安全之Mojarra?JSF反序列化講解

    Java安全之Mojarra?JSF反序列化講解

    JSF?和類(lèi)似的?Web?技術(shù)之間的區(qū)別在于?JSF?使用?ViewStates(除了會(huì)話(huà))來(lái)存儲(chǔ)視圖的當(dāng)前狀態(tài)(例如,當(dāng)前應(yīng)該顯示視圖的哪些部分),這篇文章主要介紹了Java安全之Mojarra?JSF反序列化知識(shí)講解,包括漏洞復(fù)現(xiàn)和漏洞分析,需要的朋友可以參考下
    2022-11-11
  • mybatis-plus中的Enum用法實(shí)例

    mybatis-plus中的Enum用法實(shí)例

    本文主要介紹了mybatis-plus中的Enum用法實(shí)例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-01-01
  • 詳解JavaEE中Apollo安裝使用小結(jié)

    詳解JavaEE中Apollo安裝使用小結(jié)

    這篇文章主要介紹了詳解JavaEE中Apollo安裝與使用,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-03-03
  • Java打亂ArrayList生成一個(gè)隨機(jī)序列列表

    Java打亂ArrayList生成一個(gè)隨機(jī)序列列表

    有時(shí)候會(huì)需要將一個(gè)ArrayList或者數(shù)組中的數(shù)字打亂,方便后續(xù)使用,比如隨機(jī)出題、答案選項(xiàng)打亂、連線(xiàn)題打亂、抽獎(jiǎng)號(hào)碼打亂等等,把我自己寫(xiě)的一段代碼貼出來(lái)分享給大家。
    2016-08-08
  • java list去重操作實(shí)現(xiàn)方式

    java list去重操作實(shí)現(xiàn)方式

    Java中的List是可以包含重復(fù)元素的(hash code 和equals),接下來(lái)將介紹兩種方式實(shí)現(xiàn)java list去重操作,感興趣的朋友可以參考下
    2012-12-12

最新評(píng)論

桦甸市| 揭西县| 达州市| 兖州市| 沛县| 禄劝| 衡东县| 邯郸县| 休宁县| 石嘴山市| 卓尼县| 富锦市| 阜南县| 栖霞市| 秦安县| 乐安县| 渭南市| 盈江县| 卢氏县| 云梦县| 江津市| 桐城市| 齐齐哈尔市| 登封市| 昭觉县| 青海省| 呼玛县| 固始县| 明水县| 红河县| 柯坪县| 台北县| 隆子县| 泸西县| 平谷区| 阿坝县| 康保县| 杨浦区| 宜都市| 乌拉特后旗| 清水河县|