首页 文章 精选 留言 我的

精选列表

搜索[分布式锁],共10000篇文章
优秀的个人博客,低调大师

分布式缓存专题-功能原理分析(2)缓存与数据库一致性如何解决

数据缓存由来 在实际的业务场景中,一定有很多需要做数据缓存的场景,比如售卖商品页面,包括了许多并发访问量很大的数据,它们可以称作是是"热点”数据,这些数据有一个特点,就是更新频率低,读取频率高,这些数据应该尽量被缓存,从而减少请求打到数据库上的机会,减轻数据库的压力。 保证缓存一致性 不更新缓存,而是删除缓存 做缓存不应该是去更新缓存,而是应该删除缓存,然后由下个请求去去缓存,发现不存在后再读取数据库,写入缓存。 原因一:线程安全角度 同时有请求A和请求B进行更新操作,那么会出现 (1)线程A更新了数据库 (2)线程B更新了数据库 (3)线程B更新了缓存 (4)线程A更新了缓存 这就出现请求A更新缓存应该比请求B更新缓存早才对,但是因为网络等原因,B却比A更早更新了缓存。这就导致了脏数据,因此不考虑。 原因二:业务场景角度 有如下两点: (1)如果你是一个写数据库场景比较多,而读数据场景比较少的业务需求,采用这种方案就会导致,数据压根还没读到,缓存就被频繁的更新,浪费性能。 (2)如果你写入数据库的值,并不是直接写入缓存的,而是要经过一系列复杂的计算再写入缓存。那么,每次写入数据库后,都再次计算写入缓存的值,无疑是浪费性能的。显然,删除缓存更为适合。 其实如果业务非常简单,只是去数据库拿一个值,写入缓存,那么更新缓存也是可以的。但是,淘汰缓存操作简单,并且带来的副作用只是增加了一次cache miss,建议作为通用的处理方式。 先操作缓存,还是先操作数据库 那么问题就来了,我们是先删除缓存,然后再更新数据库,还是先更新数据库,再删缓存呢? 对于一个不能保证事务性的操作,一定涉及“哪个任务先做,哪个任务后做”的问题,解决这个问题的方向是:如果出现不一致,谁先做对业务的影响较小,就谁先执行。 假设先淘汰缓存,再写数据库:第一步淘汰缓存成功,第二步写数据库失败,则只会引发一次Cache miss。 假设先写数据库,再淘汰缓存:第一步写数据库操作成功,第二步淘汰缓存失败,则会出现DB中是新数据,Cache中是旧数据,数据不一致。 先删缓存,再更新数据库 该方案会导致请求数据不一致 同时有一个请求A进行更新操作,另一个请求B进行查询操作。 那么会出现如下情形: (1)请求A进行写操作,删除缓存 (2)请求B查询发现缓存不存在 (3)请求B去数据库查询得到旧值 (4)请求B将旧值写入缓存 (5)请求A将新值写入数据库 上述情况就会导致不一致的情形出现。而且,如果不采用给缓存设置过期时间策略,该数据永远都是脏数据。 先更新数据库,再删缓存 不是的。假设这会有两个请求,一个请求A做查询操作,一个请求B做更新操作,那么会有如下情形产生: (1)缓存刚好失效 (2)请求A查询数据库,得一个旧值 (3)请求B将新值写入数据库 (4)请求B删除缓存 (5)请求A将查到的旧值写入缓存 ok,如果发生上述情况,确实是会发生脏数据。 然而,发生这种情况的概率又有多少呢? 发生上述情况有一个先天性条件,就是步骤(3)的写数据库操作比步骤(2)的读数据库操作耗时更短,才有可能使得步骤(4)先于步骤(5)。可是,大家想想,数据库的读操作的速度远快于写操作的(不然做读写分离干嘛,做读写分离的意义就是因为读操作比较快,耗资源少),因此步骤(3)耗时比步骤(2)更短,这一情形很难出现。 补充说明:我用了“先更新数据库,再删缓存”且不设过期时间策略,会不会有问题呢?由于先缓存和更新数据库不是原子的,如果更新了数据库,程序歇逼,就没删缓存,由于没有过期策略,就永远脏数据了。 所以,如果你想实现基础的缓存数据库双写一致的逻辑,那么在大多数情况下,在不想做过多设计,增加太大工作量的情况下,请先更新数据库,再删缓存! 最终一致性强调的是系统中所有的数据副本,在经过一段时间的同步后,最终能够达到一个一致的状态。因此,最终一致性的本质是需要系统保证最终数据能够达到一致,而不需要实时保证系统数据的强一致性 大佬们给出了到达最终一致性的解决思路,主要是针对上面两种双写策略(先删缓存,再更新数据库/先更新数据库,再删缓存)导致的脏数据问题,进行相应的处理,来保证最终一致性。 缓存延时双删 问:先删除缓存,再更新数据库中避免脏数据? 答案:采用延时双删策略。 那么延时双删怎么解决这个问题呢? (1)先淘汰缓存 (2)再写数据库(这两步和原来一样) (3)休眠1秒,再次淘汰缓存 这么做,可以将1秒内所造成的缓存脏数据,再次删除。 那么,这个1秒怎么确定的,具体该休眠多久呢? 针对上面的情形,读者应该自行评估自己的项目的读数据业务逻辑的耗时。然后写数据的休眠时间则在读数据业务逻辑的耗时基础上,加几百ms即可。这么做的目的,就是确保读请求结束,写请求可以删除读请求造成的缓存脏数据。 如果你用了mysql的读写分离架构怎么办? ok,在这种情况下,造成数据不一致的原因如下,还是两个请求,一个请求A进行更新操作,另一个请求B进行查询操作。 (1)请求A进行写操作,删除缓存。 (2)请求A将数据写入数据库了。 (3)请求B查询缓存发现,缓存没有值。 (4)请求B去从库查询,这时,还没有完成主从同步,因此查询到的是旧值。 (5)请求B将旧值写入缓存。 (6)数据库完成主从同步,从库变为新值。 上述情形,就是数据不一致的原因。还是使用双删延时策略。只是睡眠时间修改为在主从同步的延时时间基础上,加几百ms。 采用这种同步淘汰策略,吞吐量降低怎么办? 那就将第二次删除作为异步的。自己起一个线程,异步删除。这样,写的请求就不用沉睡一段时间后了,再返回。这么做,加大吞吐量。 所以在先删除缓存,再更新数据库的情况下,可以使用延时双删的策略,来保证脏数据只会存活一段时间,就会被准确的数据覆盖。 在先更新数据库,再删缓存的情况下,缓存出现脏数据的情况虽然可能性极小,但也会出现。我们依然可以用延时双删策略,在请求A对缓存写入了脏的旧值之后,再次删除缓存。来保证去掉脏缓存。 删缓存失败了怎么办:重试机制 还有一个问题没有考虑到,那就是删除缓存的操作,失败了怎么办?比如延时双删的时候,第二次缓存删除失败了,那不还是没有清除脏数据吗? 解决方案就是再加上一个重试机制,保证删除缓存成功。 方案一: 流程如下所示 (1)更新数据库数据; (2)缓存因为种种问题删除失败 (3)将需要删除的key发送至消息队列 (4)自己消费消息,获得需要删除的key (5)继续重试删除操作,直到成功 然而,该方案有一个缺点,对业务线代码造成大量的侵入。于是有了方案二,在方案二中,启动一个订阅程序去订阅数据库的binlog,获得需要操作的数据。在应用程序中,另起一段程序,获得这个订阅程序传来的信息,进行删除缓存操作。 方案二: 流程如下图所示: (1)更新数据库数据 (2)数据库会将操作信息写入binlog日志当中 (3)订阅程序提取出所需要的数据以及key (4)另起一段非业务代码,获得该信息 (5)尝试删除缓存操作,发现删除失败 (6)将这些信息发送至消息队列 (7)重新从消息队列中获得该数据,重试操作。

