双写一致性
redis与数据库不一致的两种情况
出现在高并发场景下,当有数据读和写的请求,就可能出现数据库与缓存不一致的情况
一、先操作删除缓存,再修改数据库数据的情况下
当缓存被线程一删除后,如果此时有新的读请求(线程二)发生,由于缓存已经被删除,这个读请求(线程二)将会去从数据库查询。如果此时线程一还没有修改完数据库,线程二从数据库读的数据仍然是旧值,同时线程二将读的旧值写入到缓存。线程一完成后,数据库变为新值,而缓存还是旧值。这就造成了数据库与缓存的不一致。
二、先修改数据库,再删除缓存
1、当数据库的数据被更新后,如果此时缓存还没有被删除,那么缓存中的数据仍然是旧值。如果此时有新的读请求(查询数据)发生,由于缓存中的数据是旧值,这个读请求将会获取到旧值。
2、当缓存刚好失效,这时有请求来读缓存(线程一),未命中缓存,然后到数据库中读取,在要写入缓存时,线程二来修改了数据库,而线程一写入缓存的是旧的数据,导致了数据的不一致。
解决办法
1、用redisson实现读锁和写锁
Redisson 是一个基于 Redis 的分布式 Java 对象存储和缓存框架,它提供了丰富的功能和 API 来操作 Redis 数据库。其中包括了读写锁的支持。
读写锁是一种常用的并发控制机制,它允许多个线程同时读取共享资源,但在写操作时互斥,只允许一个线程进行写操作
。
使用 Redisson 的读写锁方法:
-
获取读锁:通过 Redisson 的 RReadWriteLock 对象的 readLock() 方法获取读锁。在获取读锁后,可以并发读取共享资源,不会阻塞其他获取读锁的线程。
-
获取写锁:通过 Redisson 的 RReadWriteLock 对象的 writeLock() 方法获取写锁。在获取写锁后,其他获取读锁和写锁的线程将被阻塞,只有当前线程能够进行写操作。
-
释放锁:使用完读锁或写锁后,应该及时调用 unlock() 方法释放锁,以便其他线程可以获取锁并进行操作。在 Redisson 中,读锁和写锁都继承自锁对象 RLock,因此可以使用 RLock 的 unlock() 方法来释放锁。
下面是一个使用 Redisson 读写锁的示例:
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
public class RedissonReadWriteLockExample {
public static void main(String[] args) {
// 创建 Redisson 客户端
Config config = new Config();
config.useSingleServer().setAddress("redis://127.0.0.1:6379");
RedissonClient redisson = Redisson.create(config);
// 获取读写锁
RReadWriteLock rwLock = redisson.getReadWriteLock("myLock");
RLock readLock = rwLock.readLock();
RLock writeLock = rwLock.writeLock();
try {
// 获取读锁并进行读操作
readLock.lock();
// 读取共享资源
// 获取写锁并进行写操作
writeLock.lock();
// 写入共享资源
} finally {
// 释放锁
writeLock.unlock();
readLock.unlock();
}
// 关闭 Redisson 客户端
redisson.shutdown();
}
}
通过 Redisson 的 RReadWriteLock 对象获取读锁和写锁,并在需要的代码段中进行相应的操作。执行完操作后,使用 unlock() 方法释放锁,最后关闭 Redisson 客户端。
2、异步通知可以保证Redis双写一致性
在更新数据库数据时,同时发送一个异步通知给Redis,让Redis知道数据库数据已经更新,需要更新缓存中的数据。这个过程是异步的,不会阻塞数据库的更新操作。
当Redis收到异步通知后,会立即删除缓存中对应的数据,确保缓存中没有旧数据。这样,即使在这个过程中有新的读请求发生,也不会读取到旧数据。等到数据库更新完成后,Redis再次从数据库中读取最新的数据并缓存起来。
这种异步通知的方式,可以确保Redis中的数据与数据库中的数据保持一致,避免出现数据不一致的情况。
需要注意的是,异步通知可能会出现延迟或者丢失的情况,因此需要做好异常处理和重试机制,确保数据的准确性。