當前位置:首頁 » 硬碟大全 » 緩存未命中怎麼辦
擴展閱讀
webinf下怎麼引入js 2023-08-31 21:54:13
堡壘機怎麼打開web 2023-08-31 21:54:11

緩存未命中怎麼辦

發布時間: 2023-02-22 04:36:03

① 什麼叫做cache的寫命中和寫未命中,有什麼區別可以通俗解釋嗎

1、cache的寫命中和寫未命中,就是磁碟或者內存上的存儲區域之前有沒有寫過數據。

如果有,這次再寫到相同的區域叫寫命中;

如果寫到其他區域,叫寫未命中。

2、在數據恢復方面,如果寫命中了,那之前的數據被覆蓋,就很難再恢復回來;

如果寫未命中,那麼之前的數據就容易被找回。

(1)緩存未命中怎麼辦擴展閱讀:

緩存命中率

終端用戶訪問加速節點時,如果該節點有緩存住了要被訪問的數據時就叫做命中,如果沒有的話需要回原伺服器取,就是沒有命中。取數據的過程與用戶訪問是同步進行的,所以即使是重新取的新數據,用戶也不會感覺到有延時。 命中率=命中數/(命中數+沒有命中數), 緩存命中率是判斷加速效果好壞的重要因素之一。

應用場景

是OLTP還是OLAP應用,即使是OLTP,也要看訪問的頻度,一個極少被訪問到的緩存等於沒有什麼效果。一般來說,互聯網網站是非常適合緩存應用的場景。

緩存的粒度

毫無疑問,緩存的粒度越小,命中率就越高,對象緩存是目前緩存粒度最小的,因此被命中的幾率更高。

舉個例子來說吧:你訪問當前這個頁面,瀏覽帖子,那麼對於ORM來說,需要發送n條sql,取各自帖子user的對象。很顯然,如果這個user在其他帖子裡面也跟貼了,那麼在訪問那個帖子的時候,就可以直接從緩存裡面取這個user對象了。

緩存的容量

緩存太小,造成頻繁的LRU,也會降低命中率,緩存的有效期太短也會造成緩存命中率下降。

