走精品游戏路线,黑岛科技获数百万天使轮融资
06-17
文章介绍Redis作为一种非关系型数据库,已经应用于各种高性能业务场景。 Redis是一个基于内存的数据库,因此在读写方面都有非常好的性能。
实际使用中,多用于一些业务数据缓存的场景。一般团队自己搭建Redis,也会使用云服务,比如腾讯云Redis服务。
支持主从热备份,提供自动容灾切换、数据备份、故障迁移、实例监控、在线扩容、数据回滚等全套数据库服务。在设计缓存的时候,我们要考虑一种情况,就是缓存数据的一致性。
如何理解缓存一致性?举个简单的例子,在一个电商系统应用中,我们将商品的库存数量存储在缓存中。这时我们在后台更新该产品的库存数量。
如何保证缓存中的库存信息同步更新且不发生库存情况?数量问题?文章后面的代码演示也使用了这个案例作为演示。缓存设计在了解缓存设计之前,我们先看一下下面的图。
这张图也是很多缓存系统的设计模式。客户端向服务器发送请求。
直接去缓存查询数据。如果缓存中存在数据,则直接将缓存中的数据返回给客户端。
如果缓存中不存在该数据,则查询数据库。根据MySQL中查询到的数据,写入缓存并返回给客户端。
主文前面提到的数据一致性是指MySQL和缓存中的数据如何保持同步。下面的文章也分析了如何实现数据同步。
先缓存更新策略,然后数据库策略解释后端发生更新请求,更新对应的Redis缓存。这个过程中可以直接删除并写入新的;您还可以使用更新方法。
使用delete相对来说更方便。如果缓存更新失败,则直接返回客户端错误信息。
如果缓存更新成功,则执行更新MySQL操作。如果 MySQL 更新失败,则回滚整个更新(包括缓存中的更新)。
问题分析:如果步骤1中删除了缓存,步骤2中更新缓存失败,则需要手动追加缓存。否则会出现缓存崩溃的情况。
这种情况是非常严重的。步骤4中,如果更新MySQL失败,则回滚缓存中的数据。
MySQL更新操作过程中,如果客户端出现新的请求,客户端会读取到新的数据。然而,实际的MySQL更新失败,用户无法读取到新的数据,因此数据也会出现不一致的情况。
代码演示代码语言:txt copy // Redis连接对象 $redis = null; // MySQL 连接对象 $mysql = null; // 客户端请求参数 $requestParams = []; // 删除缓存 $updateRedis = $redis-> del('key');if ($updateRedis) { //更新MySQL $updateMysql = $mysql->update('update xxx set a=xx where id=xxx') ; if ($updateMysql) { return '数据更新失败'; } // 回滚缓存(因为缓存删除失败,此时不需要手动回滚,如果是更新Redis,还需要手动回滚Redis) $redis->set('key' , $requestParams );}return '缓存更新失败';先数据库,后缓存策略说明:当客户端发起更新请求时,先更新MySQL。 MySQL更新成功后,会更新缓存。
可以直接使用删除操作或指定更新来更新缓存。如果Redis更新失败,则返回客户端信息。
问题分析 这个策略可以明显看出MySQL更新阶段没有问题。如果MySQL失败,直接返回客户端更新失败,不需要操作缓存。
但是在更新缓存时,如果缓存更新失败,MySQL中的数据会更新成功。那么我们面临这个问题,是应该回滚还是什么都不做呢?如果步骤2中缓存操作失败,则缓存将始终是旧数据,不进行任何处理,除非缓存有效期已过。
代码演示代码语言:txt copy // Redis连接对象 $redis = null; // MySQL 连接对象 $mysql = null; // 客户端请求参数 $requestParams = []; // 更新 MySQL $updateMysql = $mysql-> update('update xxx set a=xx where id=xxx'); if ($updateMysql) { // 更新缓存 $updateRedis = $redis->set($requestParams); if ($updateRedis) { return '数据更新成功'; } return '缓存更新失败';} return '数据更新失败';多线程同步策略解释的是客户端发起请求,此时创建了两个线程。一个线程执行 MySQL 更新,一个线程执行缓存更新。
如果两个线程之一不成功,则回滚整个更新操作。问题分析:该策略通过多线程更新数据,减少阻塞问题,加快程序处理速度。
如果MySQL线程更新失败并且处理缓慢,则Redis更新快速处理成功。此时做回滚时,在细粒度的过程中,新的请求会从缓存中获取新的数据,而回滚后,缓存的数据将是旧数据。
代码演示代码语言:txt copy // Redis连接对象 $redis = null; // MySQL 连接对象 $mysql = null; // 客户端请求参数 $requestParams = []; // 线程1更新MySQL $updateMysql = $mysql ->update('update xxx set a=xx where id=xxx');// 线程2更新缓存 $updateRedis = $redis->set('key', $requestParams );if ($updateMysql && $updateRedis) { return '数据更新成功';}//执行数据回滚....return '数据更新失败';锁处理策略描述 客户端发起请求,创建锁。此时MySQL和缓存数据会依次更新。
无论成功还是失败,执行完毕后锁都会被释放。问题分析:客户端发起请求,创建锁。
创建锁时,可以使用set-nx方法,避免服务失败,缓存也不会自动过期。更新 MySQL 和缓存数据。
如果缓存成功则释放锁,如果缓存失败则释放锁。该方法适用于数据一致性较高的情况。
例如,当后端发起请求时,客户端必须在写操作成功或失败后释放锁才能执行读操作。使用这种方法,客户端需要读取代码来判断锁的情况。
如果有锁,则处于等待状态。不适合高并发业务场景。
但保证数据完全一致。代码演示代码语言:txt copy // Redis连接对象 $redis = null; // MySQL 连接对象 $mysql = null; // 客户端请求参数 $requestParams = [];/ 客户端发起加锁请求 // 更新MySQL $updateMysql = $mysql->update('update xxx set a=xx where id=xxx');$updateRedis = $redis ->set('key', $requestParams);if ($updateMysql && $updateRedis) { //释放锁 //返回信息 return '数据更新成功'; } // 释放锁 // 返回信息 return '更新失败';文章摘要这篇文章是针对不同情况的分析。
在许多情况下,这只是一种理论状态。比较推荐的方法是先更新MySQL,再更新缓存。
版权声明:本文内容由互联网用户自发贡献,本站不拥有所有权,不承担相关法律责任。如果发现本站有涉嫌抄袭的内容,欢迎发送邮件 举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。
标签:
相关文章
06-17
06-18
06-18
06-18
06-18
06-17
最新文章
【玩转GPU】ControlNet初学者生存指南
【实战】获取小程序中用户的城市信息(附源码)
包雪雪简单介绍Vue.js:开学
Go进阶:使用Gin框架简单实现服务端渲染
线程池介绍及实际案例分享
JMeter 注释 18 - JMeter 常用配置组件介绍
基于Sentry的大数据权限解决方案
【云+社区年度征文集】GPE监控介绍及使用