redis 缓存一致性

问题

在使用缓存中一般都是先看看缓存是否有数据,没有查 db,再回填到缓存。

然后更新时候一般是删除或者回填缓存,再更新 db。

然而因为缓存与数据库是两个独立的系统,很难去保证原子性,所以就产生了一致性的问题。

 比如说:

  1. 一个查询请求查到了数据库数据,然后准备更新到缓存。
  2. 在上一步中间状态时候有更新请求更新了数据,还删了一次缓存。
  3. 第一步更新缓存在第二步之后完成,导致缓存出现旧数据。

解决

上面说到了一些缓存更新顺序问题,在使用 Redis 时可以将查询请求的回填改为 Setnx,更新操作使用 Set 方式回填缓存。

当出现问题情况时,更新的新数据会直接强制更新上缓存,而旧数据的更新会因为已有缓存而取消重复更新。

但是如果是两个写并发时用上面的方法就没有用了。

带上版本号进行乐观锁,但实现成本很高,如果用切面方式改造需要改造中间件:

  1. 同步操作 DB
  2. 高版本 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"})
}

 

posted @ 2024-05-31 16:18  Fang20  阅读(2)  评论(0编辑  收藏  举报