缓存一致性问题

当我们使用缓存时,必定会遇到缓存一致性问题,也就是在读写请求过程中数据库缓存中的数据不一致。 下面将分析为什么会造成不一致, 所有的代码参考末尾

先更新数据库,后更新缓存

  • 数据库的值默认为 0

读操作:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
public String get(Long id) {
    // 从缓存中加载
    String userName = userCache.queryUserNameById(id);
    if (userName == null) {
        // 从数据库中加载
        userName = userDB.queryUserNameById(id);
        // 设置到缓存中
        TestUtil.sleep(200); // 表示 gc,请求延迟
        userCache.setUserNameById(id, userName);
    }
    return userName;
}

写操作:

1
2
3
4
5
6
7
public void set(Long id, String username) {
    // 更新数据库
    TestUtil.sleep(100); // 表示 gc, 请求延迟
    userDB.setUserNameById(id, username);
    // 更新缓存
    userCache.setUserNameById(id, null);
}

实际执行过程:

  1. 读操作(从缓存中读取数据,发现为空,所以查询数据库,得到 0)
  2. 写操作(更新数据库值为 1,删除缓存值)
  3. 读操作(更新缓存值为 0)
  4. 不一致(数据库值为 1,缓存值为 0)

从上面可以分析,更新数据库更新缓存的顺序,无论谁先谁后都会造成数据不一致

同时更新数据

写操作(A):

1
2
3
4
5
6
7
8
// username = 1
public void set1(Long id, String username) {
    // 更新缓存
    TestUtil.sleep(100);
    userCache.setUserNameById(id, username);
    // 更新数据库
    userDB.setUserNameById(id, username);
}

写操作(B):

1
2
3
4
5
6
7
8
// username = 2
public void set2(Long id, String username) {
    // 更新缓存
    userCache.setUserNameById(id, username);
    // 更新数据库
    TestUtil.sleep(200);
    userDB.setUserNameById(id, username);
}

实际执行过程:

  1. B(更新缓存值为 2)
  2. A(更新缓存值为 1,更新数据库值为 1)
  3. B(更新数据库值为 2)
  4. 不一致(数据库值为 2,缓存值为 1)

从上面可以分析,更新数据库更新缓存的顺序,无论谁先谁后都会造成数据不一致

解决方法

  1. 使用分布式锁来确保更新数据库更新缓存原子性

代码

demo-cache-consistency-question

0%