`
uule
  • 浏览: 6351693 次
  • 性别: Icon_minigender_1
  • 来自: 一片神奇的土地
社区版块
存档分类
最新评论

缓存总结(二)

 
阅读更多

 

四种套路更新缓存

实时刷新缓存 

分布式之数据库和缓存双写一致性方案解析(重要)

 

 

分类

 

本地缓存(HashMap/ConcurrentHashMap、Ehcache、Guava Cache等),

缓存服务(Redis/Tair/Memcache等)。

 

使用场景

 

什么情况适合用缓存?考虑以下两种场景:

1、短时间内相同数据重复查询多次且数据更新不频繁,这个时候可以选择先从缓存查询,查询不到再从数据库加载并回设到缓存的方式。此种场景较适合用单机缓存。

2、高并发查询热点数据,后端数据库不堪重负,可以用缓存来扛。

 

选型考虑

如果数据量小,并且不会频繁地增长又清空(这会导致频繁地垃圾回收),那么可以选择本地缓存。具体的话,如果需要一些策略的支持(比如缓存满的逐出策略),可以考虑Ehcache;如不需要,可以考虑HashMap;如需要考虑多线程并发的场景,可以考虑ConcurentHashMap。

其他情况,可以考虑缓存服务。目前从资源的投入度、可运维性、是否能动态扩容以及配套设施来考虑,我们优先考虑Tair。除非目前Tair还不能支持的场合(比如分布式锁、Hash类型的value),我们考虑用Redis。

 

设计关键点

 

什么时候更新缓存?如何保障更新的可靠性和实时性?

 

更新缓存的策略,需要具体问题具体分析。这里以门店POI的缓存数据为例,来说明一下缓存服务型的缓存更新策略是怎样的?目前约10万个POI数据采用了Tair作为缓存服务,具体更新的策略有两个:

1、接收门店变更的消息,准实时更新。

2、给每一个POI缓存数据设置5分钟的过期时间,过期后从DB加载再回设到DB。这个策略是对第一个策略的有力补充,解决了手动变更DB不发消息、接消息更新程序临时出错等问题导致的第一个策略失效的问题。通过这种双保险机制,有效地保证了POI缓存数据的可靠性和实时性。

 

缓存是否会满,缓存满了怎么办?

 

对于一个缓存服务,理论上来说,随着缓存数据的日益增多,在容量有限的情况下,缓存肯定有一天会满的。如何应对?

 

① 给缓存服务,选择合适的缓存逐出算法,比如最常见的LRU。

② 针对当前设置的容量,设置适当的警戒值,比如10G的缓存,当缓存数据达到8G的时候,就开始发出报警,提前排查问题或者扩容。

③ 给一些没有必要长期保存的key,尽量设置过期时间

 

缓存是否允许丢失?丢失了怎么办?

 

根据业务场景判断,是否允许丢失。如果不允许,就需要带持久化功能的缓存服务来支持,比如Redis或者Tair。更细节的话,可以根据业务对丢失时间的容忍度,还可以选择更具体的持久化策略,比如Redis的RDB或者AOF。

 

缓存被“击穿”问题

 

对于一些设置了过期时间的key,如果这些key可能会在某些时间点被超高并发地访问,是一种非常“热点”的数据。这个时候,需要考虑另外一个问题:缓存被“击穿”的问题。

概念:缓存在某个时间点过期的时候,恰好在这个时间点对这个Key有大量的并发请求过来,这些请求发现缓存过期一般都会从后端DB加载数据并回设到缓存,这个时候大并发的请求可能会瞬间把后端DB压垮。

 

如何解决:业界比较常用的做法,是使用mutex(互斥)。简单地来说,就是在缓存失效的时候(判断拿出来的值为空),不是立即去load db,而是先使用缓存工具的某些带成功操作返回值的操作(比如Redis的SETNX或者Memcache的ADD)去set一个mutex key,当操作返回成功时,再进行load db的操作并回设缓存;否则,就重试整个get缓存的方法。

类似下面的代码:

public String get(key) {
    String value = redis.get(key);      
	if (value == null) { //代表缓存值过期
          //设置3min的超时,防止del操作失败的时候,下次缓存过期一直不能load db
			if (redis.setnx(key_mutex, 1, 3 * 60) == 1) {  //代表设置成功
				//从数据库获取
				value = db.get(key);
                redis.set(key, value, expire_secs);
                redis.del(key_mutex);
            } else {  
				//这个时候代表同时候的其他线程已经load db并回设到缓存了,这时候重试获取缓存值即可
                sleep(50);
                get(key);  //重试
            }
    }else {
		return value;      
    }
}

 

setnx 赋值判断原值是否存在,存在不赋值,返回0;不存在才赋值,返回1

setnx name Tom  ---返回值:0,因为name的原有value为zlh,存在值则不赋值。

get name  ---返回值:zlh,因为有值,故上面赋值为tom失败,返回0。

 

setnx phone 18501733702   ---返回值:1,赋值成功,因为原来不存在phone的key与value。

get phone   ---返回值:18501733702,说明上面的setnx赋值成功。

 

。。。

==================================================================

说说通用的缓存策略,有两种,下面来点图



 


 

 第一种方案,客户端使用的比较多,缓存和 DB(或者文件)同步更新,服务端一般都是用第二种方案。

 

=====================================================================

那些年使用缓存踩过的坑--缓存更新策略

https://my.oschina.net/percylee/blog/903295

今天讲的这个话题,我相信是众多工程师和团队的痛。从我刚开始工作,那时候构建本地缓存,到后续memcache, Redis的出现,到现在各种分布式集群的缓存,例如redis Cluster等产品的出现,缓存越来越发达和复杂了,缓存对我们的系统也越发重要,现在很难相信一个后端服务里没有缓存的存在。在这篇文章里,我会和大家分享一下过去踩到的缓存坑,然后试图给出一些解决方案,大家可以一起讨论,最终拿出更好的方法。由于篇幅有限,所以这里的缓存讨论,只局限于后端服务的缓存,并且不涉及具体的框架,对于H5,iOS和Android等前端缓存的讨论,会在以后的文章里呈现出来。

 

案例1,缓存和DB的同步更新不在同一个事务里并且没有重试补偿机制

 

为了减少系统间的依赖,不同系统的数据更新往往不放在同一个事务里,采用MQ来进行通信。大家可以看下图,后台系统CRM更新产品数据到DB,Product系统收到异步消息通知后,更新最新数据到缓存。这是一个最常见的缓存应用场景,我相信很多团队都是这样用的。在这个Case里容易出现的问题在于,如果批处理任务收到消息后服务crash掉了,缓存没有正常更新,就出现了与DB的数据不同步,前端系统一直不能读到最新数据,导致业务异常

  

解决方案:

1.  失败消息一定要建立一定时间间隔的重试机制

2.  系统要有缓存更新的报警机制,方便更新失败或者重试超时后,可以人工介入进行补偿。

 

 

案例2, 同一数据被1个以上的服务执行写操作,其中一个服务的缓存数据没有版本控制

 

这也是两个不同服务更新数据过程中很常见的情况,见下图,CRM系统更新了某个用户的Profile, 保存更新数据库后,通过MQ通知用户系统更新缓存,由于是异步更新延迟,在缓存更新前,用户系统收到前端的指令,读取了当前缓存里的用户数据,做了修改,并更新到DB中。出现的结果就是数据库里的CRM的更新被错误覆盖。

  

解决方案:

缓存里的数据有一个标志位可以作为更新数据库数据的依据(Update_time or Version), 如果缓存里数据时间与数据库时间不能匹配,意味着另外一个服务更新了该数据,那么就先从DB里读取最新数据版本,然后在新版本上提交数据。

  

案例3, 并发查询缓存中同一数据,如果缓存没命中,导致DB瞬时被打爆做促销活动的时候,存在大量用户的并发访问某一个特定商品,该商品数据缓存失效,或者做了数据更改,但是对应缓存还没有更新,那么所有这些访问将同时直接被作用到DB上。

  

解决方案:

做一个计数器或者锁(没有特别复杂逻辑的话,可以直接用HashMap),如果发现某个KEY缓存没有命中,那么在计数器+1, 然后访问数据库,拿到结果更新缓存,清理掉计数器中的key。 在这个过程中,如果有第二个线程或者更多的线程需要访问这个KEY时,发现计数器的值>1 或者被加锁, 那么wait, 直到计数器清理掉,当然,这个技术器阈值是可以在配置文件里配置的,不一定是1。

  

案例4, 缓存没有设置默认值,被攻击,缓存一直保持在被“穿透”状态

这个情况,和案例3比较类似,都是缓存无法命中,但不一样的地方在于,数据的KEY值是无法控制的所以没法简单的用计数器和锁来处理, 比方,被人为攻击,制造的大量的无效userID访问。

 

解决方案:

所有没有在缓存的KEY,全部分配一个默认VALUE “UNKOWN-KEY” ,具体是什么情况下,将默认值分配给没有命中的KEY, 这个可以根据自己的业务系统来定,比方说,可以根据特定的IP段,或者没有命中的总次数等,然后我们就可以决定是否继续访问DB还是直接返回默认值给前端,拒绝本次数据访问。这种做法的核心在于,每次数据访问,都会有缓存结果返回,根据系统的情况来决定是否要进一步访问DB。

  

总结,今天列举的这几个案例,归纳起来,可以总结为以下几点:

 

1. 保证缓存同步

2. 减少缓存并发

3. 杜绝缓存穿透

  

缓存与背后的DB是相互依存的关系,缓存系统的设计原则,就是将访问的异常处理或者压力尽可能的前置处理掉,将DB还原成它最初本来的存储功能

 

 

 

 

 

  • 大小: 162.6 KB
  • 大小: 217.7 KB
  • 大小: 152 KB
分享到:
评论

相关推荐

    Hibernate 二级缓存 总结整理

    **Hibernate 二级缓存总结整理** 在Java的持久化框架中,Hibernate是一个广泛使用的ORM(对象关系映射)工具,它极大地简化了数据库操作。在处理大数据量或高并发的场景下,为了提高性能和减少数据库负载,...

    hibernate一级缓存和二级缓存的区别与联系

    总结来说,Hibernate 的一级缓存和二级缓存都是为了提高数据访问效率,但它们在范围和并发控制方面有所不同。一级缓存是事务级别的,保证了数据的强一致性,而二级缓存提供了更多的灵活性,可以跨事务共享,但需要...

    hibernate一级缓存、二级缓存和查询缓存

    **hibernate一级缓存、二级缓存和查询缓存** 在Java的持久化框架Hibernate中,缓存机制是提高应用程序性能的关键要素。缓存能够减少数据库的访问次数,提高数据读取速度,并且在一定程度上降低了系统的负载。本文将...

    Hibernate-二级缓存总结 开发技术 - Java.zip

    文档"Hibernate_二级缓存总结 开发技术 - Java.doc"可能详细阐述了如何配置和使用Hibernate的二级缓存,包括配置文件的设置、缓存提供者的选用、实体和集合的缓存配置,以及实战中的优化技巧。阅读该文档将有助于...

    Hibernate_二级缓存总结

    **Hibernate 二级缓存详解** 缓存是一种提升应用程序性能的技术,它通过将常用数据存储在内存中,减少了对持久层数据库的访问,从而提高系统响应速度。在Hibernate框架中,缓存主要分为一级缓存和二级缓存。 **1. ...

    MyBatis缓存(一级缓存、二级缓存)

    总结来说,MyBatis的一级缓存和二级缓存是提高数据库操作效率的有效工具。一级缓存适用于单个SqlSession内的重复查询,而二级缓存则能在更广的范围内复用数据。然而,在使用缓存时,开发者必须考虑数据一致性、缓存...

    Hibernate一级缓存和二级缓存

    标题“Hibernate一级缓存和二级缓存”指的是Hibernate框架中的两种缓存机制,它们是提高数据访问性能的关键要素。一级缓存是Session级别的,而二级缓存是SessionFactory级别的,两者在数据库操作中起到了重要的作用...

    Hibernate二级缓存

    总结来说,Hibernate的二级缓存和查询缓存是提升性能的重要手段,但正确配置和使用它们至关重要,以免带来不必要的性能损失。通过合理的缓存策略和配置,可以有效地减少数据库交互,提升应用的响应速度。

    hibernate 二级缓存详解

    总结来说,Hibernate的二级缓存是提升性能的关键手段,通过合理配置和使用,可以有效减少数据库交互,提高应用响应速度。需要注意的是,二级缓存虽然强大,但也需谨慎使用,防止数据一致性问题,尤其是在高并发环境...

    php缓存技术总结

    二、页面部分缓存 页面部分缓存针对的是动态页面中的某些部分,它缓存不变的数据片段,而动态变化的数据实时更新。这可以通过类似输出缓冲技术或其他页面片段缓存技术(例如ESI)实现。ESI技术允许缓存动态内容的...

    mybatis一二级缓存

    在 MyBatis 中,一级缓存和二级缓存是两个重要的性能优化手段,它们可以有效减少对数据库的访问,提高系统的响应速度。下面将详细阐述这两个缓存机制。 ### 一级缓存 一级缓存是 MyBatis 默认开启的本地会话缓存,...

    hibernate一级和二级缓存配置与详解

    本篇将深入探讨Hibernate的一级缓存和二级缓存,以及查询缓存的配置和使用。 ### 一级缓存 一级缓存是Hibernate默认提供的缓存,它是Session级别的,每个Hibernate Session都有一个私有的、本地的一级缓存。当我们...

    图片二级缓存

    总结,图片二级缓存是提高应用性能的重要手段,通过合理利用内存和磁盘资源,实现了快速、高效的图片加载。通过深入研究"图片二级缓存demo",开发者能够掌握这一关键技术,并将其应用于实际项目,提升应用的性能和...

    二级缓存配置

    ### 二级缓存配置 #### 一、概述 在软件开发过程中,为了提高系统的性能与响应速度,常常会采用缓存技术来存储频繁访问的数据。其中,Hibernate作为一款流行的Java持久层框架,提供了多种级别的缓存支持,包括一级...

    mybatis二级缓存学习

    总结,MyBatis二级缓存是一项实用的性能优化技术,但使用时需注意其工作原理和潜在的问题,合理配置和管理才能发挥最大效能。通过理解和实践,我们可以更好地掌握这一工具,提高应用的运行效率。

    hibernate缓存总结

    本篇将详细总结Hibernate的缓存机制,包括一级缓存、二级缓存和查询缓存,并探讨相关的优化策略。 一、Hibernate一级缓存 一级缓存是Hibernate内置的Session级别的缓存,它默认开启且不可关闭。当对象被加载到...

    hibernate二级缓存(包括注解方式)

    **标题与描述解析** ...总结来说,本主题涵盖了如何在Hibernate中使用注解配置二级缓存,以及EhCache作为二级缓存提供商的具体配置和使用。理解并熟练掌握这些知识点对于开发高效、稳定的Java企业级应用至关重要。

    配置EhCache二级缓存

    ### 配置EhCache二级缓存 #### 一、简介 EhCache是一个高性能、易于使用的开源缓存系统,最初由 Terracotta 组织开发。它支持多种缓存模型,包括本地缓存和分布式缓存。由于其简单易用且功能强大,EhCache 成为了 ...

Global site tag (gtag.js) - Google Analytics