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

緩存一致:讀多寫少時,如何解決數據更新緩存不同步?

開發 前端
臨時緩存是有 TTL 的,如果 60 秒內修改了用戶的昵稱,緩存是不會馬上更新的。最糟糕的情況是在 60 秒后才會刷新這個用戶的昵稱緩存,顯然這會給系統帶來一些不必要的麻煩。其實對于這種緩存數據刷新,可以分成幾種情況,不同情況的刷新方式有所不同,接下來我給你分別講講。

我們之前提到過,互聯網大多數業務場景的數據都屬于讀多寫少,在請求的讀寫比例中,寫的比例會達到百分之一,甚至千分之一。而對于用戶中心的業務來說,這個比例會更大一些,畢竟用戶不會頻繁地更新自己的信息和密碼,所以這種讀多寫少的場景特別適合做讀取緩存。通過緩存可以大大降低系統數據層的查詢壓力,擁有更好的并發查詢性能。但是,使用緩存后往往會碰到更新不同步的問題,下面我們具體看一看。

緩存性價比

是的,緩存的確有可能被濫用,特別是在像用戶中心這樣對數據準確性要求很高的場景中。你提到在對用戶中心進行優化時,首要想到的就是將用戶信息放入緩存,以提高性能。這確實是一個常見的優化思路,因為緩存能夠顯著減少數據庫的訪問頻率,提升系統響應速度。

# 表結構
CREATE TABLE `accounts` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`account` varchar(15) NOT NULL DEFAULT '',
`password` char(32) NOT NULL,
`salt` char(16) NOT NULL,
`status` tinyint(3) NOT NULL DEFAULT '0'
`update_time` int(10) NOT NULL DEFAULT '0',
`create_time` int(10) NOT NULL DEFAULT '0',
  PRIMARY KEY (`id`),
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;


# 登錄查詢
select id, account, update_time from accounts 
where account = 'user1'
and password = '6b9260b1e02041a665d4e4a5117cfe16'
and status = 1

確實,這是一個簡單的查詢需求。乍一看,似乎將 2000 萬條用戶數據都放入緩存可以極大地提升性能,但實際上并不完全如此。雖然緩存能提供高性能的服務,但其性價比并不一定高。這個表主要用于賬號登錄的查詢,而登錄操作本身即使頻繁,也不會對系統帶來巨大的流量壓力。因此,即便將所有用戶數據放入緩存,大部分時間這些數據都處于閑置狀態。這樣一來,緩存資源反而被浪費,我們也不必要將并發量不高的數據緩存起來,從而增加預算開銷。

這就引出一個核心問題:緩存的使用需要考慮性價比。如果花費大量時間和資源將某些數據放入緩存,但對系統性能并沒有顯著的提升,甚至增加了額外的成本,那么這樣的緩存策略就是不合理的。緩存的效果需要經過評估,通常來說,只有熱點數據才值得放入緩存。

臨時熱緩存

在推翻了將所有賬號信息都放入緩存的方案后,我們將目標轉向那些被頻繁查詢的信息上,比如用戶信息。用戶信息的使用頻率非常高,尤其是在論壇等場景中,常常需要頻繁展示,例如用戶的頭像、昵稱和性別等。不過,由于這些數據量較大,全部緩存起來不僅浪費空間,還不具備性價比。

針對這種情況,我們可以考慮使用一種臨時緩存的策略:當某個用戶信息首次被訪問時,將其存入緩存;在短時間內,若有類似查詢請求,就可以直接從緩存中獲取。這樣既可以有效地降低數據庫查詢壓力,又不會占用過多的緩存空間。以下是一個常用的實現臨時緩存的代碼示例:

# 示例代碼
def get_user_info(user_id):
# 首先嘗試從緩存中獲取用戶信息
    user_info = cache.get(user_id)
if user_info:
return user_info


# 如果緩存中沒有,查詢數據庫
    user_info = db.query_user_info(user_id)


# 將查詢到的信息存入緩存,并設置一個合理的過期時間
    cache.set(user_id, user_info, timeout=300)  # 緩存五分鐘
return user_info

正如我們看到的,這種策略將數據臨時放入緩存,在 60 秒過期后自動淘汰。如果在這段時間內再次查詢相同數據,我們的代碼會重新將數據填入緩存,繼續提供使用。這種臨時緩存策略非常適合數據量大但熱點數據較少的場景,有助于緩解數據庫的查詢壓力。

設置緩存的 TTL(Time-to-Live)是為了更有效地利用內存資源。當數據在指定時間內未被再次訪問,就會被自動清除,這樣我們就能避免購買過多內存。通過這種方式,可以在節省成本的同時,提高緩存的性價比,且實現起來簡單,維護也方便,是一種很常用的策略

緩存更新不及時問題