优秀的个人博客,低调大师

Hadoop大数据平台环境搭建注意事项,波若分布式数据采集工具功能剖析,数道云

Hadoop大数据作为时代发展的产物,影响着互联网企业发展、以及企业关于品牌形象推广、政府有关民意采集、以及有关数据信息收集分类……………… Hadoop技术的发展,带来了海量数据高效处理的能力,也给互联网政企、高校的发展带来了突破性的发展。互联网的数据容量巨大,以10-100GB或更多,数据种类多种多样。如何利用Hadoop大数据平台实现大数据的快捷处理呢?以及Hadoop环境搭建注意事项分析。 Hadoop大数据平台选择注意事项: 1.环境的部署和搭建 前期准备:网络环境的搭建、Hadoop环境的部署、需要下载或者安装的Hadoop程序、虚拟机、java JDK环境部署…… 2.集群环境部署 (1)电脑配置(适用于多少台虚拟机同时上线) (2)什么样的虚拟机适用于Hadoop集群环境使用 (3)关于大数据平台搭建系统的选择及使用 (4)了解熟悉安装过程 (5)选择适合的安装模式 3.产品的使用及操作 (1)登录管理界面 (2)启动安装导向 (3)设置集群名称 (4)安装选项,输入集群节点主机名列表 (5)根据需求,选择相对应的服务项目 (6)根据步骤,如果有需要,可以进行定制开发选项 (7)安装完成 (8)功能使用测试

