Cache缓存设计
缓存的适用场景:
缓存的目的是提高访问速度,减少不必要的开销,提高性能。那什么样的场景适用于缓存呢。试想一个多项式的计算是一个CPU bound的操作,如果频繁调用同一个多项式的结果。显然缓存结果是一个提高性能的方法。减少了不必要的CPU开销。另外就是提高访问速度。启动的时候,需要加载DB的数据到内存,如果有cache,那么重复get的时候,会优先从缓存获取。显然内存比走IO的db快。
并不是所有的数据都应用于缓存。经常更新的data就不适用于缓存。因为IO的开销没有减少。还要多出维护缓存的额外开销,另外要求强一致的系统也不能使用缓存,因为除非采用P2C,缓存和DB的之间不一致无法避免。
缓存的分类:
- 本地缓存(LRU,hashmap实现的cachebase类)
- 分布式缓存(Redis等)
两者的区分:
- 最重要的是本地缓存在多线程环境中,需要加锁,防止线程竞争. 缓存类的线程安全不同于STL中容器线程安全,因为缓存类提供的接口有限get,delete,set,可以内部封装缓存容器类。缓存单元无需提供线程安全. leveldb中类
LRUCache是一个缓存管理容器,需要提供线程安全,缓存的单元是LRUHandle,无需线程安全. 注意加锁mutex需要会影响性能,但是多线程加锁影响性能的关键是锁的竞争,所以要减少锁的粒度,由此减少竞争的概率.提高性能.Redis 本身是单线程的,所以天然线程安全. - redis支持水平拓展scale up,如果缓存内存过大,可以增加redis cluster模式。而本地内存仅限于单个应用。由于redis支持高可用,本地缓存不支持. 持久化的可以使down 机之后快速热启动. redis的数据结构丰富,针对不同的场景,应用不同类型的数据结构,本地缓存需要自己实现各个场景的cachebase基类.
- 本地缓存需要自己设计多缓存之间相互通知缓存更新的功能。redis天然支持,因为大家共用一份缓存。
- 都无法保证事务.
常见的缓存设计:
1. 本地缓存和DB 2.Redis和DB 3.多层缓存设计(本地缓存,Redis ,DB)
关键问题点:缓存过期的支持,缓存淘汰的支持,缓存更新通知机制的支持。缓存过期就是设置过期时间。每次get缓存的时候,如果超过过期时间,就把缓存删除,这是缓存的惰性删除。还有定期删除,和定时删除,定时删除最为精确,设置定时器,只要缓存过去就能快速的删除,但是需要检测,开销大。redis采用惰性和定期删除的策略。缓存过期的作用结果就是删除过期缓存,节省内存。还有一个关键的作用是让缓存失效。用于保证最终一致性。试想,如果db和redis的数据总是不一致,那么应该怎么办?根据一致性的需求,我们可以提出两个策略:1.一定时间内,一致性得到解决,通过定期去比较db version和redis version. 2.时间不确定,但是可以保证最终一致性。我们设置缓存过期时间。一旦缓存过期,就会删除内存。然后从db取最新的数据.
缓存淘汰的目的更多是在于保证缓存系统在内存不足的条件下能够继续使用。最后是多个缓存之间的更新通知机制,这个保证多缓存之间的一致性。
缓存更新通知机制:
本地缓存如果detect到其他缓存导致的db更新?这需要引入version版本号的概念。cachebase中维护一个m_version的成员变量,代表此id的cache的版本号,同时本地维护一个所有caches的map<id,counter>结构,id表示cache,counter表示版本。这个map更新时机有两处,1. 如果本地cache更新之后,需要increaseCounter那么这个map更新为最新的db值, 2,如果上次更新已经过了T时间,那么也会更新db最小值。 也就是说如果不是本地cache更新,那么map与DB的数据有T时间的mismatch。
m_version在每次getcacheObject的时候比较m_vesion与map[id]是否相等,如果不想等,就应该refresh缓存,从db加载最新的data到本地缓存。如此就实现了本地缓存监测由其他缓存导致的db更新。
关键点:要维护一个version_num的概念。且要考虑多进程条件下,多个本地缓存同时写的情况。因此increaseCounter函数是增加版本号,一定会取db最新的值加1。同时多进程同时写DB的顺序问题有数据库本身处理,可以实现sequence 访问,无需程序员考虑.如果两个进程同时写同一个db table且写入值相同,那么可以让后来的写入失败。
多级缓存的设计:
加入redis作为二级缓存,就构成二级缓存的系统,本地缓存和redis缓存,DB三者的同步更新更加复杂。因为db和redis之间的无法保证事务,需要分布式事务。副作用比本地和db之间大。缓存更新的策略通常包两种情况:
- 先删除缓存,再更新数据库。
- 先更新数据库,再删除缓存。 这两种情况在业界,大家对其都有自己的看法。具体怎么使用还得看各自的取舍。当然肯定会有人问为什么要删除缓存呢?而不是更新缓存呢?你可以想想当有多个并发的请求更新数据,你并不能保证更新数据库的顺序和更新缓存的顺序一致,那就会出现数据库中和缓存中数据不一致的情况。所以一般来说考虑删除缓存。
- 如果采用redis version和db version的策略,那么就可以用更新db,更新redis的策略,因为可以定期通过版本号保证一致性,毕竟之前的策略只是降低不一致性的可能性,但是不能完全避免。需要兜底,要么定期过期,要么通过version扫表
一种设计:

更新的步骤:
Cache缓存设计的更多相关文章
- LeetCode题解: LRU Cache 缓存设计
LeetCode题解: LRU Cache 缓存设计 2014年12月10日 08:54:16 邴越 阅读数 1101更多 分类专栏: LeetCode 版权声明:本文为博主原创文章,遵循CC 4 ...
- 一种小型后台管理系统通用开发框架中的Cache缓存设计
本篇博客记录一下我在实习的公司的后台管理系统开发框架中学习到的一种关于网站的缓存(Cache)的实现方法,我会在弄懂的基础上,将该方法在.net core上进行实现.因为公司开发都是基于.net fr ...
- CYQ.Data V5 分布式自动化缓存设计介绍
前方: 其实完成这个功能之前,我就在思考:是先把想法写了来,和大伙讨论讨论后再实现,还是实现后再写文论述自己的思维. 忽然脑后传来一个声音说:你发文后会进入发呆阶段. 所以还是静下心,让我轻轻地把代码 ...
- nginx+Memcached 缓存设计
单页面缓存方案 单静态页缓存 解决问题场景 常见的缓存设计利用System.Web.Cache 保存在内存内,效率高,可以减轻数据库访问的压力.但是Web除了获取数据之外,还有呈现页面渲染,生成HTM ...
- 注释驱动的 Spring cache 缓存介绍
概述 Spring 3.1 引入了激动人心的基于注释(annotation)的缓存(cache)技术,它本质上不是一个具体的缓存实现方案(例如 EHCache 或者 OSCache),而是一个对缓存使 ...
- [转]注释驱动的 Spring cache 缓存介绍
原文:http://www.ibm.com/developerworks/cn/opensource/os-cn-spring-cache/ 概述 Spring 3.1 引入了激动人心的基于注释(an ...
- paip.cache 缓存架构以及性能提升总结
paip.cache 缓存架构以及性能提升总结 1 缓存架构以及性能(贯穿读出式(LookThrough) 旁路读出式(LookAside) 写穿式(WriteThrough) 回写式 ...
- 注释驱动的 Spring cache 缓存介绍--转载
概述 Spring 3.1 引入了激动人心的基于注释(annotation)的缓存(cache)技术,它本质上不是一个具体的缓存实现方案(例如 EHCache 或者 OSCache),而是一个对缓存使 ...
- CYQ.Data V5 分布式自动化缓存设计介绍(二)
前言: 最近一段时间,开始了<IT连>创业,所以精力和写的文章多数是在分享创业的过程. 而关于本人三大框架CYQ.Data.Aries.Taurus.MVC的相关文章,基本都很少写了. 但 ...
随机推荐
- fastDFS配置及日志查看 所遇到的问题
FastDFS的配置文件在/usr/local/webserver/fastdfs/etc目录下,其中包括 client.conf 客户端上传配置文件 storage.conf 文件存储服 ...
- p2944 [USACO09MAR]地震损失2Earthquake Damage 2
传送门 分析 我们让s到1,关键点到t分别连流量为inf的边 于是我们可以考虑跑s到t的最小割 于是我们将所有点拆为两个点,关键点和1的两个点之间连inf,其余点连1 将原图的边也连上,流量为inf ...
- Python之FTP传输-乾颐堂
访问FTP,无非两件事情:upload和download,最近在项目中需要从ftp下载大量文件,然后我就试着去实验自己的ftp操作类,如下(PS:此段有问题,别复制使用,可以参考去试验自己的ftp类! ...
- 关于如何在服务器上搭建tomcat并发布自己的web项目
最近在学习如何在服务起上搭建tomcat,并发布自己的项目,自己是花了一下午的时间才把里面的东西弄明白,各种百度,各种请教大神,真的是备受折磨啊.好了废话不多说,直接进入主题. 1:众所周知,tomc ...
- 从零开始学习前端JAVASCRIPT — 8、JavaScript基础COOKIE
1:通信协议 定义:通信协议是指双方实体完成通信或服务所必须遵循的规则和约定.协议定义了数据单元使用的格式,信息单元应该包含的信息与含义,连接方式,信息发送和接收的时序,从而确保网络中数据顺利地传送到 ...
- 在Chrome 39中无法使用插件
在chrome 42+版本中在开启npapi选项. 1.打开插件面板,在地址栏中输入 chrome://plugins 2.找到npScreenCapture插件,点击始终允许选框 3允许控件
- WINAPI和APIENTRY是一样的
今天写线程函数时,发现msdn中对ThreadProc的定义有要求:DWORD WINAPI ThreadProc(LPVOID lpParameter); 不解为什么要用WINAPI宏定义,查了后发 ...
- Python 操作 Excel 、txt等文件
#xlrd 读取excel import xlrd import os #获取文件路径 filepath = os.path.join(os.getcwd(),'user_info') #获取文件名称 ...
- c语言和java以及安卓和苹果
苹果手机是本地,没有中间环节,速度快,基于Linux系统 安卓是通过虚拟机,影响速度 就像c语言和java c适用于架构小的地方,因为直接编译运行 而java用于架构比较大的地方,启动慢,启动之后效率 ...
- 读取IE缓存文件
使用WebCacheTool项目中的WinInetAPI.cs和Win32API.cs两个类 /// <summary> /// 获取IE缓存文件 /// </summary> ...