臨時緩存是有 TTL 的,如果 60 秒內修改了用戶的昵稱,緩存是不會馬上更新的。最糟糕的情況是在 60 秒后才會刷新這個用戶的昵稱緩存,顯然這會給系統帶來一些不必要的麻煩。其實對于這種緩存數據刷新,可以分成幾種情況,不同情況的刷新方式有所不同,接下來我給你分別講講。

1. 單條實體數據緩存刷新

單條實體數據緩存更新是最簡單的一個方式,比如我們緩存了 9527 這個用戶的 info 信息,當我們對這條數據做了修改,我們就可以在數據更新時同步更新對應的數據緩存:

Type UserInfo struct {
  Id         int    `gorm:"column:id;type:int(11);primary_key;AUTO_INCREMENT" json:"id"`
  Uid        int    `gorm:"column:uid;type:int(4);NOT NULL" json:"uid"`
  NickName   string `gorm:"column:nickname;type:varchar(32) unsigned;NOT NULL" json:"nickname"`
  Status     int16  `gorm:"column:status;type:tinyint(4);default:1;NOT NULL" json:"status"`
  CreateTime int64  `gorm:"column:create_time;type:bigint(11);NOT NULL" json:"create_time"`
  UpdateTime int64  `gorm:"column:update_time;type:bigint(11);NOT NULL" json:"update_time"`
}


//更新用戶昵稱
func (m *UserInfo)UpdateUserNickname(ctx context.Context, name string, uid int) (bool, int64, error) {
//先更新數據庫
  ret, err := m.db.UpdateUserNickNameById(ctx, uid, name)
if ret {
//然后清理緩存,讓下次讀取時刷新緩存,防止并發修改導致臨時數據進入緩存
//這個方式刷新較快,使用很方便,維護成本低
    Redis.Del("user_info_" + strconv.Itoa(uid))
  }
return ret, count, err
}

總體來說,我們可以先識別出被修改的數據 ID,然后根據這些 ID 刪除相應的數據緩存。在下次請求到來時,系統會重新獲取最新的數據并更新到緩存中,這樣可以有效減少并發操作將臟數據寫入緩存的可能性。

除了這種方法,我們還可以向隊列發送更新消息,讓子系統處理更新,或者開發中間件,將數據操作發送到子系統,讓其自行決定需要更新的數據范圍。然而,通過隊列更新消息時,我們可能會遇到一個問題——條件批量更新時,可能無法直接確定具體有多少個 ID 發生了變化。常見的解決方法是:首先按照相同的條件查詢出所有受影響的 ID,然后執行更新操作,最后使用這些相關的 ID 更新具體的緩存。

2. 關系型和統計型數據緩存刷新

首先,有一種人工維護緩存的方式。眾所周知,關系型數據或統計結果的緩存刷新具有一定的難度,主要原因在于這些統計數據通常是基于多條數據計算得出的。當我們需要刷新這類數據的緩存時,很難準確識別出需要更新的關聯緩存。

為了解決這個問題,可以通過人工方式,在集中管理的地方記錄或定義特定的刷新邏輯,以實現關聯緩存的更新。

圖片圖片

不過這種方式比較精細,如果刷新緩存很多,那么緩存更新會比較慢,并且存在延遲。而且人工書寫還需要考慮如何查找到新增數據關聯的所有 ID,因為新增數據沒有登記在 ID 內,人工編碼維護會很麻煩。除了人工維護緩存外,還有一種方式就是通過訂閱數據庫來找到 ID 數據變化。如下圖,我們可以使用 Maxwell 或 Canal,對 MySQL 的更新進行監控。

圖片圖片

在這種方案中,變更信息會被推送到 Kafka。我們可以根據表名和具體的 SQL 確認哪些數據 ID 發生了更新,然后依據腳本中設定的邏輯,對相關緩存 key 進行更新。比如,當用戶更新了昵稱,緩存更新服務就能夠識別需要更新 user_info_9527 這個緩存,同時根據配置找到并刪除其他相關的緩存。這種方法的優勢在于,可以快速地更新簡單的緩存,并且核心系統可以向子系統廣播數據變更信息,代碼實現也相對簡單。不過,對于復雜的關聯關系刷新,仍然需要人工書寫邏輯來實現。

如果表內數據更新較少,還可以考慮使用版本號緩存策略。這種方法比較直接:一旦有任何更新,表中所有數據緩存都會過期。例如,可以為 user_info 表設置一個版本號 key,比如 user_info_version。當表數據發生更新時,直接將 user_info_version 自增 1。寫入緩存時,同時記錄當前版本號;讀取時,業務邏輯會檢查緩存版本號與表版本號是否一致。如果不一致,就更新緩存數據。需要注意的是,如果版本號頻繁更新,緩存命中率會大幅下降,因此該方法更適合數據更新不頻繁的表

當然,我們還可以對這個表做一個范圍拆分,比如按 ID 范圍分塊拆分出多個 version,通過這樣的方式來減少緩存刷新的范圍和頻率。

圖片圖片