优秀的个人博客,低调大师

java版b2b2c社交电商spring cloud分布式微服务-服务提供与调用

上一篇文章我们介绍了eureka服务注册中心的搭建,这篇文章介绍一下如何使用eureka服务注册中心,搭建一个简单的服务端注册服务,客户端去调用服务使用的案例。 案例中有三个角色:服务注册中心、服务提供者、服务消费者,其中服务注册中心就是我们上一篇的eureka单机版启动既可,流程是首先启动注册中心,服务提供者生产服务并注册到服务中心中,消费者从服务中心中获取服务并执行。 服务提供 我们假设服务提供者有一个hello方法,可以根据传入的参数,提供输出“hello xxx,this is first messge”的服务 1、pom包配置 创建一个springboot项目,pom.xml中添加如下配置: <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> 2、配置文件 application.properties配置如下: spring.application.name=spring-cloud-producer server.port=9000 eureka.client.serviceUrl.defaultZone=http://localhost:8000/eureka/ 参数在上一篇都已经解释过,这里不多说。 3、启动类 启动类中添加@EnableDiscoveryClient注解 @SpringBootApplication @EnableDiscoveryClient public class ProducerApplication { public static void main(String[] args) { SpringApplication.run(ProducerApplication.class, args); } } 4、controller 提供hello服务 @RestController public class HelloController { @RequestMapping("/hello") public String index(@RequestParam String name) { return "hello "+name+",this is first messge"; } } 添加@EnableDiscoveryClient注解后,项目就具有了服务注册的功能。启动工程后,就可以在注册中心的页面看到SPRING-CLOUD-PRODUCER服务。 到此服务提供者配置就完成了。 服务调用 1、pom包配置 和服务提供者一致 <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> 2、配置文件 application.properties配置如下: spring.application.name=spring-cloud-consumer server.port=9001 eureka.client.serviceUrl.defaultZone=http://localhost:8000/eureka/ 3、启动类 启动类添加@EnableDiscoveryClient和@EnableFeignClients注解。 @SpringBootApplication @EnableDiscoveryClient @EnableFeignClients public class ConsumerApplication { public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class, args); } } @EnableDiscoveryClient`:启用服务注册与发现 @EnableFeignClients`:启用feign进行远程调用 Feign是一个声明式Web Service客户端。使用Feign能让编写Web Service客户端更加简单, 它的使用方法是定义一个接口,然后在上面添加注解,同时也支持JAX-RS标准的注解。Feign也支持可拔插式的编码器和解码器。Spring Cloud对Feign进行了封装,使其支持了Spring MVC标准注解和HttpMessageConverters。Feign可以与Eureka和Ribbon组合使用以支持负载均衡。 4、feign调用实现 @FeignClient(name= "spring-cloud-producer") public interface HelloRemote { @RequestMapping(value = "/hello") public String hello(@RequestParam(value = "name") String name); } name:远程服务名,及spring.application.name配置的名称 此类中的方法和远程服务中contoller中的方法名和参数需保持一致。 5、web层调用远程服务 将HelloRemote注入到controller层,像普通方法一样去调用即可。 @RestController public class ConsumerController { @Autowired HelloRemote HelloRemote; @RequestMapping("/hello/{name}") public String index(@PathVariable("name") String name) { return HelloRemote.hello(name); } } 到此,最简单的一个服务注册与调用的例子就完成了。 测试 简单调用 依次启动spring-cloud-eureka、spring-cloud-producer、spring-cloud-consumer三个项目 先输入:http://localhost:9000/hello?name=neo检查spring-cloud-producer服务是否正常 返回:hello neo,this is first messge 说明spring-cloud-producer正常启动,提供的服务也正常。 浏览器中输入:http://localhost:9001/hello/neo 返回:hello neo,this is first messge 说明客户端已经成功的通过feign调用了远程服务hello,并且将结果返回到了浏览器。

资源下载

更多资源
腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册