问题
在使用缓存中一般都是先看看缓存是否有数据,没有查 db,再回填到缓存。
然后更新时候一般是更新 db,更新完了再删除或者回填缓存。
然而因为缓存与数据库是两个独立的系统,很难去保证原子性,所以就产生了一致性的问题。
比如说:
- 一个查询请求查到了数据库数据,然后准备更新到缓存。
- 在上一步中间状态时候有更新请求更新了数据,还删了一次缓存。
- 第一步更新缓存在第二步之后完成,导致缓存出现旧数据。
解决
上面说到了一些缓存更新顺序问题,在使用 Redis 时可以将查询请求的回填改为 Setnx,更新操作使用 Set 方式回填缓存。
当出现问题情况时,更新的新数据会直接强制更新上缓存,而旧数据的更新会因为已有缓存而取消重复更新。
但是如果是两个写并发时用上面的方法就没有用了。
带上版本号进行乐观锁,但实现成本很高,如果用切面方式改造需要改造中间件:
- 同步操作 DB
- 高版本 Cache 无法覆盖 低版本 Cache。
低并发时也可以考虑缓存双删。
func updateDataHandler(c *gin.Context) { id := c.Param("id") var requestBody struct { Data string `json:"data"` } if err := c.BindJSON(&requestBody); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } // 更新前删除缓存 deleteCache(id) // 更新数据库 updateDataInDB(id, requestBody.Data) // 更新后再次删除缓存,使用 Goroutine 异步操作 go func() { time.Sleep(500 * time.Millisecond) deleteCache(id) }() c.JSON(http.StatusOK, gin.H{"status": "success"}) }
标签:缓存,err,redis,更新,回填,一致性,gin,id From: https://www.cnblogs.com/caiawo/p/18224781