网站刷流量有什么用,WordPress禁止下载,网页微信加群,个人网站如何制作在高并发场景下#xff0c;Redis 通常用作缓存层#xff0c;与数据库结合使用以提高系统的性能。为了保证缓存数据与数据库的最终一致性#xff0c;通常采用的有双写机制、缓存失效机制#xff0c;基于双写机制、缓存失效机制又衍生出来了消息队列、事件驱动架构等
常见机…在高并发场景下Redis 通常用作缓存层与数据库结合使用以提高系统的性能。为了保证缓存数据与数据库的最终一致性通常采用的有双写机制、缓存失效机制基于双写机制、缓存失效机制又衍生出来了消息队列、事件驱动架构等
常见机制
常见的机制如下个人理解无非是先后或各种手段操作数据库、redis代码ai给写的示列只需看懂即可。
双写机制 在更新数据库的同时同步更新缓存。 适用于写操作较少的场景 public class CacheService {private final JdbcTemplate jdbcTemplate;private final RedisTemplateString, Object redisTemplate;public void updateData(String key, String value) {// 更新数据库jdbcTemplate.update(UPDATE table SET value ? WHERE key ?, value, key);// 更新缓存redisTemplate.opsForValue().set(key, value);}缓存失效机制 在更新数据库后删除缓存中的旧数据读取数据时候时写入缓存 适用于写操作频繁的场景。 public class CacheService {private final JdbcTemplate jdbcTemplate;private final RedisTemplateString, Object redisTemplate;public void updateData(String key, String value) {// 更新数据库jdbcTemplate.update(UPDATE table SET value ? WHERE key ?, value, key);// 删除缓存redisTemplate.delete(key);}public String getData(String key) {// 从缓存中获取数据String value (String) redisTemplate.opsForValue().get(key);if (value null) {// 缓存未命中从数据库中获取数据value jdbcTemplate.queryForObject(SELECT value FROM table WHERE key ?, new Object[]{key}, String.class);if (value ! null) {// 将数据写入缓存redisTemplate.opsForValue().set(key, value);}}return value;}}消息队列机制 使用消息队列异步更新redis确保数据的一致性。 适用于高并发写操作的场景。 import com.rabbitmq.client.Channel;import com.rabbitmq.client.Connection;import com.rabbitmq.client.ConnectionFactory;public class CacheService {private final JdbcTemplate jdbcTemplate;private final RedisTemplateString, Object redisTemplate;public void updateData(String key, String value) {// 更新数据库jdbcTemplate.update(UPDATE table SET value ? WHERE key ?, value, key);// 发送消息到消息队列sendUpdateMessage(key, value);}private void sendUpdateMessage(String key, String value) {ConnectionFactory factory new ConnectionFactory();factory.setHost(localhost);try (Connection connection factory.newConnection();Channel channel connection.createChannel()) {channel.queueDeclare(cache_update_queue, true, false, false, null);channel.basicPublish(, cache_update_queue, null, (key : value).getBytes());} catch (Exception e) {e.printStackTrace();}}public void consumeUpdateMessages() {ConnectionFactory factory new ConnectionFactory();factory.setHost(localhost);try (Connection connection factory.newConnection();Channel channel connection.createChannel()) {channel.queueDeclare(cache_update_queue, true, false, false, null);DeliverCallback deliverCallback (consumerTag, delivery) - {String message new String(delivery.getBody(), UTF-8);String[] parts message.split(:);String key parts[0];String value parts[1];// 更新缓存redisTemplate.opsForValue().set(key, value);};channel.basicConsume(cache_update_queue, true, deliverCallback, consumerTag - {});} catch (Exception e) {e.printStackTrace();}}}事件驱动机制 使用事件驱动架构当数据库数据发生变化时触发事件事件处理器负责更新缓存。 适用于复杂的数据更新逻辑。 import org.springframework.context.ApplicationEventPublisher;import org.springframework.context.ApplicationEventPublisherAware;import org.springframework.stereotype.Service;Servicepublic class CacheService implements ApplicationEventPublisherAware {private final JdbcTemplate jdbcTemplate;private final RedisTemplateString, Object redisTemplate;private ApplicationEventPublisher eventPublisher;public void updateData(String key, String value) {// 更新数据库jdbcTemplate.update(UPDATE table SET value ? WHERE key ?, value, key);// 发布事件eventPublisher.publishEvent(new DataUpdatedEvent(this, key, value));}Overridepublic void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {this.eventPublisher applicationEventPublisher;}Servicepublic class EventListener {private final RedisTemplateString, Object redisTemplate;org.springframework.context.event.EventListenerpublic void handleDataUpdatedEvent(DataUpdatedEvent event) {// 更新缓存redisTemplate.opsForValue().set(event.getKey(), event.getValue());}}}public class DataUpdatedEvent extends ApplicationEvent {private final String key;private final String value;public DataUpdatedEvent(Object source, String key, String value) {super(source);this.key key;this.value value;}public String getKey() {return key;}public String getValue() {return value;}}定期补偿机制 定期对缓存和数据库的数据进行校验发现不一致时进行补偿操作。 适用于对数据一致性要求较高的场景。 import java.util.concurrent.Executors;import java.util.concurrent.ScheduledExecutorService;import java.util.concurrent.TimeUnit;public class DataConsistencyChecker {private final JdbcTemplate jdbcTemplate;private final RedisTemplateString, Object redisTemplate;private final ScheduledExecutorService scheduler Executors.newScheduledThreadPool(1);public void startChecking() {scheduler.scheduleAtFixedRate(() - {// 从数据库中获取所有数据ListMapString, Object dataFromDB jdbcTemplate.queryForList(SELECT key, value FROM table);for (MapString, Object row : dataFromDB) {String key (String) row.get(key);String value (String) row.get(value);// 从缓存中获取数据String cacheValue (String) redisTemplate.opsForValue().get(key);if (!value.equals(cacheValue)) {// 数据不一致更新缓存redisTemplate.opsForValue().set(key, value);}}}, 0, 1, TimeUnit.HOURS);}}废弃缓存与更新缓存的取舍
由上面代码可看出 1和2 最大的区别在于更新数据库时到底是更新缓存还是删除缓存。 【废弃缓存】 优点 操作简单只需在更新数据库后删除缓存下次读取时重新从数据库加载数据减少了写的操作日数 缺点 可能短暂不一致在缓存删除后和新数据写入缓存前可能会出现短暂的缓存不一致
【更新缓存】 优点 数据强一致性更新数据库和缓存同时进行确保数据的一致性。 减少数据库读压力缓存始终是最新的减少了对数据库的读操作。 缺点 复杂性增加需要处理缓存更新失败的情况可能需要回滚操作。 性能影响每次更新操作都需要同时更新数据库和缓存增加了操作的复杂性和时间
写操作较少的场景 推荐使用更新缓存因为写操作较少更新缓存的额外开销相对较小且可以确保数据的一致性。写操作频繁的场景 推荐使用废弃缓存因为写操作频繁更新缓存会增加系统的复杂性和开销而废弃缓存可以减少缓存的写操作降低系统负担。对数据一致性要求极高的场景 推荐使用更新缓存尽管复杂性增加但可以确保数据的强一致性。对性能要求较高且可以容忍短暂不一致的场景 推荐使用废弃缓存可以减少数据库的读压力提高系统的整体性能
淘汰缓存的顺序 https://blog.csdn.net/qq_39033181/article/details/119276120 【 方案一 】先淘汰缓存再更新数据库 在并发量较大的情况下会导致数据的不一致。 1. A线程进行写操作先成功淘汰缓存但由于网络或其它原因还未更新数据库 2. B线程进行读操作发现缓存中没有想要的数据从数据库中读取到的是旧数据并把旧数据放入缓存。此时数据库与缓存都是旧值数据没有不一致 3. A线程将数据库更新完成数据库中是更新后的新数据缓存中是更新前的旧数据造成数据不一致。
【 方案二 】先更新数据库再淘汰缓存 在并发量较大的情况下会导致数据的短暂不一致但是数据会最终一致。 1. A线程进行写操作更新数据库还未淘汰缓存 2. B线程从缓存中可以读取到旧数据此时数据不一致 3. A线程完成淘汰缓存操作其它线程进行读操作从数据库中读入最新数据此时数据一致
延时双删
上述方案二更简单在高并发场景下也能保证数据的最终一致性但是如果我就想用方案一呢
什么是延时双删
先删再更新数据库 过N秒后再删一次缓存怎么实现放后面spring-cache集成里大概有 1.延时队列、2.线程池实现延时任务。
小结 这些都是理论真正写代码有cache框架哪有这么烦很多人喜欢问那我们就得理理了总比不理好写这个就是怕我自己忘呵 无论怎么样在高并发场景下我们也只能要求缓存数据与数据库的最终一致性如果要求强一致性还要缓存干嘛呢操作直接走DB更香 大多数情况下建议使用淘汰缓存机制然后先更新数据库再淘汰缓存满足大多数的场景了