`
答案在风中
  • 浏览: 65541 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

缓存与分页

阅读更多
    最近再做服务端缓存性能的优化,上一篇《分布式缓存下缓存优化设计方案》http://gkqcz-126-com.iteye.com/admin/blogs/1705695只是粗略的谈了谈对于设计的思路,最近在原设计上又做了一些优化,仅当抛砖引玉(主要参考资料详见上一篇文章)。

    上一篇文章里我提到了两种优化方案,一种是使用本地缓存、另一种是分级缓存。这里谈一谈原设计的缺陷,分级缓存中我提出来通过确定两个不同size的缓存块来缓存两种级别的数据,这里带来一些问题:size的大小难以确定、为了避免边界问题大缓存数据包含了小缓存数据这就带来了缓存数据的冗余(这背离了我们设计的初衷)。针对这些问题我们又在原有基础上结合了应用场景的特殊性修改分级缓存为分页缓存(因为对数据列表的访问往往都是伴随分页需求的),将数据库中原始数据中较常使用部分按照固定大小的页进行缓存,服务端根据客户端分页的数据请求到相应的缓存页内查找数据进行填充。采用分页缓存一方面解决了缓存数据冗余的问题,也不用关注分级的边界,虽然相比分级缓存,分页的内容要零散一些,但是总体上而言灵活性要更高。这里谈谈为什么采用固定大小页进行缓存而不是按照客户端分页请求来缓存结果?如果服务端根据客户端分页请求进行缓存这种耦合关系会导致缓存命中率的下、降性能降低,特别是多类型客户端就更糟糕了。按照固定大小页进行缓存类似与MVC模式中将处理逻辑与显示逻辑解耦的思想,服务端的缓存不要依赖客户端,一方面提高了缓存命中率同时也为缓存清理提供了遍历。

    下面是我使用IL动态生成的一个Demo反编译后的代码(这里针对了同时启用本地缓存和分页缓存的情况,还支持分页缓存无本地缓存、仅进行memcache缓存,这里就不加赘述了),可读性不高不想看直接pass吧。
public override ListObject<DemoEntity> GetList(int num5, int num6, int num1, int num4)
{
    ListObject<DemoEntity> local;
    int num = num1;
    int num2 = num4;
    int num3 = ((num1 - 1) * num4) % 100;
    num1 = (((num1 - 1) * num4) / 100) + 1;//计算缓存页对应的页码和页大小
    num4 = 100;
    if (num1 > 10)//不在缓存页内直接进行数据库查询
    {
        return base.GetList(num5, num6, num, num2);
    }
    ListObject<DemoEntity> obj3 = new ListObject<DemoEntity>();
    do
    {
        string str = string.Concat(new object[] { "DemoCachekeyName", "|", num5, "|", num6, "|", num1, "|", num4 });//根据客户端分页请求计算出对应的cachekey
        local = CacheManager.get_Instance().GetLocal(str) as ListObject<DemoEntity>;//LocalCache的访问
        if (local == null)
        {
            DateTime time;
            local = CacheManager.get_Instance().Get(str) as ListObject<DemoEntity>;//访问memcache
            if (local == null)
            {
                local = base.GetList(num5, num6, num1, num4);
                if (local != null)
                {
                    time = DateTime.Now.AddSeconds(3600.0);
                    CacheManager.get_Instance().Set(str, local, time);
                }
            }
            if (local != null)
            {
                time = DateTime.Now.AddSeconds(100.0);
                CacheManager.get_Instance().SetLocal(str, local, time);
            }
        }
        num1++;
    }
    while (((num1 <= 10) && (local != null)) && PageFormatUtil.FillResult<DemoEntity>(obj3, num2, num3, local));//填充结果集
    if (obj3.totalCount == 0)
    {
        return null;
    }
    return obj3;
}


(下次再完善这里的IL代码的流程图,一直想在缓存结果中再织入进一些过滤操作思前想后没想到如何在不污染原有接口的前提下实现,正在努力中...)

PS:关于IL代码编写:IL代码因为是一种中间代码可读性不是很高,所以进行IL编码其实还是有一点难度的(学习IL编码可以参看《IL Emit学习之旅》一问)。我简单谈谈我在编写IL代码中遇到的一些小问题和自己总结的一些技巧。
    1.先编写c#代码的demo,再参照其IL指令,先完成代码框架,在进一步编码。在IL编码前可以先写一个目标生成的动态代码,再通过参照其IL代码进编码,先用IL写出的主体逻辑(即if else、for、while等),再进一步完善。这样逐步编码查错和编码效率都相对高一点。
    2.什么时候用“_S”,IL代码中为了缩减指令长度对于某些同一操作提供了两种指令实现,比如无条件跳转有Br、Br_S,有时候使用Br_S跳转目标地址会被截断导致程序出错。我个人觉得可以先在可能出现这类情况的地方使用不带“_S”的指令,待动态代码生成后查看其IL代码,再对指令进行优化。
关于泛型函数的反射:
    IL代码中常常需要调用函数,这就需要使用到反射(第一次生成动态代码时的反射对整体的性能影响还是可以接受的)。泛型函数的反射还是稍稍有些绕的:
//目标函数public static bool FillResult<T>(...)
MethodInfo fillResult= typeof(PageFormatUtil).GetMethod("FillResult");
fillResult=fillResult.MakeGenericMethod(info.ReturnParameter.ParameterType.GetGenericArguments()[0]);//info.ReturnParameter.ParameterType.GetGenericArguments()获取函数返回结果中泛型参数的信息,MakeGenericMethod之后才是真正完成了泛型函数的反射

//public class ListObject<T>{...}
reflectType=typeof(ListObject<>).MakeGenericType(info.ReturnParameter.ParameterType.GetGenericArguments()[0]);
reflectConstruct=reflectType.GetConstructor(new Type[]{});


End:
   作为一个刚工作的大学生,自身存在着很多不足,所以博客中有不足之处希望大家指出。自己写博客分享是一方面,另一方面更多的是想勉励自己多学习多积累。因为很大程度上我也只是一个初学者,分享博客更多的能站在初学者的角度写,另一方面博客中不足和错误也能给像我一样的年轻的程序员一些借鉴。 睡了,大家晚安~

欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接。
1
0
分享到:
评论
10 楼 答案在风中 2013-09-18  
youjianbo_han_87 写道
你这种按照key规则来进行缓存分页有太大的局限性了。换个业务需求和场景,你就得换个实现。

有段时间没登自己的blog了,之前使用的cachekey规则是:自定义keyname+被代理的函数入参。要说这个设计没有漏洞肯定是不对的,使用字符串拼接必然存在两个不同的业务函数拼接出了相同的cachekey的可能性,所以这个“自定义keyname”的使用就需要谨慎了。
例如:
[CacheMethod("Test1")]
public int test(int i){..}
Test1为自定义keyname,经过代理后,缓存key应该是“Test1|函数入参i的值”
[CacheMethod("Test2")]
public string test(int i){..}
上面两个业务函数分别使用了两个不同的keyname,从一定程度上避免了因为缓存key重复导致缓存的读写异常(int和string类型无法兼容)。
再看一个例子:
[CacheMethod("Test1")]
public int test(int i){..}
[CacheMethod("Test1")]
public int test2(int i){..}
如果定义了这样两个业务函数,希望复用缓存结果。这种情况不能说完全没有意义,不过定义两个“签名”相同(这里仅指returntype、paramtertypes)甚至业务数据都能复用的函数的确是一个不好的设计。
说到这,其实有很重要一点,对keyname的约束应该在编译期或者尽早进行检查,避免可能的缓存混乱的问题。
这里的实现并不是最优的设计,近期我正在使用java来重写这给个cache代理,有什么好的idea欢迎分享~
9 楼 youjianbo_han_87 2013-09-09  
你这种按照key规则来进行缓存分页有太大的局限性了。换个业务需求和场景,你就得换个实现。
8 楼 答案在风中 2012-11-07  
KA兔 写道
可以在后台起一个进程,从数据库中读出数据后直接分好页,然后再按每页的信息存入数据库,这样前端直接取某页的缓存即可

谢谢关注~
7 楼 答案在风中 2012-11-07  
wingsrao 写道
支持楼主继续学习分享。

谢谢支持
6 楼 答案在风中 2012-11-07  
KA兔 写道
KA兔 写道
可以在后台起一个进程,从数据库中读出数据后直接分好页,然后再按每页的信息存入数据库,这样前端直接取某页的缓存即可

然后再按每页的信息存入缓存,比如事先从数据20页的内容,再将这20页的数据分别存入缓存

不知道我理解的有没有偏差呀。“比如事先从数据20页的内容”这样是指数据分块存储吗?这个优化是有的。如果仅仅是为了存放要缓存的信息来创建一张表,在缓存到期的时候,进程启动更新数据(这个过程伴随着查询插入)可能会造成缓存击穿。另外如果已经使用了分页缓存为何还要再建立一张表来做物理存储呢?呵呵,不知道理解的意思对不对哦。
5 楼 答案在风中 2012-11-07  
zhongmin2012 写道
怎么处理缓存中的脏数据呢?

这也是我头疼的东西,没想到什么在不污染原有接口的前提下带入过滤操作。。。
4 楼 zhongmin2012 2012-11-07  
怎么处理缓存中的脏数据呢?
3 楼 KA兔 2012-11-07  
KA兔 写道
可以在后台起一个进程,从数据库中读出数据后直接分好页,然后再按每页的信息存入数据库,这样前端直接取某页的缓存即可

然后再按每页的信息存入缓存,比如事先从数据20页的内容,再将这20页的数据分别存入缓存
2 楼 KA兔 2012-11-07  
可以在后台起一个进程,从数据库中读出数据后直接分好页,然后再按每页的信息存入数据库,这样前端直接取某页的缓存即可
1 楼 wingsrao 2012-11-07  
支持楼主继续学习分享。

相关推荐

    基于ASP.NET缓存与分页策略优化Web数据查询性能

    ### 基于ASP.NET缓存与分页策略优化Web数据查询性能 #### 概述 随着互联网技术的快速发展和Web应用的普及,提高Web数据查询性能成为了提升用户体验的关键因素。传统的分页查询方法虽然能够一定程度上减轻数据库的...

    Hibernate二级缓存+分页功能

    一级缓存的生命周期与Session相同,当Session关闭时,一级缓存中的数据也会被清除。 二级缓存则是SessionFactory级别的缓存,它可以跨Session共享数据,提供全局性的缓存服务。它分为进程内缓存(如EHCache)和...

    可缓存的分页控件

    "可缓存的分页控件"是一个特定类型的分页组件,其设计目的是优化性能,通过缓存数据来减少对数据库的访问频率,从而提高系统的响应速度。 首先,我们来看一下`PageControls.ascx`和`PageControls.ascx.cs`这两个...

    分页缓存

    在IT行业中,分页缓存是一种优化大数据量查询性能的技术,尤其在Web应用程序中非常常见。它通过将数据库查询结果存储在内存缓存中,减少对数据库的直接访问,从而提高系统响应速度和用户体验。本篇文章将深入探讨...

    ajax的分页分页请求,通过ajax来分页

    6. **缓存与分页组件**:考虑缓存已加载的页面,减少重复请求。同时,可以封装一个通用的分页组件,方便在多个地方复用。 综上所述,Ajax分页请求是Web开发中的常见实践,通过合理运用Ajax和相关技术,可以构建出...

    servlet使用MVC设计模式实现cookie缓存和分页显示数据

    本示例中,我们探讨的是如何利用Servlet结合MVC(Model-View-Controller)设计模式来实现cookie缓存以及分页显示数据,这两项功能在实际Web应用中非常常见。 **MVC设计模式**: MVC模式是软件工程中的一种架构模式...

    另类的缓存分页并读取过程先读取缓存数据

    本文将详细探讨“另类的缓存分页并读取过程”,以及如何先读取缓存数据来优化性能。 首先,我们要理解缓存的基本原理。缓存是一种存储层次结构,它位于主存和CPU之间,存储最近频繁使用的数据副本。当CPU需要数据时...

    案例实战-SpringBoot整合Redis实现缓存分页数据查询

    在本案例实战中,我们将探讨如何使用Spring Boot与Redis集成,以实现缓存分页数据查询,提升应用程序的性能和响应速度。Spring Boot是Java领域一个流行的微服务框架,它简化了Spring应用的初始搭建以及开发过程。而...

    hibernate分页例子.rar

    5. **缓存与分页**: 考虑到性能,你可能需要使用Hibernate的二级缓存来存储已分页的数据。但是,注意缓存和分页的配合使用可能导致数据不一致,因为缓存中的数据可能会被其他事务修改。 6. **性能优化**: 在...

    Android 分页缓存图片加载器,gridview显示

    本项目针对这种情况,利用Android Studio进行开发,实现了分页缓存图片加载器,旨在提高图片加载速度并优化内存管理。 1. **Android GridView** GridView是一种可以显示多列数据的视图,常用于展示图片或列表项。...

    三,MyBatis动态SQL,缓存和分页插件, Lombok工具

    通过简单的配置,PageHelper能够与各种数据库兼容,提供参数化分页、复杂条件分页等功能。使用PageHelper可以避免手动编写分页SQL,提高开发效率,同时保持代码的简洁性和可维护性。 4. **Lombok工具**: Lombok是...

    SpringBoot+MybatisPlus+Redis,涉及分页、缓存等

    代码主要涉及SpringBoot框架搭建,以及MP分页和Redis缓存使用,内容详解可见博主的博客,如有疑问或建议,可以博文留言或加Q群:583138104交流讨论

    spring+ibatis+oracle分页缓存源码

    在IT行业中,数据库分页和缓存是两个关键的概念,特别是在构建高性能的Web应用程序时。Spring、iBatis和Oracle的结合提供了强大的数据处理能力。本文将深入探讨这些技术如何协同工作,实现高效的分页缓存策略。 ...

    PythonWeb从入门到实战教程之10Ajax会话、日志、缓存、分页.zip

    在本教程中,我们将深入探讨Python Web开发的关键概念,包括Ajax、会话管理、日志记录、缓存以及分页。这些技术对于构建高效、动态和用户友好的Web应用程序至关重要。 1. **Ajax(异步JavaScript和XML)**:Ajax是...

    数据更新与分页数据更新与分页数据更新与分页

    为了减少数据库负载,可以采用缓存策略,如使用Redis等缓存系统存储已分页的数据。另外,索引优化也是提高分页效率的有效手段,尤其是对排序和过滤条件的列创建索引。 同时,考虑到用户体验,分页设计应考虑以下几...

    ListView分页加载及图片缓存显示的实现

    本示例“ListView分页加载及图片缓存显示的实现”着重讲解如何优化ListView的性能,提高用户体验,特别是处理大量图片时。以下是对该主题的详细解释: 1. **ListView分页加载**: 分页加载(Pagination)是一种...

    这是一个能够轻松帮助你管理请求状态的库,支持 SWR、轮询、错误重试、缓存、分页等常用功能

    这是一个能够轻松帮助你管理请求状态的库,支持 SWR、轮询、错误重试、缓存、分页等常用功能

    DAO模式与分页显示

    在实际应用中,DAO模式与分页显示相结合,通常是这样的:首先,通过DAO对象从数据库中查询出需要的数据集合,然后根据分页参数(如每页记录数、当前页码)计算出显示的数据范围,最后在前端使用DisplayTag或Pager-...

    用recyclerview实现分页滑动,横向纵向分页

    实现方法与纵向分页类似,只是判断滑动方向和触发加载条件不同。 3. **HorizontalPager**: 提到的`HorizontalPager`可能是一个自定义的LayoutManager或者一个库,用于更方便地实现横向分页。通常,它会提供更高级...

Global site tag (gtag.js) - Google Analytics