redis系列之資料庫與快取資料一致性解決方案

2022-06-06 11:51:10 字數 1601 閱讀 3431

一般來說,只要你用到了快取,不管是redis還是memcache,就可能會涉及到資料庫快取與資料的一致性問題,這裡我們以redis為例。

我們該如何保證redis與資料庫的一致性呢?

so easy:

乍一看,一致性問題貌似很好的得到了解決。但仔細一想,你會發現還是有問題:如果先更新了資料庫,刪除快取的時候失敗了怎麼辦?那麼資料庫中是新資料,快取中是老資料,資料出現不一致了。

改進方案:

先刪除快取,後更新資料庫。因為即使後面更新資料庫失敗了,快取是空的,讀的時候會從資料庫中重新拉,雖然都是舊資料,但資料是一致的。

這種情況應該是先刪除快取,然後在更新資料庫,如果刪除快取失敗,那就不要更新資料庫,如果說刪除快取成功,而更新資料庫失敗,那查詢的時候只是從資料庫里查了舊的資料而已,這樣就能保持資料庫與快取的一致性。

所以方案就變成了:

在高併發的情況下,如果當刪除完快取的時候,這時去更新資料庫,但還沒有更新完,另外乙個請求來查詢資料,發現快取裡沒有,就去資料庫里查,還是以上面商品庫存為例,如果資料庫中產品的庫存是100,那麼查詢到的庫存是100,然後插入快取,插入完快取後,原來那個更新資料庫的執行緒把資料庫更新為了99,導致資料庫與快取不一致的情況

遇到這種情況,可以用佇列的去解決這個問,建立幾個佇列,如20個,根據商品的id去做hash值,然後對佇列個數取摸,當有資料更新請求時,先把它丟到佇列裡去,當更新完後在從佇列裡去除,如果在更新的過程中,遇到以上場景,先去快取裡看下有沒有資料,如果沒有,可以先去佇列裡看是否有相同商品id在做更新,如果有也把查詢的請求傳送到佇列裡去,然後同步等待快取更新完成。

這裡有乙個優化點,如果發現佇列裡有乙個查詢請求了,那麼就不要放新的查詢操作進去了,用乙個while(true)迴圈去查詢快取,迴圈個200ms左右,如果快取裡還沒有則直接取資料庫的舊資料,一般情況下是可以取到的。

在高併發下解決場景二要注意的問題

(1)讀請求時長阻塞

由於讀請求進行了非常輕度的非同步化,所以一定要注意讀超時的問題,每個讀請求必須在超時間內返回,該解決方案最大的風險在於可能資料更新很頻繁,導致佇列中擠壓了大量的更新操作在裡面,然後讀請求會發生大量的超時,最後導致大量的請求直接走資料庫,像遇到這種情況,一般要做好足夠的壓力測試,如果壓力過大,需要根據實際情況新增機器。

(2)請求併發量過高

這裡還是要做好壓力測試,多模擬真實場景,併發量在最高的時候qps多少,扛不住就要多加機器,還有就是做好讀寫比例是多少

(3)多服務例項部署的請求路由

可能這個服務部署了多個例項,那麼必須保證說,執行資料更新操作,以及執行快取更新操作的請求,都通過nginx伺服器路由到相同的服務例項上

(4)熱點商品的路由問題,導致請求的傾斜

某些商品的讀請求特別高,全部打到了相同的機器的相同丟列裡了,可能造成某台伺服器壓力過大,因為只有在商品資料更新的時候才會清空快取,然後才會導致讀寫併發,所以更新頻率不是太高的話,這個問題的影響並不是很大,但是確實有可能某些伺服器的負載會高一些。

redis系列之 資料庫

當我們在redis資料庫中set乙個kv的時候,這個kv儲存在 如果我們get的時候,又從 get出來。時間複雜度,空間複雜的等等,怎麼優化等等一系列問題。redis伺服器將所有資料庫資訊都儲存在redis.h redisservice結構體中。如下 1 struct redisserver 列了幾...

redis資料庫快取

使用redis作為快取,資料還需要存入資料庫中嗎?我的答案是 1redis只是快取,不是資料庫如mysql,所以redis中有的資料庫,mysql中一定有。2使用者請求先去請求redis,如果沒有,再去資料庫中去讀取。3redis中快取一些請求量比較大的資料 這些快取資料,mysql中一定也是有的 ...

Redis 如何保證快取與資料庫雙寫時的資料一致性

寫請求來了,要更新資料庫和快取,一前一後更新,就可能導致快取和db中的資料在一段時間內不一致。你只要用快取,就可能會涉及到快取與資料庫雙儲存雙寫,你只要是雙寫,就一定會有資料一致性的問題,那麼你如何解決一致性問題?一般來說,就是如果你的系統不是嚴格要求快取 資料庫必須一致性的話,快取可以稍微的跟資料...