此外,關聯型數據更新還可以通過識別主要實體 ID 來刷新緩存。這要保證其他緩存保存的 key 也是主要實體 ID,這樣當某一條關聯數據發生變化時,就可以根據主要實體 ID 對所有緩存進行刷新。這個方式的缺點是,我們的緩存要能夠根據修改的數據反向找到它關聯的主體 ID 才行。

圖片圖片

最后,還有一種方法是通過異步腳本遍歷數據庫來刷新所有相關緩存。這種方式適用于在兩個系統之間進行數據同步,能夠減少系統之間的接口交互頻率。其缺點是,在數據被刪除后,還需要手動刪除相應的緩存,因此更新存在一定延遲。不過,如果結合訂閱更新消息廣播機制,這種方案可以實現近乎同步的數據更新。

長期熱數據緩存

回過頭來看之前提到的臨時緩存方案,雖然它能解決大部分問題,但有個潛在風險需要考慮:當 TTL 到期時,如果有大量緩存請求未命中,透傳的流量可能會給數據庫帶來巨大的壓力,甚至可能導致數據庫崩潰。這就是業內常說的緩存穿透問題。如果發生大規模的并發穿透,服務可能宕機。因此,如果數據庫無法承受日常流量,就不能依賴臨時緩存方案來設計緩存系統,而應該采用長期緩存的方式來實現熱點緩存,以避免緩存穿透對數據庫的影響。

要實現長期緩存,需要更多的人工操作來保證緩存與數據表的一致性。長期緩存的普及主要得益于 NoSQL 技術的發展,它與臨時緩存不同,需要業務幾乎不依賴數據庫,所有在服務運行期間所需的數據都必須在緩存中可用,并確保緩存不會在使用期間丟失。這帶來的挑戰是,我們需要精確知道緩存中的數據,并提前對這些數據進行預熱。如果數據規模較小,還可以考慮將所有數據緩存起來,這樣的實現會相對簡單一些。

總結

并不是所有數據放入緩存都會帶來良好的收益,因此我們需要從數據量、使用頻率和緩存命中率三個方面進行分析。對于讀多寫少的數據,雖然將其緩存能夠降低數據層的壓力,但仍需根據一致性需求來更新緩存中的數據。

在這方面,單條實體數據的緩存更新相對容易實現,但對于需要條件查詢的統計結果,實時更新則較為困難。因此,在設計緩存策略時,需綜合考慮這些因素,以確保緩存的有效性和數據的一致。

圖片 圖片

責任編輯:武曉燕 來源: 二進制跳動
相關推薦

2020-06-01 22:09:48

緩存緩存同步緩存誤用

2021-04-18 15:01:56

緩存系統數據

2020-05-12 10:43:22

Redis緩存數據庫

2022-09-06 15:30:20

緩存一致性

2022-03-31 08:21:14

數據庫緩存雙寫數據一致性

2021-06-11 09:21:58

緩存數據庫Redis

2024-12-26 15:01:29

2022-12-14 08:23:30

2020-09-03 09:45:38

緩存數據庫分布式

2021-01-19 10:39:03

Redis緩存數據

2015-11-25 11:20:23

WindowsUbuntu時間同步

2022-03-29 10:39:10

緩存數據庫數據

2024-10-28 12:41:25

2018-07-15 08:18:44

緩存數據庫數據

2023-04-13 08:15:47

Redis緩存一致性

2024-04-11 13:45:14

Redis數據庫緩存

2022-07-25 09:48:22

緩存數據服務

2022-04-01 16:55:22

數據庫緩存日志

2023-05-09 10:59:33

緩存技術派MySQL

2018-12-13 12:43:07

Redis緩存穿透
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 在线看无码的免费网站 | 国产精品久久精品 | 日韩一区二区三区在线看 | 99久久精品一区二区毛片吞精 | 欧美日韩成人影院 | 精品亚洲一区二区三区 | 欧美精品a∨在线观看不卡 国产精品久久国产精品 | 欧美精三区欧美精三区 | 久久久久免费精品国产 | 国产1区2区 | 欧美日韩在线精品 | 国产欧美日韩精品一区 | 黄色大片观看 | 亚州成人 | 日本精品裸体写真集在线观看 | 黄色在线免费看 | 久久综合九色综合欧美狠狠 | 欧美一区二区 | 91久久精品国产 | 国产激情一区二区三区 | 天天艹| 亚洲精品一区二区三区中文字幕 | 在线免费看黄 | 欧美精品综合在线 | 久久久久1| 午夜在线视频 | 欧美在线精品一区 | 久久国产精品一区二区三区 | 黄色永久免费 | 成人欧美一区二区三区在线播放 | 国产成人精品a视频一区www | 最近中文字幕在线视频1 | 欧美日韩久久精品 | 伊人免费观看视频 | 中文字幕伊人 | 成人做爰www免费看视频网站 | 可以在线看的黄色网址 | 欧美9999 | 午夜精品视频在线观看 | 欧美精品一区二区三区四区五区 | 午夜黄色影院 |