所以緩存命中率問題不能一概而論,一定說命中率很低或者命中率很高。但是如果你對於緩存的掌握很精通,有意識的去調整應用的架構,去分解緩存的粒度,總是會帶來很高的命中率的。

    ② php 分頁查詢怎麼redis緩存

    對於有分頁條件的緩存,我們也可以按照不同的分頁條件來緩存多個key,比如分頁查詢產品列表,page=1&limit=10和page=1&limit=5這兩次請求可以這樣緩存查詢結果

    proctList:page:1:limit:10

    proctList:page:1:limit:5
    這個是一種常見方案,但是存在著一些問題:

    緩存的value存在冗餘,proctList:page:1:limit:10緩存的內容其實是包括了proctList:page:1:limit:5中的內容(緩存兩個key的時候,數據未發生變化的情況下)

    僅僅是改變了查詢條件的分頁條件,就會導致緩存未命中,降低了緩存的命中率

    為了保證數據一致性,需要清理緩存的時候,很難處理,redis的keys命令對性能影響很大,會導致redis很大的延遲,生產環境一般來說禁止該命令。自己手動拼緩存key,你可能根本不知道拼到哪一個page為止。

    放棄數據一致性,通過設置失效時間來自動失效,可能會出現查詢第一頁命中了緩存,查詢第二頁的時候未命中緩存,但此時數據已經發生了改變,導致第二頁查詢返回的和第一頁相同的結果。
    以上,在分頁條件下這樣使用常規方案總感覺有諸多困擾,諸多麻煩,那是不是就應該放棄使用緩存?
    基於SortedSet的分頁查詢緩存方案
    首先想到的解決方法是使用@see ListOperations<K, V>不再根據分頁條件使用多個key,而是使用一個key,也不分頁將全部的數據緩存到redis中,然後按照分頁條件使用range(key,start,limit)獲取分頁的結果,這個會導致一個問題,當緩存失效時,並發的寫緩存會導致出現重復數據
    所以想到通過使用set來處理並發時的重復數據,@see ZSetOperations<K, V>
    代碼邏輯如下:

    range(key,start,limit)按照分頁條件獲取緩存,命中則直接返回

    緩存未命中,查詢(沒有分頁條件)資料庫或是調用(沒有分頁)底層介面

    add(key,valueScoreMap<value,score>)寫入緩存,expire設置緩存時間

    當需要清理緩存時,直接刪除key,如果是因為數據新增和刪除,可以add(key,value,score)或remove(key,value)

    redis中會按照score分值升序排列map中的數據,一般的,score分值是sql語句的order by filedA的filedA的值,這樣能保證數據一致性
    但是這種方式也存在一定問題:

    這個key緩存的value確實是熱數據,但可能只有少數數據被頻繁使用其餘的可能根本就未被使用,比如數據有100頁,實際可能只會用到前10頁,這也會導致緩存空間的浪費,如果使用了redis虛擬內存,也會有一定影響

    sql查詢由原來的分頁查詢變成了不分頁查詢,緩存失效後,系統的處理能力較之前會有下降,尤其是對於大表.

    ③ 如何保證資料庫緩存的最終一致性

    對於互聯網業務來說,傳統的直接訪問資料庫方式,主要通過數據分片、一主多從等方式來扛住讀寫流量,但隨著數據量的積累和流量的激增,僅依賴資料庫來承接所有流量,不僅成本高、效率低、而且還伴隨著穩定性降低的風險。

    鑒於大部分業務通常是讀多寫少(讀取頻率遠遠高於更新頻率),甚至存在讀操作數量高出寫操作多個數量級的情況。因此, 在架構設計中,常採用增加緩存層來提高系統的響應能力 ,提升數據讀寫性能、減少資料庫訪問壓力,從而提升業務的穩定性和訪問體驗。

    根據 CAP 原理,分布式系統在可用性、一致性和分區容錯性上無法兼得,通常由於分區容錯無法避免,所以一致性和可用性難以同時成立。對於緩存系統來說, 如何保證其數據一致性是一個在應用緩存的同時不得不解決的問題 。

    需要明確的是,緩存系統的數據一致性通常包括持久化層和緩存層的一致性、以及多級緩存之間的一致性,這里我們僅討論前者。持久化層和緩存層的一致性問題也通常被稱為雙寫一致性問題,「雙寫」意為數據既在資料庫中保存一份,也在緩存中保存一份。

    對於一致性來說,包含強一致性和弱一致性 ,強一致性保證寫入後立即可以讀取,弱一致性則不保證立即可以讀取寫入後的值,而是盡可能的保證在經過一定時間後可以讀取到,在弱一致性中應用最為廣泛的模型則是最終一致性模型,即保證在一定時間之後寫入和讀取達到一致的狀態。對於應用緩存的大部分場景來說,追求的則是最終一致性,少部分對數據一致性要求極高的場景則會追求強一致性。

    為了達到最終一致性,針對不同的場景,業界逐步形成了下面這幾種應用緩存的策略。


    1

    Cache-Aside


    Cache-Aside 意為旁路緩存模式,是應用最為廣泛的一種緩存策略。下面的圖示展示了它的讀寫流程,來看看它是如何保證最終一致性的。在讀請求中,首先請求緩存,若緩存命中(cache hit),則直接返回緩存中的數據;若緩存未命中(cache miss),則查詢資料庫並將查詢結果更新至緩存,然後返回查詢出的數據(demand-filled look-aside )。在寫請求中,先更新資料庫,再刪除緩存(write-invalidate)。


    1、為什麼刪除緩存,而不是更新緩存?

    在 Cache-Aside 中,對於讀請求的處理比較容易理解,但在寫請求中,可能會有讀者提出疑問,為什麼要刪除緩存,而不是更新緩存?站在符合直覺的角度來看,更新緩存是一個容易被理解的方案,但站在性能和安全的角度,更新緩存則可能會導致一些不好的後果。

    首先是性能 ,當該緩存對應的結果需要消耗大量的計算過程才能得到時,比如需要訪問多張資料庫表並聯合計算,那麼在寫操作中更新緩存的動作將會是一筆不小的開銷。同時,當寫操作較多時,可能也會存在剛更新的緩存還沒有被讀取到,又再次被更新的情況(這常被稱為緩存擾動),顯然,這樣的更新是白白消耗機器性能的,會導致緩存利用率不高。

    而等到讀請求未命中緩存時再去更新,也符合懶載入的思路,需要時再進行計算。刪除緩存的操作不僅是冪等的,可以在發生異常時重試,而且寫-刪除和讀-更新在語義上更加對稱。

    其次是安全 ,在並發場景下,在寫請求中更新緩存可能會引發數據的不一致問題。參考下面的圖示,若存在兩個來自不同線程的寫請求,首先來自線程 1 的寫請求更新了資料庫(step 1),接著來自線程 2 的寫請求再次更新了資料庫(step 3),但由於網路延遲等原因,線程 1 可能會晚於線程 2 更新緩存(step 4 晚於 step 3),那麼這樣便會導致最終寫入資料庫的結果是來自線程 2 的新值,寫入緩存的結果是來自線程 1 的舊值,即緩存落後於資料庫,此時再有讀請求命中緩存(step 5),讀取到的便是舊值。


    2、為什麼先更新資料庫,而不是先刪除緩存?

    另外,有讀者也會對更新資料庫和刪除緩存的時序產生疑問,那麼為什麼不先刪除緩存,再更新資料庫呢?在單線程下,這種方案看似具有一定合理性,這種合理性體現在刪除緩存成功。

    但更新資料庫失敗的場景下,盡管緩存被刪除了,下次讀操作時,仍能將正確的數據寫回緩存,相對於 Cache-Aside 中更新資料庫成功,刪除緩存失敗的場景來說,先刪除緩存的方案似乎更合理一些。那麼,先刪除緩存有什麼問題呢?

    問題仍然出現在並發場景下,首先來自線程 1 的寫請求刪除了緩存(step 1),接著來自線程 2 的讀請求由於緩存的刪除導致緩存未命中,根據 Cache-Aside 模式,線程 2 繼而查詢資料庫(step 2),但由於寫請求通常慢於讀請求,線程 1 更新資料庫的操作可能會晚於線程 2 查詢資料庫後更新緩存的操作(step 4 晚於 step 3),那麼這樣便會導致最終寫入緩存的結果是來自線程 2 中查詢到的舊值,而寫入資料庫的結果是來自線程 1 的新值,即緩存落後於資料庫,此時再有讀請求命中緩存( step 5 ),讀取到的便是舊值。


    另外,先刪除緩存,由於緩存中數據缺失,加劇資料庫的請求壓力,可能會增大緩存穿透出現的概率。

    3、如果選擇先刪除緩存,再更新資料庫,那如何解決一致性問題呢?

    為了避免「先刪除緩存,再更新資料庫」這一方案在讀寫並發時可能帶來的緩存臟數據,業界又提出了延時雙刪的策略,即在更新資料庫之後,延遲一段時間再次刪除緩存,為了保證第二次刪除緩存的時間點在讀請求更新緩存之後,這個延遲時間的經驗值通常應稍大於業務中讀請求的耗時。

    延遲的實現可以在代碼中 sleep 或採用延遲隊列。顯而易見的是,無論這個值如何預估,都很難和讀請求的完成時間點准確銜接,這也是延時雙刪被詬病的主要原因。


    4、那麼 Cache-Aside 存在數據不一致的可能嗎?

    在 Cache-Aside 中,也存在數據不一致的可能性。在下面的讀寫並發場景下,首先來自線程 1 的讀請求在未命中緩存的情況下查詢資料庫(step 1),接著來自線程 2 的寫請求更新資料庫(step 2),但由於一些極端原因,線程 1 中讀請求的更新緩存操作晚於線程 2 中寫請求的刪除緩存的操作(step 4 晚於 step 3),那麼這樣便會導致最終寫入緩存中的是來自線程 1 的舊值,而寫入資料庫中的是來自線程 2 的新值,即緩存落後於資料庫,此時再有讀請求命中緩存(step 5),讀取到的便是舊值。

    這種場景的出現,不僅需要緩存失效且讀寫並發執行,而且還需要讀請求查詢資料庫的執行早於寫請求更新資料庫,同時讀請求的執行完成晚於寫請求。足以見得,這種 不一致場景產生的條件非常嚴格,在實際的生產中出現的可能性較小 。


    除此之外,在並發環境下,Cache-Aside 中也存在讀請求命中緩存的時間點在寫請求更新資料庫之後,刪除緩存之前,這樣也會導致讀請求查詢到的緩存落後於資料庫的情況。


    雖然在下一次讀請求中,緩存會被更新,但如果業務層面對這種情況的容忍度較低,那麼可以採用加鎖在寫請求中保證「更新資料庫&刪除緩存」的串列執行為原子性操作(同理也可對讀請求中緩存的更新加鎖)。 加鎖勢必會導致吞吐量的下降,故採取加鎖的方案應該對性能的損耗有所預期。


    2

    補償機制


    我們在上面提到了,在 Cache-Aside 中可能存在更新資料庫成功,但刪除緩存失敗的場景,如果發生這種情況,那麼便會導致緩存中的數據落後於資料庫,產生數據的不一致的問題。

    其實,不僅 Cache-Aside 存在這樣的問題,在延時雙刪等策略中也存在這樣的問題。針對可能出現的刪除失敗問題,目前業界主要有以下幾種補償機制。

    1、刪除重試機制

    由於同步重試刪除在性能上會影響吞吐量,所以常通過引入消息隊列,將刪除失敗的緩存對應的 key 放入消息隊列中,在對應的消費者中獲取刪除失敗的 key ,非同步重試刪除。這種方法在實現上相對簡單,但由於刪除失敗後的邏輯需要基於業務代碼的 trigger 來觸發 ,對業務代碼具有一定入侵性。


    鑒於上述方案對業務代碼具有一定入侵性,所以需要一種更加優雅的解決方案,讓緩存刪除失敗的補償機制運行在背後,盡量少的耦合於業務代碼。一個簡單的思路是通過後台任務使用更新時間戳或者版本作為對比獲取資料庫的增量數據更新至緩存中,這種方式在小規模數據的場景可以起到一定作用,但其擴展性、穩定性都有所欠缺。

    一個相對成熟的方案是基於 MySQL 資料庫增量日誌進行解析和消費,這里較為流行的是阿里巴巴開源的作為 MySQL binlog 增量獲取和解析的組件 canal(類似的開源組件還有 Maxwell、Databus 等)。

    canal sever 模擬 MySQL slave 的交互協議,偽裝為 MySQL slave,向 MySQL master 發送 mp 協議,MySQL master 收到 mp 請求,開始推送 binary log 給 slave (即 canal sever ),canal sever 解析 binary log 對象(原始為 byte 流),可由 canal client 拉取進行消費,同時 canal server 也默認支持將變更記錄投遞到 MQ 系統中,主動推送給其他系統進行消費。

    在 ack 機制的加持下,不管是推送還是拉取,都可以有效的保證數據按照預期被消費。當前版本的 canal 支持的 MQ 有 Kafka 或者 RocketMQ。另外, canal 依賴 ZooKeeper 作為分布式協調組件來實現 HA ,canal 的 HA 分為兩個部分:


    那麼,針對緩存的刪除操作便可以在 canal client 或 consumer 中編寫相關業務代碼來完成。這樣,結合資料庫日誌增量解析消費的方案以及 Cache-Aside 模型,在讀請求中未命中緩存時更新緩存(通常這里會涉及到復雜的業務邏輯),在寫請求更新資料庫後刪除緩存,並基於日誌增量解析來補償資料庫更新時可能的緩存刪除失敗問題,在絕大多數場景下,可以有效的保證緩存的最終一致性。

    另外需要注意的是,還應該隔離事務與緩存,確保資料庫入庫後再進行緩存的刪除操作。 比如考慮到資料庫的主從架構,主從同步及讀從寫主的場景下,可能會造成讀取到從庫的舊數據後便更新了緩存,導致緩存落後於資料庫的問題,這就要求對緩存的刪除應該確保在資料庫操作完成之後。所以,基於 binlog 增量日誌進行數據同步的方案,可以通過選擇解析從節點的 binlog,來避免主從同步下刪除緩存過早的問題。

    3、數據傳輸服務 DTS


    3

    Read-Through


    Read-Through 意為讀穿透模式,它的流程和 Cache-Aside 類似,不同點在於 Read-Through 中多了一個訪問控制層,讀請求只和該訪問控制層進行交互,而背後緩存命中與否的邏輯則由訪問控制層與數據源進行交互,業務層的實現會更加簡潔,並且對於緩存層及持久化層交互的封裝程度更高,更易於移植。


    4

    Write-Through


    Write-Through 意為直寫模式,對於 Write-Through 直寫模式來說,它也增加了訪問控制層來提供更高程度的封裝。不同於 Cache-Aside 的是,Write-Through 直寫模式在寫請求更新資料庫之後,並不會刪除緩存,而是更新緩存。


    這種方式的 優勢在於讀請求過程簡單 ,不需要查詢資料庫更新緩存等操作。但其劣勢也非常明顯,除了上面我們提到的更新資料庫再更新緩存的弊端之外,這種方案還會造成更新效率低,並且兩個寫操作任何一次寫失敗都會造成數據不一致。

    如果要使用這種方案, 最好可以將這兩個操作作為事務處理,可以同時失敗或者同時成功,支持回滾,並且防止並發環境下的不一致 。另外,為了防止緩存擾動的頻發,也可以給緩存增加 TTL 來緩解。

    站在可行性的角度,不管是 Write-Through 模式還是 Cache-Aside 模式,理想狀況下都可以通過分布式事務保證緩存層數據與持久化層數據的一致性,但在實際項目中,大多都對一致性的要求存在一些寬容度,所以在方案上往往有所折衷。

    Write-Through 直寫模式適合寫操作較多,並且對一致性要求較高的場景,在應用 Write-Through 模式時,也需要通過一定的補償機制來解決它的問題。首先,在並發環境下,我們前面提到了先更新資料庫,再更新緩存會導致緩存和資料庫的不一致,那麼先更新緩存,再更新資料庫呢?

    這樣的操作時序仍然會導致下面這樣線程 1 先更新緩存,最後更新資料庫的情況,即由於線程 1 和 線程 2 的執行不確定性導致資料庫和緩存的不一致。這種由於線程競爭導致的緩存不一致,可以通過分布式鎖解決,保證對緩存和資料庫的操作僅能由同一個線程完成。對於沒有拿到鎖的線程,一是通過鎖的 timeout 時間進行控制,二是將請求暫存在消息隊列中順序消費。


    在下面這種並發執行場景下,來自線程 1 的寫請求更新了資料庫,接著來自線程 2 的讀請求命中緩存,接著線程 1 才更新緩存,這樣便會導致線程 2 讀取到的緩存落後於資料庫。同理,先更新緩存後更新資料庫在寫請求和讀請求並發時,也會出現類似的問題。面對這種場景,我們也可以加鎖解決。


    另在,在 Write-Through 模式下,不管是先更新緩存還是先更新資料庫,都存在更新緩存或者更新資料庫失敗的情況,上面提到的重試機制和補償機制在這里也是奏效的。


    5

    Write-Behind


    Write behind 意為非同步回寫模式,它也具有類似 Read-Through/Write-Through 的訪問控制層,不同的是,Write behind 在處理寫請求時,只更新緩存而不更新資料庫,對於資料庫的更新,則是通過批量非同步更新的方式進行的,批量寫入的時間點可以選在資料庫負載較低的時間進行。

    在 Write-Behind 模式下,寫請求延遲較低,減輕了資料庫的壓力,具有較好的吞吐性。但資料庫和緩存的一致性較弱,比如當更新的數據還未被寫入資料庫時,直接從資料庫中查詢數據是落後於緩存的。同時,緩存的負載較大,如果緩存宕機會導致數據丟失,所以需要做好緩存的高可用。顯然,Write behind 模式下適合大量寫操作的場景,常用於電商秒殺場景中庫存的扣減。


    6

    Write-Around


    如果一些非核心業務,對一致性的要求較弱,可以選擇在 cache aside 讀模式下增加一個緩存過期時間,在寫請求中僅僅更新資料庫,不做任何刪除或更新緩存的操作,這樣,緩存僅能通過過期時間失效。這種方案實現簡單,但緩存中的數據和資料庫數據一致性較差,往往會造成用戶的體驗較差,應慎重選擇。


    7

    總結


    在解決緩存一致性的過程中,有多種途徑可以保證緩存的最終一致性,應該根據場景來設計合適的方案,讀多寫少的場景下,可以選擇採用「Cache-Aside 結合消費資料庫日誌做補償」的方案,寫多的場景下,可以選擇採用「Write-Through 結合分布式鎖」的方案 ,寫多的極端場景下,可以選擇採用「Write-Behind」的方案。

    ④ 三級緩存是為讀取二級緩存後未命中的數據設計的—種緩存

    首先你要明白緩存的定義,
    緩存的英文是cache,原本是儲藏、儲藏所、儲藏物的意思。
    在計算機科學領域,緩存指的是一組數據的集合,這些數據來自於儲存在其他地方或先前計算的結果,而獲取或運算出這些數據的代價非常昂貴,為此,把這些結果數據保存起來,讓下一次需要這些數據的時候直接使用,而不用重新獲取或計算,這就大大提高了系統效率。
    CPU緩存(Cache Memory)位於CPU與內存之間的臨時存儲器,它的容量比內存小但交換速度快。在緩存中的數據是內存中的一小部分,但這一小部分是短時間內CPU即將訪問的,當CPU調用大量數據時,就可避開內存直接從緩存中調用,從而加快讀取速度。由此可見,在CPU中加入緩存是一種高效的解決方案,這樣整個內存儲器(緩存+內存)就變成了既有緩存的高速度,又有內存的大容量的存儲系統了。緩存對CPU的性能影響很大,主要是因為CPU的數據交換順序和CPU與緩存間的帶寬引起的。
    緩存是為了解決CPU速度和內存速度的速度差異問題。內存中被CPU訪問最頻繁的數據和指令被復制入CPU中的緩存,這樣CPU就可以不經常到象「蝸牛」一樣慢的內存中去取數據了,CPU只要到緩存中去取就行了,而緩存的速度要比內存快很多。
    這里要特別指出的是:
    1.因為緩存只是內存中少部分數據的復製品,所以CPU到緩存中尋找數據時,也會出現找不到的情況(因為這些數據沒有從內存復制到緩存中去),這時CPU還是會到內存中去找數據,這樣系統的速度就慢下來了,不過CPU會把這些數據復制到緩存中去,以便下一次不要再到內存中去取。
    2.因為隨著時間的變化,被訪問得最頻繁的數據不是一成不變的,也就是說,剛才還不頻繁的數據,此時已經需要被頻繁的訪問,剛才還是最頻繁的數據,現在又不頻繁了,所以說緩存中的數據要經常按照一定的演算法來更換,這樣才能保證緩存中的數據是被訪問最頻繁的。
    緩存的工作原理是當CPU要讀取一個數據時,首先從緩存中查找,如果找到就立即讀取並送給CPU處理;如果沒有找到,就用相對慢的速度從內存中讀取並送給CPU處理,同時把這個數據所在的數據塊調入緩存中,可以使得以後對整塊數據的讀取都從緩存中進行,不必再調用內存。
    正是這樣的讀取機制使CPU讀取緩存的命中率非常高(大多數CPU可達90%左右),也就是說CPU下一次要讀取的數據90%都在緩存中,只有大約10%需要從內存讀取。這大大節省了CPU直接讀取內存的時間,也使CPU讀取數據時基本無需等待。總的來說,CPU讀取數據的順序是先緩存後內存。
    一級緩存和二級緩存
    為了分清這兩個概念,我們先了解一下RAM 。RAM和ROM相對的,RAM是掉電以後,其中的信息就消失那一種,ROM在掉電以後信息也不會消失那一種。
    RAM又分兩種,一種是靜態RAM,SRAM;一種是動態RAM,DRAM。前者的存儲速度要比後者快得多,我們現在使用的內存一般都是動態RAM。
    有的菜鳥就說了,為了增加系統的速度,把緩存擴大不就行了嗎,擴大的越大,緩存的數據越多,系統不就越快了嗎?緩存通常都是靜態RAM,速度是非常的快, 但是靜態RAM集成度低(存儲相同的數據,靜態RAM的體積是動態RAM的6倍), 價格高(同容量的靜態RAM是動態RAM的四倍), 由此可見,擴大靜態RAM作為緩存是一個非常愚蠢的行為, 但是為了提高系統的性能和速度,我們必須要擴大緩存, 這樣就有了一個折中的方法,不擴大原來的靜態RAM緩存,而是增加一些高速動態RAM做為緩存, 這些高速動態RAM速度要比常規動態RAM快,但比原來的靜態RAM緩存慢, 我們把原來的靜態ram緩存叫一級緩存,而把後來增加的動態RAM叫二級緩存。
    一級緩存和二級緩存中的內容都是內存中訪問頻率高的數據的復製品(映射),它們的存在都是為了減少高速CPU對慢速內存的訪問。 通常CPU找數據或指令的順序是:先到一級緩存中找,找不到再到二級緩存中找,如果還找不到就只有到內存中找了。

    ⑤ 請問「cpu三級緩存是為讀取二級緩存後未命中的數據設計的一種緩存」中未命中是指什麼具體情況,沒看懂

    原本CPU需要的指令和數據都要從內存讀取

    但內存太慢,系統就緩存常用的內容到更高速的緩存中

    命中表示所需要的指令/數據在緩存中找到了

    未命中表示所需要的指令/數據不在緩存中,此時需要在下一級緩存中查找,如果都不命中,就只有在內存中查找了

    ⑥ 如何根據代碼判斷處理器緩存未命中率

    valgrind在C語言編程中,對程序的性能調試,判斷一個程序的代碼質量是否高效有很明顯的用處。下面具體分析一下:
    測試程序的CPU命中率(CPU cache hit/miss rate):
    模擬寫兩個小程序,用來測試CPU的緩存命中率,分別名為cache1.c和cache2.c.

    cache1.c代碼如下:
    點擊(此處)折疊或打開
    #include <stdio.h>

    #define MAXROW 8000
    #define MAXCOL 8000

    int main () {

    int i,j;

    static int x[MAXR www.hbbz08.com OW][MAXCOL];

    printf ("Starting!\n");

    for (i=0;i<MAXROW;i++)

    for (j=0;j<MAXCOL;j++)

    x[i][j] = i*j;

    printf("Completed!\n");

    return 0;

    }
    cache2.c代碼如下:

    點擊(此處)折疊或打開
    #include <stdio.h>

    #define MAXROW 8000
    #define MAXCOL 8000

    int main () {

    int i,j;

    static int x[MAXROW][MAXCOL];

    printf ("Starting!\n");

    for (j=0;j<MAXCOL;j++)

    for (i=0;i<MAXROW;i++)

    x[i][j] = i*j;

    printf("Completed!\n");

    return 0;

    }

    ⑦ 什麼是三級緩存

    這就要先了解什麼是CPU緩存。簡單來說,緩存就是介於CPU核心的寄存器和內存之間的緩沖存儲結構。

    CPU的執行單元中有寄存器用於計算,由於是計算使用的,所以不能用於海量存儲執行數據,只能是計算時將數據調入,計算完了就輸出並清除,准備進行下一次計算。所以寄存器速度雖然快,但是卻不能用於長期存儲數據。而內存則是CPU堆放臨時計算數據的地方(如果需要永久存儲則會被放到速度更慢但是容量更大的硬碟里)。但是內存是動態存儲器,由於需要刷新動作,雖然容量相對大但是是片外定址,訪問速度比CPU慢。這就需要在CPU寄存器和內存之間建立緩存。緩存為靜態存儲器。集成度低(緩存會佔去CPU上十分可觀的一塊面積)但速度極高。雖然相比內存來說容量很小,但是速度快了很多。拋開早期的外置結構不談,目前的CPU緩存都與CPU在同一塊晶元上。因此定址速度也很快。CPU執行程序時先從緩存找數據,遍歷緩存後沒有找到需要的數據稱為沒有「命中」。如果緩存未命中,則CPU會轉向內存尋找所需數據。

    好了,知道啥是緩存了,那緩存為啥要分一二三級呢?

    因為緩存速度雖然快,但是CPU效率十分高。對緩存的速度還是有要求。而如果緩存越大,遍歷一遍尋找數據的速度也就越慢。因此需要對緩存內的數據進行有效的管理。不是最常用的數據就要踢出緩存給最常用的數據騰出空間。這就是緩存分級的原理。緩存的大小需要適度,否則會影響遍歷速度。將近幾個時鍾中最常用的數據保存在一級緩存能大幅提升執行效率。

    不過這只是理論,是犧牲者緩存的一般原理。目前intel處理器採用的是一級緩存是二級緩存的索引目錄,二級緩存內存實際數據。這樣僅需遍歷很小的一級緩存就能知道較大的二級緩存內都保存了哪些數據,是否是CPU需要的。如果一級緩存命中,直接按照地址去二級緩存找。如果一級緩存未命中,也不用遍歷二級緩存了,直接去下一級緩存或者內存中尋找了。

    了解以上內容我們就可以了解二級緩存和三級緩存的差異了。正入上面所說,類似於intel的一二級緩存結構僅能服務CPU的一個核心。每個CPU核心都有獨立的一級緩存和二級緩存結構。但是CPU內的各個核心有時需要協同工作,這在科學計算等應用中十分常見。這就需要各個核心共享一部分數據。顯然私有的一二級緩存不能成為交流媒介。而跑去內存交換效率實在太低了。所以一個各個核心能共享使用的三級緩存就應運而生了。也就是說三級緩存實際上是CPU的各個核心共享的公共緩存結構。

    ⑧ 電腦中的二級緩存有什麼用它跟電腦內存的功能各是什麼對電腦的性能有什麼影響

    更大的二級緩存以及前端匯流排帶寬使得電腦具備了更好的游戲性能。
    在游戲中,L2使得一些圖形晶元處理國家流暢,尤其是3D浮點運算能力的增加,最好的例子就是DOOM3的彈道預測!

    更多:
    說到CPU,不得不說的就是CPU緩存,目前CPU的緩存已經成了衡量CPU性能的一個必要指標,那麼CPU緩存到底對CPU性能的影響有多大呢?

    我們知道,CPU執行指令時,會將執行結果放在一個叫「寄存器」的元件中,由於「寄存器」集成在CPU內部,與ALU等構成CPU的重要元件,因此寄存器中的指令很快被CPU所訪問,但畢竟寄存器的容量太小,CPU所需的大量指令和數據還在內存(RAM)當中,所以CPU為了完成指令操作,需要頻繁地向內存發送接收指令、數據。

    由於內存的處理速度遠遠低於CPU,所以傳統的系統瓶頸在這里就產生了,CPU在處理指令時往往花費很多時間在等待內存做准備工作。

    為了解決這個問題,人們在CPU內集成了一個比內存快許多的「Cache」,這就是最早的「高速緩存」。

    L1高速緩存是與CPU完全同步運行的存儲器,也就是我們常說的一級緩存,如果CPU需要的數據和指令已經在高速緩存中了,那麼CPU不必等待,直接就可以從一級緩存(L1)中取得數據,如果數據不在L1中,CPU再從二級緩存(L2)中提取數據,大大提高了系統的工作效率。

    趣談CPU緩存工作原理

    沒有CPU緩存前

    我們可以形象地把CPU的運算單元想像成是一間坐落在城市中心的工廠,把內存看成是工廠設置在郊區的一間面積很大的倉庫A。

    工廠生產所需要的原材料每次都要花時間去遠處的倉庫A調運,而且到達倉庫後,還要等待倉庫准備好材料,中間浪費了不少時間。這就是CPU頻率未變的情況下,CPU與內存的數據交換不同步的現象。

    而突然有一天,由於資金短缺,倉庫A從近郊區「搬到」了遠郊區,這樣原料和成品在工廠與倉庫A之間的運輸所花費的時間就更長了,工廠生產所需的原料供應不足,經常處於空運轉的狀態下。這就是說當CPU頻率增加後,CPU與內存交換數據等待需時間會變得更長。

    增加L1Cache

    要解決CPU與內存交換數據不同步這個系統瓶頸問題,其中一個辦法是在靠近工廠的市區設置一個小型的倉庫B(L1Cache)。

    平時把生產最迫切需要、用得最多的原材料(指令和數據)從倉庫A(內存)調配到倉庫B(L1Cache),這樣工廠生產所需要的原材料就可以很快地調配過來,減少空運轉的時間。當所需的原材料在倉庫B中找不到(緩存未命中)時,仍然要到倉庫A(內存)里調配,雖然無可避免地使工廠又進入空運轉,或部分空運轉(CPU等待若干個時鍾周期),但這樣畢竟使等待時間大大降低了。

    小知識:緩存有一個「預讀」功能,也就是可以通過一定的演算法,猜測接下來所要的數據,並預先取入緩存。

    再添L2Cache

    隨著CPU的頻率提高,與內存之間交換數據不同步的現象更明顯了,可以理解為倉庫A(內存)搬離郊區,遷到更遠的地方了。解決這一問題的一個更好的辦法就是在城市的邊緣再設立一個比倉庫B大的倉庫C,也就是我們說的二級緩存。

    它的作用是把郊區之外的倉庫A(內存)中最迫切用的材料(指令)運到倉庫C,而工廠如果在倉庫B中找不到所需的材料,就可以到倉庫C中找,而不必老遠跑到倉庫A那裡找,節省了不少時間。

    通常情況下,L2包括L1所有的數據,另外還有一些附加的數據。換言之,L1與L2、L2與內存之間是子母關系,所以CPU緩存的出現更有效地解決了CPU空等待所造成的資源浪費問題。

    CPU緩存越大越好?

    當然,CPU緩存並不是越大越好,因為緩存採用的是速度快、價格昂貴的靜態RAM(SRAM),由於每個SRAM內存單元都是由4~6個晶體管構成,增加緩存會帶來CPU集成晶體管個數大增,發熱量也隨之增大,給設計製造帶來很大的難度。所以就算緩存容量做得很大,但如果設計不合理會造成緩存的延時,CPU的性能也未必得到提高

    CPU緩存(Cache Memory)位於CPU與內存之間的臨時存儲器,它的容量比內存小但交換速度快。在緩存中的數據是內存中的一小部分,但這一小部分是短時間內CPU即將訪問的,當CPU調用大量數據時,就可避開內存直接從緩存中調用,從而加快讀取速度。由此可見,在CPU中加入緩存是一種高效的解決方案,這樣整個內存儲器(緩存+內存)就變成了既有緩存的高速度,又有內存的大容量的存儲系統了。緩存對CPU的性能影響很大,主要是因為CPU的數據交換順序和CPU與緩存間的帶寬引起的。

    緩存的工作原理是當CPU要讀取一個數據時,首先從緩存中查找,如果找到就立即讀取並送給CPU處理;如果沒有找到,就用相對慢的速度從內存中讀取並送給CPU處理,同時把這個數據所在的數據塊調入緩存中,可以使得以後對整塊數據的讀取都從緩存中進行,不必再調用內存。

    正是這樣的讀取機制使CPU讀取緩存的命中率非常高(大多數CPU可達90%左右),也就是說CPU下一次要讀取的數據90%都在緩存中,只有大約10%需要從內存讀取。這大大節省了CPU直接讀取內存的時間,也使CPU讀取數據時基本無需等待。總的來說,CPU讀取數據的順序是先緩存後內存。

    最早先的CPU緩存是個整體的,而且容量很低,英特爾公司從Pentium時代開始把緩存進行了分類。當時集成在CPU內核中的緩存已不足以滿足CPU的需求,而製造工藝上的限制又不能大幅度提高緩存的容量。因此出現了集成在與CPU同一塊電路板上或主板上的緩存,此時就把 CPU內核集成的緩存稱為一級緩存,而外部的稱為二級緩存。一級緩存中還分數據緩存(Data Cache,D-Cache)和指令緩存(Instruction Cache,I-Cache)。二者分別用來存放數據和執行這些數據的指令,而且兩者可以同時被CPU訪問,減少了爭用Cache所造成的沖突,提高了處理器效能。英特爾公司在推出Pentium 4處理器時,用新增的一種一級追蹤緩存替代指令緩存,容量為12KμOps,表示能存儲12K條微指令。

    隨著CPU製造工藝的發展,二級緩存也能輕易的集成在CPU內核中,容量也在逐年提升。現在再用集成在CPU內部與否來定義一、二級緩存,已不確切。而且隨著二級緩存被集成入CPU內核中,以往二級緩存與CPU大差距分頻的情況也被改變,此時其以相同於主頻的速度工作,可以為CPU提供更高的傳輸速度。

    二級緩存是CPU性能表現的關鍵之一,在CPU核心不變化的情況下,增加二級緩存容量能使性能大幅度提高。而同一核心的CPU高低端之分往往也是在二級緩存上有差異,由此可見二級緩存對於CPU的重要性。

    CPU在緩存中找到有用的數據被稱為命中,當緩存中沒有CPU所需的數據時(這時稱為未命中),CPU才訪問內存。從理論上講,在一顆擁有二級緩存的CPU中,讀取一級緩存的命中率為80%。也就是說CPU一級緩存中找到的有用數據占數據總量的80%,剩下的20%從二級緩存中讀取。由於不能准確預測將要執行的數據,讀取二級緩存的命中率也在80%左右(從二級緩存讀到有用的數據占總數據的16%)。那麼還有的數據就不得不從內存調用,但這已經是一個相當小的比例了。目前的較高端的CPU中,還會帶有三級緩存,它是為讀取二級緩存後未命中的數據設計的—種緩存,在擁有三級緩存的CPU中,只有約5%的數據需要從內存中調用,這進一步提高了CPU的效率。

    為了保證CPU訪問時有較高的命中率,緩存中的內容應該按一定的演算法替換。一種較常用的演算法是「最近最少使用演算法」(LRU演算法),它是將最近一段時間內最少被訪問過的行淘汰出局。因此需要為每行設置一個計數器,LRU演算法是把命中行的計數器清零,其他各行計數器加1。當需要替換時淘汰行計數器計數值最大的數據行出局。這是一種高效、科學的演算法,其計數器清零過程可以把一些頻繁調用後再不需要的數據淘汰出緩存,提高緩存的利用率。

    CPU產品中,一級緩存的容量基本在4KB到64KB之間,二級緩存的容量則分為128KB、256KB、512KB、1MB、2MB等。一級緩存容量各產品之間相差不大,而二級緩存容量則是提高CPU性能的關鍵。二級緩存容量的提升是由CPU製造工藝所決定的,容量增大必然導致CPU內部晶體管數的增加,要在有限的CPU面積上集成更大的緩存,對製造工藝的要求也就越高

    ⑨ 計算思維中講未來可能會被用到的數據存放在高效存儲區域中的方式是

    在計算機科學中,緩存是一種高效存儲區域,用於存儲可能被頻繁使用的數據。緩存的作用是通過減少對主存儲器(如硬碟或內存)的訪問來提高系統性能。
    在計算機系統中,通常有多級緩存系統,其中每一級都比上一級更快,但也更小。例如,CPU可能會有一個內置的緩存,並且內存模塊也可能會有一個緩存。
    為了在高效存儲區域(如緩存)中存儲未來可能會被用到的數據,可以使用一種稱為"緩存命中"的機制。緩存命中是指當程序試圖訪問一個數據項時,緩存會先檢查它是否已經存儲在緩存中。如果是,則稱為"緩存命中",並且程序會從緩存中直接獲取數據。如果數據不在緩存中,則稱為"緩存未命中",並且程序會從主存儲器中獲取數據。
    緩存命中的一般策略是將常用的數據存儲在緩存中,並將不常用的數據替換出緩存。這樣,當程序需要訪問常用的數據時,就可以從緩存中快速獲取數據,

    ⑩ EMSI協議

    翻譯至 https://en.wikipedia.org/wiki/MESI_protocol
    EMSI是基於緩存無效化的一致性緩存協議,並且是一種最常見的支持回寫式緩存的協議。它也被稱為伊利諾伊州協議(由於其在伊利諾伊大學厄本那香檳分校被開發)。回寫式緩存和寫入式緩存相比可以節約很多的帶寬。回寫式緩存經常會存在臟狀態,而臟狀態表明了高數緩存中的數據與主存中的數據不一致。EMSI要求當緩存未命中時並且別的緩存中有該數據,那麼緩存和緩存間應該互相傳輸數據。MESI相對與MSI來說降低了與主存的交互次數,這帶來了顯著的性能提升。

    MESI的四個字母分別代表了四個可以被標記在緩存行上的獨立狀態。(也就是用2bit來編碼)

    當緩存行處於Modified狀態時,它表明該緩存行只存在於當前緩存中。並且這個緩存行中的數據是臟數據,也就是說這個緩存行中的數據與主存中的數據不一致。緩存被要求在未來將緩存行的數據寫於主存中,但不用立即寫入。但如果別的緩存向該緩存請求這個數據,那必須保證該數據寫入主存已經完成。當回寫回主存完成後,緩存行狀態會有Modified變為Shared狀態。

    當緩存行處於Exclusive狀態時,它表明該緩存行只存在於當前緩存中,不過其中的數據與主存中的數據是一致的。當別的緩存向該緩存請求read當前緩存行時,緩存行狀態會變成Shared。或者當有write操作時,他會變成Modified狀態。

    當緩存行處於Shared狀態時,它表明該緩存行可能同時存在與別的緩存中,並且其中的數據與主存中一致。這個緩存行隨時可能被丟棄(改變為Invalid狀態)。

    當緩存行處於Invalid 狀態時,表明該緩存行是無效的。

    對於給定的兩個緩存,以下是允許共同存在的狀態:

    當一個緩存中的變數被標記為M狀態,那同樣擁有這個變數的別的緩存的緩存行會被標記為Invalid。

    從一個狀態到另一個狀態的轉變有兩個重要影響因素。第一個因素是處理器發出了特殊的讀寫請求。舉個栗子:處理器A的緩存中有變數X,然後這個處理器向自己的緩存發送了對於這個變數的讀寫請求。第二個影響因素是來自別的處理器的請求,這些處理器緩存中沒有這個變數,或者它們想要更新這個變數。這些匯流排請求被一個名叫Snoopers的監聽器監聽著。

    下面解釋不同種類的處理器請求和匯流排請求

    處理器請求包括如下兩種:

    匯流排請求包括如下五種:

    如果從主存中獲取一個值需要等待的時間比通過緩存間傳值等待的時間長,那麼我們就可以說緩存間傳值可以降低緩存未命中後的讀延遲。在多核架構下,一致性主要在二級緩存間保持,但處理器仍然有三級緩存,從三級緩存中獲取未命中的變數往往快於從二級緩存。

    監控操作
    所有緩存中的變數都擁有四種狀態的有限狀態機。
    對於不同輸入的狀態轉換關系和回復如下表1.1和1.2

    只有寫操作發生在Modified或Exclusive狀態的緩存行時,才緩存才不需要做額外操作。如果狀態為Shared的緩存行被寫入,那麼首先別的緩存需要無效它們的緩存行。這個由一個叫 Request For Ownership (RFO) .的廣播操作執行。

    當一個變數為Modified時,這個緩存必須監聽所有別的緩存對於該變數對應主存的讀請求,一旦監聽到就必須將這個變數寫入主存。這個過程可以通過強制讓別的緩存等待的方法來完成。當完成了對主存的寫入,狀態變為Shared。但同樣可以直接通知別的緩存這個變數的值,而不寫入主存。

    當處於Shared狀態時,必須監聽所有的rfo廣播,一旦收到就讓緩存中的變數無效。

    Modified和Exclusive狀態是精確的,當有緩存行處於這個狀態就表明,這個變數是這個緩存獨有的。而Shared狀態時不精確的,當別的緩存丟棄了這個Shared狀態的緩存行,那麼可能只存在一個緩存行狀態為Shared但它不會變為Exclusive。丟棄Shared狀態行不會引起別的緩存的注意。

    使用Exclusive可以帶來性能上的提升,因為修改Exclusive不需要通知別的緩存,而修改Shared狀態的緩存行需要告訴別的緩存,並使這些緩存行無效,這是耗時的。(這也是EMSI與MSI協議的區別)

    MESI簡單直接的實現存在著兩個性能問題。1.當向一個Invalid的緩存行寫入時,需要從別的緩存獲取值,這是很耗時的。2.需要將別的緩存行的該值設置為Invalid這也是耗時的。為了解決這兩個問題,我們引入了存儲緩沖區和無效化隊列。

    當向一個無效的緩存行寫入時會用到儲存緩沖區。因為這個寫操作最終一定會被執行,因此CPU發送讀無效消息(讓別的緩存中的該緩存行無效)並且將要寫入的值放到存儲緩存區中,當這個需要的緩存行寫入緩存時,存儲緩存區中存儲的值將被執行寫入。
    存儲緩存區存在帶來的後果是,當一個CPU進行寫操作,值不會立即寫入緩存中。因此,無論何時CPU讀取緩存中的值時,都需要先掃描自己的存儲緩沖區,確保緩沖區中是否還有未寫入的值。值得注意的時不同CPU之間存儲緩沖區時不可見的。

    當CPU收到無效請求時,它會將這寫無效請求放入無效化隊列中。放入隊列中的請求會被迅速執行但不是立即執行。所以CPU可以忽略它的緩存塊是無效的。CPU不能掃描它的無效隊列,這是和存儲緩沖區的區別。

    可見存儲緩沖區帶來的是寫不同步,而無效化隊列則帶來讀不同步,為了解決這些不同步,我們需要內存屏障。寫屏障會刷新我們的存儲緩沖區,確保裡面所有的寫都會被執行到這個CPU的cache上,而讀屏障可以保證所有無效化隊列里的任務都被執行,確保別的CPU的寫對自己可見。