保证多级缓存数据一致性是一个复杂的任务,尤其是在分布式和高并发环境中。以下是一些常见的方法和策略,可以帮助实现多级缓存的数据一致性
在更新数据库时,主动使相关缓存失效。
步骤:
public class CacheService {
private LocalCache localCache;
private RedisCache redisCache;
private Database database;
public void updateData(Data data) {
// 更新数据库
database.update(data);
// 删除相关缓存
String cacheKey = "data:" + data.getId();
localCache.delete(cacheKey);
redisCache.delete(cacheKey);
}
}
在更新数据库前后都删除缓存,确保缓存数据的准确性。
步骤:
public class CacheService {
private LocalCache localCache;
private RedisCache redisCache;
private Database database;
public void updateData(Data data) {
String cacheKey = "data:" + data.getId();
// 第一次删除缓存
localCache.delete(cacheKey);
redisCache.delete(cacheKey);
// 更新数据库
database.update(data);
// 确保数据库更新完成后再次删除缓存
try {
Thread.sleep(1000); // 延迟 1 秒
} catch (InterruptedException e) {
e.printStackTrace();
}
localCache.delete(cacheKey);
redisCache.delete(cacheKey);
}
}
步骤:
public class CacheService {
private LocalCache localCache;
private RedisCache redisCache;
private Database database;
public void updateData(Data data) {
// 更新数据库
database.update(data);
// 更新缓存
String cacheKey = "data:" + data.getId();
localCache.set(cacheKey, data);
redisCache.set(cacheKey, data);
}
}
public class CacheService {
private LocalCache localCache;
private RedisCache redisCache;
private Database database;
public void updateData(Data data) {
String cacheKey = "data:" + data.getId();
// 删除缓存
localCache.delete(cacheKey);
redisCache.delete(cacheKey);
// 更新数据库
database.update(data);
}
}
在写入数据库时,同时更新缓存。
步骤:
public class CacheService {
private LocalCache localCache;
private RedisCache redisCache;
private Database database;
public void updateData(Data data) {
// 更新数据库
database.update(data);
// 更新缓存
String cacheKey = "data:" + data.getId();
localCache.set(cacheKey, data);
redisCache.set(cacheKey, data);
}
}
先更新缓存,定期将缓存数据写回数据库。
步骤:
public class CacheService {
private LocalCache localCache;
private RedisCache redisCache;
private Database database;
public void updateData(Data data) {
// 更新缓存
String cacheKey = "data:" + data.getId();
localCache.set(cacheKey, data);
redisCache.set(cacheKey, data);
// 定期将缓存数据写回数据库
scheduleWriteBack(data);
}
private void scheduleWriteBack(Data data) {
// 实现定期将缓存数据写回数据库的逻辑
}
}
使用分布式锁(如 Redis 的 RedLock)来确保多节点环境下的一致性。
步骤:
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
public class CacheService {
private LocalCache localCache;
private RedisCache redisCache;
private Database database;
private JedisPool jedisPool;
public void updateDataWithLock(Data data) {
String lockKey = "lock:data:" + data.getId();
try (Jedis jedis = jedisPool.getResource()) {
// 获取分布式锁
String lock = acquireLock(jedis, lockKey, 10000);
if (lock != null) {
try {
updateData(data);
} finally {
// 释放分布式锁
releaseLock(jedis, lockKey, lock);
}
}
}
}
private void updateData(Data data) {
// 更新数据库
database.update(data);
// 更新缓存
String cacheKey = "data:" + data.getId();
localCache.set(cacheKey, data);
redisCache.set(cacheKey, data);
}
private String acquireLock(Jedis jedis, String lockKey, int timeout) {
// 实现获取分布式锁的逻辑
}
private void releaseLock(Jedis jedis, String lockKey, String lock) {
// 实现释放分布式锁的逻辑
}
}
使用异步机制,在更新数据库后异步更新缓存。
步骤:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class CacheService {
private LocalCache localCache;
private RedisCache redisCache;
private Database database;
private ExecutorService executorService = Executors.newFixedThreadPool(10);
public void updateData(Data data) {
// 更新数据库
database.update(data);
// 异步更新缓存
String cacheKey = "data:" + data.getId();
executorService.submit(() -> updateCache(cacheKey, data));
}
private void updateCache(String cacheKey, Data data) {
localCache.set(cacheKey, data);
redisCache.set(cacheKey, data);
}
}
通过消息队列通知其他缓存节点更新缓存。这里举例使用rabbitmq
简单实现,也可以用其他mq
或者Canal
实现
步骤:
// RabbitMQConfig.java
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Channel;
public class RabbitMQConfig {
public static final String QUEUE_NAME = "cache_invalidation_queue";
public static Channel createChannel() throws Exception {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost"); // RabbitMQ 服务器地址
Connection connection = factory.newConnection();
return connection.createChannel();
}
}
// CacheService.java
public class CacheService {
private LocalCache localCache;
private RedisCache redisCache;
private Database database;
private Channel channel;
public CacheService() throws Exception {
this.channel = RabbitMQConfig.createChannel();
channel.queueDeclare(RabbitMQConfig.QUEUE_NAME, false, false, false, null);
}
public void updateData(Data data) throws Exception {
// 更新数据库
database.update(data);
// 生成缓存键
String cacheKey = "data:" + data.getId();
// 发送缓存失效消息到 RabbitMQ
channel.basicPublish("", RabbitMQConfig.QUEUE_NAME, null, cacheKey.getBytes());
}
}
// CacheInvalidationListener.java
import com.rabbitmq.client.*;
public class CacheInvalidationListener {
private LocalCache localCache;
private RedisCache redisCache;
private Channel channel;
public CacheInvalidationListener() throws Exception {
this.channel = RabbitMQConfig.createChannel();
channel.queueDeclare(RabbitMQConfig.QUEUE_NAME, false, false, false, null);
}
public void startListener() throws Exception {
Consumer consumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
String cacheKey = new String(body, "UTF-8");
localCache.delete(cacheKey);
redisCache.delete(cacheKey);
}
};
channel.basicConsume(RabbitMQConfig.QUEUE_NAME, true, consumer);
}
public static void main(String[] args) throws Exception {
CacheInvalidationListener listener = new CacheInvalidationListener();
listener.startListener();
}
}
TIPS:异步更新和消息队列思想的区别
异步更新:适用于单节点或小规模系统,依赖于应用内部的异步处理机制,较为简单但在分布式环境中扩展性差。使用消息队列:适用于分布式和大规模系统,依赖于外部消息队列系统,在多个节点之间确保数据一致性,更具扩展性但实现和运维复杂度较高。
通过结合使用主动失效、延迟双删、双写一致性、写通过、写回、分布式锁和异步更新等策略,可以有效地保证多级缓存的数据一致性。选择合适的策略取决于具体的应用场景和需求O(∩_∩)O