刚说要坚持写博客,这一晃两周过去了...汗啊,今天刚上新版服务,忙里偷闲总结下最近的工作,继续谈谈对缓存的利用吧。
先推荐篇博文,在cnblog上看了篇文章写的不错,关于cache和db之间的关系以及怎么用好两者《Memcache和Mongodb》
http://www.cnblogs.com/lovecindywang/archive/2010/05/19/1739025.html。
这一周大部分工作还是投入在优化缓存设计上面,之前两篇博客分别介绍了我之前对cache的设计由最初的模糊的服务器代理进行分层缓存改进到本地缓存+服务器代理进行分页缓存,但是一直都遗留下一个问题——过滤。
例如对于一个数据源A,Web客户端要能对全量的A进行处理,而Android端只能解析A中一部分数据B,同样IOS端能解析的部分为C,这种情况如何处理?一种方式是写三条查询语句在DAO层控制数据差异,过滤动作由跨表查询代替。当然到这一步对开发在性能和难度上都是可以接受的,然而如果客户端版本迭代,假设Android端的不同版本支持的数据类型也有了差异,老版本支持的数据集为D,新版则支持E,那问题出现了如果还是利用纯sql来处理的话,客户端差异*版本差异将会使DAO层变的更复杂,逻辑层版本和客户端控制就更不必说了。
对于这一问题,我们最终采用了折中的一种方案,客户端差异的区分由SQL跨表查询解决,对于版本迭代差异则通过过滤器来完成。如何实现过滤器以及如何结合缓存,这一问题还是困扰了我一段时间的,下面谈谈我的思路。
之前结合了Attribute和静态解析做了一套缓存代理工具,通过在DAO层接口简单的使用Attribute声明、服务器静态解析利用IL运行期生成代理函数来完成缓存代理(详见上一篇博客
http://gkqcz-126-com.iteye.com/admin/blogs/1717882)。这样的设计首要的原则是代理函数不要有任何侵入性,确保代理函数的签名与被代理的函数一致,也正是因为这个原因让我在引入过滤操作时有了顾虑,如果直接在原接口上引入过滤会不会导致接口被污染?起初我的想法是在DAO层之上的Facade层进行过滤操作,Facade层负责过滤逻辑以及结果组装,而DAO负责从DB中获取数据,分别进行cache,对于需要取过滤数据的则从Facade层取,取全量数据则由DAO层取。不过这样却认为的将同样的取数据操作变成了两个层级,甚至两个层级间缓存也产生了依赖(这一点很糟糕),最后我不得不改变了原来的思路,定义了空过滤修改了原接口。
这是利用缓存工具以及结合过滤生成出代理函数的伪代码(这个示例使用了本地缓存、分页缓存以及过滤):
//被代理的函数
public virtual ListObject<DemoEntity> GetDemoEntity(string condition ,int pageIndex ,int pageSize ,Filter filter)
{
ListObject<DemoEntity> res=DemoEntityDAO.GetDemoEntity(condition)//查询数据库
FillFilterResult()//进行过滤并填充结果,直至满足请求的数据量进行返回,
//对与无需过滤的情况,我定义了一个名为NullFilter的过滤器,返回结果恒为true;
//取出请求部分的数据并返回
}
//动态生成的代理函数
public ListObject<DemoEntity> GetDemoEntity(string condition ,int pageIndex ,int pageSize ,Filter filter)
{
//根据入参的pageIndex、pageSize,计算其对应的缓存所对应的pageIndex/pageSize并进行替换,
//例如假设服务器缓存配置为cache10页每页大小100,若请求pageIndex=3、pageSize=40,则计算后pageIndex=1(对应缓存的起始页),pageSize=100
if (..)//客户端请求的数据范围在缓存范围内
{
do{
ListObject<DemoEntity> result;
string key=string.concat(new Object{..})//根据函数入参以及过滤器的ID生成出的Cachekey
result=cacheManager.GetLocal(key);//查询本地缓存,这类缓存时间设置不要过程,过长会导致各服务器数据不一致
if(result==null)
{
result=cacheManager.Get(key);//当本地缓存失效时,查询memcache
if(result==null)
{
result=base.GetDemoEntity(condition ,pageIndex ,pageSize ,Filter)//缓存失效,进行数据库查询
if(result!=null)
cacheManager.Set(..);//重新写memcache缓存
}
if(result!=null)
{
cacheManager.SetLocal(..)//重新写本地缓存
}
}
}while(..)//如果客户端请求跨页则获取下一缓存页内容,并填充结果
PageFormatUtil.Format(..);//这里取得的数据都是服务器进行代理后的,最后需要取出真正请求的那一部分数据
}
else
return base.GetDemoEntity(...)//这类访问比较少,缓存带来的内存开销不如直接查询数据库
}
动态代码其实只是通过了继承完成了对原函数的代理,完成了对调用者的隐藏(当然写到这都只是我个人的一点小思考和实现,要是大家有更好的设计和实现方式望多多指点啊)。到这里只是完成了对工具构造,今天就先写到这里了,下一篇博客谈谈如何使用过滤器进行版本控制以及另外我们采用的一些小技巧。
PS:再附上一点使用IL Emit的一些容易犯得小错误
1.值类型数据需要通过box才能转会为对象类型,例如new object[]{"str",1},在用IL代码实现时不要忘记先对1进行装箱,同样的在取出去取出值对象的时候也要unbox一下,例如缓存命中返回int类型值时先对返回值unbox后再返回。
2.枚举类型本质上也是值类型哦,这个也要进行box(这个好像是因为枚举值每个对应了从0开始的整数,就是我们定义时候赋的int值)。
欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接。
分享到:
相关推荐
在本范例中,每当过滤条件改变,都会生成新的缓存以替换旧的,保证前端用户看到的是与过滤条件匹配的地图视图。 实现这一功能需要考虑线程管理,以避免更新缓存的过程阻塞主线程,影响用户的地图浏览体验。在Java中...
在本篇文章中,我们将深入探讨如何使用Servlet过滤器来实现缓存机制,以此提高Web应用的性能。 首先,我们需要了解什么是缓存。缓存是一种存储技术,用于临时存储频繁访问的数据,以便快速检索。在Web应用中,通过...
缓存更新涉及到如何保持缓存与数据库数据的一致性。常见的策略包括: - **失效更新**:当数据库中的数据发生变化时,让缓存失效,下次请求时自动重新加载。 - **主动更新**:在更新数据库的同时,同步更新缓存。 ...
在这个案例中,我们关注的是使用C++来处理与Internet Explorer(IE)相关的功能,包括过滤器、缓存管理和主页设置。下面将详细讨论这些知识点。 首先,**C++过滤器**在IE中通常指的是扩展或插件,它们可以增强...
【布隆过滤器详解及其在缓存击穿中的应用】 布隆过滤器是一种空间效率极高的概率型数据结构,用于判断一个元素是否可能在一个集合中。它通过使用多个哈希函数将元素映射到一个固定大小的位数组上,允许存在一定比例...
- **缓存穿透**:防止无效数据的查询,通过布隆过滤器或者预加载机制,确保缓存中存储的数据有效。 - **缓存刷新**:设置定时任务或基于TTL(Time To Live)的自动过期策略,定期检查并更新缓存。 4. **分布式锁*...
**分布式缓存的挑战与优化** 尽管分布式缓存带来了诸多好处,但也面临一些挑战,如缓存一致性、缓存穿透和缓存雪崩等问题。一致性是指更新数据库后,确保缓存中的数据同步更新。缓存穿透是指恶意用户尝试请求不存在...
"springMybatis+redis三级缓存框架"是一个高效且灵活的解决方案,它将MyBatis的二级缓存与Redis相结合,形成一个三级缓存体系,以优化数据读取速度并减轻数据库压力。 首先,MyBatis作为一款轻量级的持久层框架,其...
在大数据时代,面对海量的数据处理和分析,性能与缓存的优化显得至关重要。数据库、页面以及缓存是数据处理流程中的关键环节,它们的优化能够显著提升系统效率,降低延迟,提高用户体验。以下是对这些关键知识点的...
但是,如果我们需要在用户登出时执行额外的操作,如清除特定的缓存数据,我们可以自定义一个过滤器来扩展 Shiro 的 `LogoutFilter` 类。 在 Spring 配置文件 `spring-shiro.xml` 中,我们需要定义一个名为 `shiro...
在高并发的环境下,保持缓存与数据库之间的一致性是一个挑战。缓存一致性问题主要体现在当缓存中的数据与数据库中的数据出现不匹配时,可能导致业务逻辑错误。以下是一些常见策略及其分析: 1. **锁机制**: 锁在...
使用这个工具,你可以轻松地浏览和导出缓存的图片和音频文件,步骤可能包括选择Chrome的缓存目录、过滤特定类型的文件、预览资源,以及选择导出的文件格式和位置。此外,它可能还提供了一些高级功能,比如按时间排序...
解决方案有很多种方法可以有效地解决缓存穿透问题,最常见的则是采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的 bitmap 中,一个一定不存在的数据会被这个 bitmap 拦截掉,从而避免了对底层存储系统的查询...
1. **布隆过滤器**:使用布隆过滤器来存储可能存在的数据键,通过这个过滤器预先判断一个键是否存在,减少无效的数据库查询。 2. **设置空值缓存**:即使数据库中没有对应数据,也将空值放入缓存,设置一个较短的...
### Redis系统学习之缓存穿透、缓存击穿与缓存雪崩的概念及其解决方案 #### 缓存穿透 **概念**: 缓存穿透是指在高并发场景下,大量的请求访问了一个既不在缓存中也不在数据库中的数据。这种情况通常发生在非法...
缓存穿透解决方案通过布隆过滤器和缓存空对象两种方式,有效防止缓存穿透问题。 缓存雪崩解决方案通过设置不同的过期时间、热点数据永不过期、限流降级等手段,防止缓存雪崩。 缓存击穿解决方案通过互斥锁和逻辑...
- **缓存穿透**:防止无效的请求导致缓存失效并直接查询数据库,可以采用布隆过滤器等技术进行预防。 - **缓存雪崩**:避免大量缓存同时过期导致数据库压力激增,可设置合理的过期策略或使用分布式锁来解决。 总的...
保持缓存与数据库的一致性是多级缓存设计的重要考虑因素。常见的一致性模型有强一致性和最终一致性,前者要求每次读取都返回最新数据,后者允许短暂的不一致,但保证最终达到一致。 七、缓存雪崩与穿透 缓存雪崩是...
#### 二、Squid 的硬件需求与配置建议 为了确保 Squid 能够高效稳定运行,推荐的硬件配置包括但不限于: - **内存**:至少 128MB,但为了达到最佳性能,建议内存容量更大。根据实际经验,分配给 Squid 的缓存内存...
- **使用Filter实现对请求结果页面的缓存**:通过过滤器拦截请求并缓存结果。 - **Java对象的缓存**:例如缓存数据库查询结果对象。 **1.2 缓存介质[保存在哪里]** 从硬件介质上来讲,缓存主要分为内存和硬盘两种...