精华内容
下载资源
问答
  • ASP.NET Cache 实现依赖Oracle缓存策略

    千次阅读 2015-08-28 14:38:37
    ASP.NET 中的缓存提供了对SQL依赖项的...但微软一向很小家子气,只为使用自家产品SQL SERVER的程序员提供了方便,那些用Oracle数据库的ASP.NET程序员怎么办呢?  其实不用着急,因为ASP.NET中的缓存还提供了对文件依
    ASP.NET 中的缓存提供了对SQL依赖项的支持,也就是说当SQL SERVER数据库中的表或行中的数据被更改后,缓存中的页面就失效,否则,页面输出可一直保留在缓存当中。这确实为程序员提供了方便。但微软一向很小家子气,只为使用自家产品SQL SERVER的程序员提供了方便,那些用Oracle数据库的ASP.NET程序员怎么办呢?
    

      其实不用着急,因为ASP.NET中的缓存还提供了对文件依赖项的支持,也就是缓存依赖于某个文件,该文件被修改后,缓存中的页面就失效。只要巧妙利用ASP.NET的文件依赖项缓存策略和Oracle中的触发器,就可轻松实现依赖Oracle的缓存策略。思路很简单,先将页面的缓存策略设置为依赖某一个文件,再为Oracle中需要依赖的表添加一个触发器,当表中的数据被更改时,修改缓存所依赖的文件中的内容。

      下面以一个小例子来具体说明:

      试验目的:Default.aspx页面的缓存依赖于Oracle数据库中SCOTT用户的DEPT表,该表中数据被更改后,缓存中的页面失效。缓存的过期时间为120秒。

       一、设置网站页面的缓存依赖于文件TextFile.txt

      详见System.Web.Caching.Cache类 Asp.Net缓存 各种缓存依赖

    二、在Oracle数据库中创建触发器

      1、触发器被触发时执行PL/SQL代码块。PL/SQL代码块直接读写操作系统中的文件,需调用内置的utl_file程序包。这需要先修改Oracle的初始化参数文件INIT.ORA,在其中添加参数utl_file_dir,来指定文件的目录。修改INIT.ORA文件后,需重启Oracle数据库,设置的参数才能生效。

      在INIT.ORA文件中添加下面一行内容:

            utl_file_dir='E:/CSharp/CacheByOracleDependncy'

      也可以设置为utl_file_dir=*,不指定具体目录,即任何目录都可以。

      如果是Oracle 9i数据库,还有一种方法也能起到同样的作用:在sys用户下创建一个directory目录(实际上是在sys用户下的dir$表中增加一个对应的OS_PATH),然后将对该directory对象的读/写操作的权限grant给public。

    create or replace directory FILEPATH as 'E:/CSharp/CacheByOracleDependncy';
    
    grant read on directory FILEPATH to public;

            这里我使用的是第二种方法。

      2、为所依赖的表(SCOTT用户的DEPT表)创建一个触发器:当DEPT表中的数据更改后,触发器就会将当前系统时间写入TextFile.txt文件中。

    CREATE OR REPLACE TRIGGER
    "SCOTT"."TEST_CACHE_BY_ORACLE_DEPENDNCY" AFTER
    INSERT
    OR UPDATE
    OR DELETE OF "DEPTNO", "DNAME", "LOC" ON "SCOTT"."DEPT" DECLARE
    file_handle utl_file.file_type;
    BEGIN
    --打开文件
    file_handle := utl_file.fopen('FILEPATH','TextFile.txt','w');
    --将当前系统时间写入文件
    IF utl_file.is_open(file_handle) THEN
     utl_file.put_line(file_handle,to_char(SYSDATE,'yyyy-mm-dd hh24:mi:ss'));
    END IF;
    --关闭文件
    utl_file.fclose(file_handle);
    EXCEPTION
    WHEN OTHERS THEN
    BEGIN
    IF utl_file.is_open(file_handle) THEN
    utl_file.fclose(file_handle);
    END IF;
    EXCEPTION
    WHEN OTHERS THEN
    NULL;
    END;
    END;

    如果应用服务器和数据库服务器不是同一台服务器可能会遇到项目无法成功访问文件进行依赖的情况:

    解决方法详见ASP.NET访问网络驱动器(映射磁盘)

    展开全文
  • ASP.NET 2.0中的缓存提供了对SQL依赖项的...但微软一向很小家子气,只为使用自家产品SQL SERVER的程序员提供了方便,那些用Oracle数据库的ASP.NET程序员怎么办呢? 其实不用着急,因为ASP.NET 2.0中的缓存还提供了对

    ASP.NET 2.0中的缓存提供了对SQL依赖项的支持,也就是说当SQL SERVER数据库中的表或行中的数据被更改后,缓存中的页面就失效,否则,页面输出可一直保留在缓存当中。这确实为程序员提供了方便。但微软一向很小家子气,只为使用自家产品SQL SERVER的程序员提供了方便,那些用Oracle数据库的ASP.NET程序员怎么办呢?

      其实不用着急,因为ASP.NET 2.0中的缓存还提供了对文件依赖项的支持,也就是缓存依赖于某个文件,该文件被修改后,缓存中的页面就失效。只要巧妙利用ASP.NET 2.0的文件依赖项缓存策略和Oracle中的触发器,就可轻松实现依赖Oracle的缓存策略。思路很简单,先将页面的缓存策略设置为依赖某一个文件,再为Oracle中需要依赖的表添加一个触发器,当表中的数据被更改时,修改缓存所依赖的文件中的内容。

      下面以一个小例子来具体说明:

      试验目的:Default.aspx页面的缓存依赖于Oracle数据库中test用户的tb_users表,该表中数据被更改后,缓存中的页面失效。缓存的过期时间为120秒。

      一、设置网站页面的缓存依赖于文件TextFile.txt

      1、打开Visual Studio 2005,在C:/Websites/testOracleCacheDependency目录下新建一个WEB项目,在其Default.aspx页面上添加一个Label控件,显示页面生成的时间,以判断刷新时页面是否为重新生成的,并设置页面缓存依赖于文件C:/Websites/testOracleCacheDependency/TextFile.txt。

    protected void Page_Load(object sender, EventArgs e)
    {
     //显示当前时间,以便判断是否为缓存中页面
     this.Label1.Text = "CacheByOracleDependency:" + DateTime.Now.ToString();
     //缓存依赖于文件TextFile.txt
     string fileDependencyPath = Server.MapPath("TextFile.txt");
     Response.AddFileDependency(fileDependencyPath);
     // 设置缓存的过期时间为120秒。
     Response.Cache.SetExpires(DateTime.Now.AddSeconds(120));
     Response.Cache.SetCacheability(HttpCacheability.Public);
     Response.Cache.SetValidUntilExpires(true);
    }


      2、在C:/Websites/testOracleCacheDependency目录下新建一个TextFile.txt文件。

      二、在Oracle数据库中创建触发器

      1、触发器被触发时执行PL/SQL代码块。PL/SQL代码块直接读写操作系统中的文件,需调用内置的utl_file程序包。这需要先修改Oracle的初始化参数文件INIT.ORA,在其中添加参数utl_file_dir,来指定文件的目录。修改INIT.ORA文件后,需重启Oracle数据库,设置的参数才能生效。

      在INIT.ORA文件中添加下面一行内容:

      utl_file_dir='C:/Websites/testOracleCacheDependency'

      也可以设置为utl_file_dir=*,不指定具体目录,即任何目录都可以。

      如果是Oracle 9i数据库,还有一种方法也能起到同样的作用:在sys用户下创建一个directory目录(实际上是在sys用户下的dir$表中增加一个对应的OS_PATH),然后将对该directory对象的读/写操作的权限grant给public。

      create or replace directory FILEPATH as 'C:/Websites/testOracleCacheDependency';

      grant write on directory FILEPATH to public;

      这里我使用的是第二种方法。

      2、为所依赖的表(test用户的tb_users表)创建一个触发器:当tb_users表中的数据更改后,触发器就会将当前系统时间写入TextFile.txt文件中。

    CREATE OR REPLACE TRIGGER CacheDependency_trigger
    AFTER
    INSERT
    OR UPDATE
    OR DELETE ON tb_users

    DECLARE
    file_handle utl_file.file_type;

    BEGIN
    --打开文件
    file_handle := utl_file.fopen('FILEPATH','TextFile.txt','w');
    --将当前系统时间写入文件
    IF utl_file.is_open(file_handle) THEN
       utl_file.put_line(file_handle,to_char(SYSDATE,'yyyy-mm-dd hh24:mi:ss'));
    end if;
    --关闭文件
    utl_file.fclose(file_handle);
    exception
    WHEN OTHERS THEN
    BEGIN
    if utl_file.is_open(file_handle) then
        utl_file.fclose(file_handle);
    end if;
    exception
         when others then
           null;
    END;

    END CacheDependency_trigger;


      三、测试

      前面两步完成后,依赖Oracle的缓存策略就设置好了。只要tb_users表中的数据更改后,触发器就会修改TextFile.txt文件中的内容,TextFile.txt文件一被修改,缓存中的页面就自动失效了,再次访问页面时,请求就会发给服务器,来重新生成页面。

      在Visual Studio 2005中调试程序,不断刷新打开的Default.aspx页面,页面显示的时间每隔120秒,才会发生变化一次。这是因为设置的缓存过期时间为120秒。这时,只要我们手工修改SCOTT用户的DEPT表中的数据后,再次刷新页面时,页面上显示的时间马上就会发生变化。这说明我们设置的依赖Oracle的缓存策略成功了。

    展开全文
  • Oracle RAC数据库缓存优化策略探究.pdf
  • oracle缓存机制详解

    千次阅读 2014-04-10 23:09:29
    转自: ...   hibernate提供的一级缓存 hibernate是一个线程对应...也就是说session级缓存(一级缓存)只能给一个线程用,别的线程用不了,一级缓存就是和线程绑定了。   hibernate一级缓存生命周期很短,和sessi...

    转自:

    http://www.blogjava.net/tbwshc/articles/380013.html

     

    hibernate提供的一级缓存

    hibernate是一个线程对应一个session,一个线程可以看成一个用户。也就是说session级缓存(一级缓存)只能给一个线程用,别的线程用不了,一级缓存就是和线程绑定了。

     

    hibernate一级缓存生命周期很短,和session生命周期一样,一级缓存也称session级的缓存或事务级缓存。如果tb事务提交或回滚了,我们称session就关闭了,生命周期结束了。

     

    缓存和连接池的区别:缓存和池都是放在内存里,实现是一样的,都是为了提高性能的。但有细微的差别,池是重量级的,里面的数据是一样的,比如一个池里放100个Connection连接对象,这个100个都是一样的。缓存里的数据,每个都不一样。比如读取100条数据库记录放到缓存里,这100条记录都不一样。

    缓存主要是用于查询

     

    //同一个session中,发出两次load方法查询

    Student student = (Student)session.load(Student.class, 1);

    System.out.println("student.name=" + student.getName());

     

    //不会发出查询语句,load使用缓存

    student = (Student)session.load(Student.class, 1);

    System.out.println("student.name=" + student.getName());

    第二次查询第一次相同的数据,第二次load方法就是从缓存里取数据,不会发出sql语句到数据库里查询。

     

    //同一个session,发出两次get方法查询

    Student student = (Student)session.get(Student.class, 1);

    System.out.println("student.name=" + student.getName());

     

    //不会发出查询语句,get使用缓存

    student = (Student)session.get(Student.class, 1);

    System.out.println("student.name=" + student.getName());

    第二次查询第一次相同的数据,第二次不会发出sql语句查询数据库,而是到缓存里取数据。

     

    //同一个session,发出两次iterate查询实体对象

    Iterator iter = session.createQuery

    ("from Student s where s.id<5").iterate();

    while (iter.hasNext()) {

    Student student = (Student)iter.next();

    System.out.println(student.getName());

    }

    System.out.println("--------------------------------------");

    //它会发出查询id的语句,但不会发出根据id查询学生的语句,因为iterate使用缓存

    iter = session.createQuery("from Student s where s.id<5").iterate();

    while (iter.hasNext()) {

    Student student = (Student)iter.next();

    System.out.println(student.getName());

    }

    一说到iterater查询就要立刻想起:iterater查询在没有缓存的情况下会有N+1的问题。

    执行上面代码查看控制台的sql语句,第一次iterate查询会发出N+1条sql语句,第一条sql语句查询所有的id,然后根据id查询实体对象,有N个id就发出N条语句查询实体。

    第二次iterate查询,却只发一条sql语句,查询所有的id,然后根据id到缓存里取实体对象,不再发sql语句到数据库里查询了。

    //同一个session,发出两次iterate查询,查询普通属性

    Iterator iter = session.createQuery(

    "select s.name from Student s where s.id<5").iterate();

    while (iter.hasNext()) {

    String name = (String)iter.next();

    System.out.println(name);

    }

    System.out.println("--------------------------------------");

     

    //iterate查询普通属性,一级缓存不会缓存,所以发出查询语句

    //一级缓存是缓存实体对象的

    iter = session.createQuery

    ("select s.name from Student s where s.id<5").iterate();

    while (iter.hasNext()) {

    String name = (String)iter.next();

    System.out.println(name);

    }

    执行代码看控制台sql语句,第一次发出N+1条sql语句,第二次还是发出了N+1条sql语句。因为一级缓存只缓存实体对象,tb不会缓存普通属性,所以第二次还是发出sql查询语句。

     

    //两个session,每个session发出一个load方法查询实体对象

    try {

    session = HibernateUtils.getSession();

    session.beginTransaction();

    Student student = (Student)session.load(Student.class, 1);

    System.out.println("student.name=" + student.getName());

    session.getTransaction().commit();

    }catch(Exception e) {

    e.printStackTrace();

    session.getTransaction().rollback();

    }finally {

    HibernateUtils.closeSession(session);

    }

    第二个session调用load方法

    try {

    session = HibernateUtils.getSession();

    session.beginTransaction();

    Student student = (Student)session.load(Student.class, 1);

    //会发出查询语句,session间不能共享一级缓存数据

    //因为他会伴随着session的消亡而消亡

    System.out.println("student.name=" + student.getName());

    session.getTransaction().commit();

    }catch(Exception e) {

    e.printStackTrace();

    session.getTransaction().rollback();

    }finally {

    HibernateUtils.closeSession(session);

    }

    第一个session的load方法会发出sql语句查询实体对象,第二个session的load方法也会发出sql语句查询实体对象。因为session间不能共享一级缓存的数据,所以第二个session的load方法查询相同的数据还是要到数据库中查询,因为它找不到第一个session里缓存的数据。

     

    //同一个session,先调用save方法再调用load方法查询刚刚save的数据

    Student student = new Student();

    student.setName("张三");

    //save方法返回实体对象的id

    Serializable id = session.save(student);

    student = (Student)session.load(Student.class, id);

    //不会发出查询语句,因为save支持缓存

    System.out.println("student.name=" + student.getName());

    先save保存实体对象,再用load方法查询刚刚save的实体对象,则load方法不会发出sql语句到数据库查询的,而是到缓存里取数据,因为save方法也支持缓存。当然前提是同一个session。

     

    //大批量的数据添加

    for (int i=0; i<100; i++) {

    Student student = new Student();

    student.setName("张三" + i);

    session.save(student);

    //每20条更新一次

    if (i % 20 == 0) {

    session.flush();

    //清除缓存的内容

    session.clear();

    }

    }

    大批量数据添加时,会造成内存溢出的,因为save方法支持缓存,每save一个对象就往缓存里放,如果对象足够多内存肯定要溢出。一般的做法是先判断一下save了多少个对象,如果save了20个对象就对缓存手动的清理缓存,这样就不会造成内存溢出。

    注意:清理缓存前,要手动调用flush方法同步到数据库,否则save的对象就没有保存到数据库里。

     

    注意:大批量数据的添加还是不要使用hibernate,这是hibernate弱项。可以使用jdbc(速度也不会太快,只是比hibernate好一点),或者使用工具产品来实现,比如oracle的Oracle SQL Loader,导入数据特别快。 



    Hibernate 二级缓存

    二级缓存需要sessionFactory来管理,它是进初级的缓存,所有人都可以使用,它是共享的。

    二级缓存比较复杂,一般用第三方产品。hibernate提供了一个简单实现,用Hashtable做的,只能作为我们的测试使用,商用还是需要第三方产品。

    使用缓存,肯定是长时间不改变的数据,如果经常变化的数据放到缓存里就没有太大意义了。因为经常变化,还是需要经常到数据库里查询,那就没有必要用缓存了。

    hibernate做了一些优化,和一些第三方的缓存产品做了集成。老师采用EHCache缓存产品。

     

    和EHCache二级缓存产品集成:EHCache的jar文件在hibernate的lib里,我们还需要设置一系列的缓存使用策略,需要一个配置文件ehcache.xml来配置。这个文件放在类路径下。

    //默认配置,所有的类都遵循这个配置

    <defaultCache

            //缓存里可以放10000个对象

            maxElementsInMemory="10000"

            //过不过期,如果是true就是永远不过期

            eternal="false"

            //一个对象被访问后多长时间还没有访问就失效(120秒还没有再次访问就失效)

            timeToIdleSeconds="120"

            //对象存活时间(120秒),如果设置永不过期,这个就没有必要设了

            timeToLiveSeconds="120"

            //溢出的问题,如果设成true,缓存里超过10000个对象就保存到磁盘里

            overflowToDisk="true"

            />

     

    我们也可以对某个对象单独配置:

    <cache name="com.bjpowernode.hibernate.Student"

            maxElementsInMemory="100"

            eternal="false"

            timeToIdleSeconds="10000"

            timeToLiveSeconds="10000"

            overflowToDisk="true"

            />

     

    还需要在hibernate.cfg.xml配置文件配置缓存,让hibernate知道我们使用的是那个二级缓存。

    <!-- 配置缓存提供商 -->

    <property name="hibernate.cache.provider_class">

    org.hibernate.cache.EhCacheProvider</property>

     

    <!-- 启用二级缓存,这也是它的默认配置 -->

    <property name="hibernate.cache.use_second_level_cache">

    true</property>

    启用二级缓存的配置可以不写的,因为默认就是true开启二级缓存。

     

    必须还手动指定那些实体类的对象放到缓存里在hibernate.cfg.xml里:

    //在<sessionfactory>标签里,在<mapping>标签后配置

    <class-cache class="com.bjpowernode.hibernate.Student"

    usage="read-only"/>

    或者在实体类映射文件里:

    //在<class>标签里,<id>标签前配置

    <cache usage="read-only"/>

    usage属性表示使用缓存的策略,一般优先使用read-only,表示如果这个数据放到缓存里了,则不允许修改,如果修改就会报错。这就要注意我们放入缓存的数据不允许修改。因为放缓存里的数据经常修改,也就没有必要放到缓存里。

    使用read-only策略效率好,因为不能改缓存。但是可能会出现脏数据的问题,这个问题解决方法只能依赖缓存的超时,比如上面我们设置了超时为120秒,120后就可以对缓存里对象进行修改,而在120秒之内访问这个对象可能会查询脏数据的问题,因为我们修改对象后数据库里改变了,而缓存却不能改变,这样造成数据不同步,也就是脏数据的问题。

     

    第二种缓存策略read-write,当持久对象发生变化,缓存里就会跟着变化,数据库中也改变了。这种方式需要加解锁,效率要比第一种慢。

    还有两种策略,请看hibernate文档,最常用还是第一二种策略。

     

    二级缓存测试代码演示:注意上面我们讲的两个session分别调用load方法查询相同的数据,第二个session的load方法还是发了sql语句到数据库查询数据,这是因为一级缓存只在当前session中共享,也就是说一级缓存不能跨session访问。

    //开启二级缓存,二级缓存是进程级的缓存,可以共享

    //两个session分别调用load方法查询相同的实体对象

    try {

    session = HibernateUtils.getSession();

    session.beginTransaction();

    Student student = (Student)session.load(Student.class, 1);

    System.out.println("student.name=" + student.getName());

    session.getTransaction().commit();

    }catch(Exception e) {

    e.printStackTrace();

    session.getTransaction().rollback();

    }finally {

    HibernateUtils.closeSession(session);

    }

     

    try {

    session = HibernateUtils.getSession();

    session.beginTransaction();

    Student student = (Student)session.load(Student.class, 1);

     

    //不会发出查询语句,因为配置二级缓存,session可以共享二级缓存中的数据

    //二级缓存是进程级的缓存

    System.out.println("student.name=" + student.getName());

    session.getTransaction().commit();

    }catch(Exception e) {

    e.printStackTrace();

    session.getTransaction().rollback();

    }finally {

    HibernateUtils.closeSession(session);

    }

    如果开启了二级缓存,那么第二个session调用的load方法查询第一次查询的数据,是不会发出sql语句查询数据库的,而是去二级缓存中取数据。

     

    //开启二级缓存

    //两个session分别调用get方法查询相同的实体对象

    try {

    session = HibernateUtils.getSession();

    session.beginTransaction();

    Student student = (Student)session.get(Student.class, 1);

    System.out.println("student.name=" + student.getName());

    session.getTransaction().commit();

    }catch(Exception e) {

    e.printStackTrace();

    session.getTransaction().rollback();

    }finally {

    HibernateUtils.closeSession(session);

    }

     

    try {

    session = HibernateUtils.getSession();

    session.beginTransaction();

    Student student = (Student)session.get(Student.class, 1);

     

    //不会发出查询语句,因为配置二级缓存,session可以共享二级缓存中的数据

    //二级缓存是进程级的缓存

    System.out.println("student.name=" + student.getName());

    session.getTransaction().commit();

    }catch(Exception e) {

    e.printStackTrace();

    session.getTransaction().rollback();

    }finally {

    HibernateUtils.closeSession(session);

    }

     

    注意:二级缓存必须让sessionfactory管理,让sessionfactory来清除二级缓存。sessionFactory.evict(Student.class);//清除二级缓存中所有student对象,sessionFactory.evict(Student.class,1);//清除二级缓存中id为1的student对象。

     

    如果在第一个session调用load或get方法查询数据后,把二级缓存清除了,那么第二个session调用load或get方法查询相同的数据时,还是会发出sql语句查询数据库的,因为缓存里没有数据只能到数据库里查询。

     

    我们查询数据后会默认自动的放到二级和一级缓存里,如果我们想查询的数据不放到缓存里,也是可以的。也就是说我们可以控制一级缓存和二级缓存的交换。

    session.setCacheMode(CacheMode.IGNORE);禁止将一级缓存中的数据往二级缓存里放。

     

    还是用上面代码测试,在第一个session调用load方法前,执行session.setCacheMode(CacheMode.IGNORE);这样load方法查询的数据不会放到二级缓存里。那么第二个session执行load方法查询相同的数据,会发出sql语句到数据库中查询,因为二级缓存里没有数据,一级缓存因为不同的session不能共享,所以只能到数据库里查询。

     

    上面我们讲过大批量的数据添加时可能会出现溢出,解决办法是每当天就20个对象后就清理一次一级缓存。如果我们使用了二级缓存,光清理一级缓存是不够的,还要禁止一二级缓存交互,在save方法前调用session.setCacheMode(CacheMode.IGNORE)。

     

    二级缓存也不会存放普通属性的查询数据,这和一级缓存是一样的,只存放实体对象。session级的缓存对性能的提高没有太大的意义,因为生命周期太短了。



     

    Hibernate 查询缓存

    一级缓存和二级缓存都只是存放实体对象的,如果查询实体对象的普通属性的数据,只能放到查询缓存里,查询缓存还存放查询实体对象的id。

     

    查询缓存的生命周期不确定,当它关联的表发生修改,查询缓存的生命周期就结束。这里表的修改指的是通过hibernate修改,并不是通过数据库客户端软件登陆到数据库上修改。

     

    hibernate的查询缓存默认是关闭的,如果要使用就要到hibernate.cfg.xml文件里配置:

    <property name="hibernate.cache.use_query_cache">true</property>

    并且必须在程序中手动启用查询缓存,在query接口中的setCacheable(true)方法来启用。

     

    //关闭二级缓存,没有开启查询缓存,采用list方法查询普通属性

    //同一个sessin,查询两次

    List names = session.createQuery("select s.name from Student s")

    .list();

    for (int i=0; i<names.size(); i++) {

    String name = (String)names.get(i);

    System.out.println(name);

    }

    System.out.println("-----------------------------------------");

    //会发出sql语句

    names = session.createQuery("select s.name from Student s")

    .setCacheable(true)

    .list();

    for (int i=0; i<names.size(); i++) {

    String name = (String)names.get(i);

    System.out.println(name);

    }

    上面代码运行,由于没有使用查询缓存,而一、二级缓存不会缓存普通属性,所以第二次查询还是会发出sql语句到数据库中查询。

     

    现在开启查询缓存,关闭二级缓存,并且在第一次的list方法前调用setCacheable(true),并且第二次list查询前也调用这句代码,可以写出下面这样:

    List names = session.createQuery("select s.name from Student s")

    .setCacheable(true)

    .list();

    其它代码不变,运行代码后发现第二次list查询普通属性没有发出sql语句,也就是说没有到数据库中查询,而是到查询缓存中取数据。

     

    //开启查询缓存,关闭二级缓存,采用list方法查询普通属性

    //在两个session中调用list方法

    try {

    session = HibernateUtils.getSession();

    session.beginTransaction();

    List names = session.createQuery("select s.name from Student s")

    .setCacheable(true)

    .list();

    for (int i=0; i<names.size(); i++) {

    String name = (String)names.get(i);

    System.out.println(name);

    }

    session.getTransaction().commit();

    }catch(Exception e) {

    e.printStackTrace();

    session.getTransaction().rollback();

    }finally {

    HibernateUtils.closeSession(session);

    }

    System.out.println("----------------------------------------");

    try {

    session = HibernateUtils.getSession();

    session.beginTransaction();

    //不会发出查询语句,因为查询缓存和session的生命周期没有关系

    List names = session.createQuery("select s.name from Student s")

    .setCacheable(true)

    .list();

    for (int i=0; i<names.size(); i++) {

    String name = (String)names.get(i);

    System.out.println(name);

    }

    session.getTransaction().commit();

    }catch(Exception e) {

    e.printStackTrace();

    session.getTransaction().rollback();

    }finally {

    HibernateUtils.closeSession(session);

    }

    运行结果是第二个session发出的list方法查询普通属性,没有发出sql语句到数据库中查询,而是到查询缓存里取数据,这说明查询缓存和session生命周期没有关系。

     

    //开启缓存,关闭二级缓存,采用iterate方法查询普通属性

    //在两个session中调用iterate方法查询

    运行结果是第二个session的iterate方法还是发出了sql语句查询数据库,这说明iterate迭代查询普通属性不支持查询缓存。

     

    //关闭查询缓存,关闭二级缓存,采用list方法查询实体对象

    //在两个session中调用list方法查询

    运行结果第一个session调用list方法查询实体对象会发出sql语句查询数据,因为关闭了二级缓存,所以第二个session调用list方法查询实体对象,还是会发出sql语句到数据库中查询。

     

    //开启查询缓存,关闭二级缓存

    //在两个session中调用list方法查询实体对象

    运行结果第一个session调用list方法查询实体对象会发出sql语句查询数据库的。第二个session调用list方法查询实体对象,却发出了很多sql语句查询数据库,这跟N+1的问题是一样的,发出了N+1条sql语句。为什么会出现这样的情况呢?这是因为我们现在查询的是实体对象,查询缓存会把第一次查询的实体对象的id放到缓存里,当第二个session再次调用list方法时,它会到查询缓存里把id一个一个的拿出来,然后到相应的缓存里找(先找一级缓存找不到再找二级缓存),如果找到了就返回,如果还是没有找到,则会根据一个一个的id到数据库中查询,所以一个id就会有一条sql语句。

    注意:如果配置了二级缓存,则第一次查询实体对象后,会往一级缓存和二级缓存里都存放。如果没有二级缓存,则只在一级缓存里存放。(一级缓存不能跨session共享)

     

    //开启查询缓存,开启二级缓存

    //在两个session中调用list方法查询实体对象

    运行结果是第一个session调用list方法会发出sql语句到数据库里查询实体对象,因为配置了二级缓存,则实体对象会放到二级缓存里,因为配置了查询缓存,则实体对象所有的id放到了查询缓存里。第二个session调用list方法不会发出sql语句,而是到二级缓存里取数据。

     

    查询缓存意义不大,查询缓存说白了就是存放由list方法或iterate方法查询的数据。我们在查询时很少出现完全相同条件的查询,这也就是命中率低,这样缓存里的数据总是变化的,所以说意义不大。除非是多次查询都是查询相同条件的数据,也就是说返回的结果总是一样,这样配置查询缓存才有意义。

    展开全文
  • 我曾经用Java代码模拟了Redis的存储和读取,那只是一个功能的简单实现,并没有什么技术含量可言.Redis在如今的互联网应用当中算是起到了一个中流砥柱的作用吧,我姑且这么认知,毕竟在扛并发和缓存方面使用范围比较广,...

    我曾经用Java代码模拟了Redis的存储和读取,那只是一个功能的简单实现,并没有什么技术含量可言.Redis在如今的互联网应用当中算是起到了一个中流砥柱的作用吧,我姑且这么认知,毕竟在扛并发和缓存方面使用范围比较广,而且集群部署健壮性也是OK的.对于大厂而言可能已经很小儿科,但是对于小型企业而言还是有点帮助的.最近如果有时间准备去看看Memcache,虽然这东西最大存储单槽1MB,Redis是1GB,But Redis是单进程单线程的对于多核CPU而言最多也就占据单个核心而Memcache则不然,多核CPU对其更加有利..但是我没用过Memcache,惭愧啊.希望有时间能模拟一个环境进行压测,获取压力曲线这种第一手资料,再来更加详细总结一下吧.

    优势和使用的原因
    互联网项目,类似电商、社交、网约车之类的都会面临高并发的一个问题.在解决并发安全问题的基础上必须考虑的就是一个性能问题,这直接影响到用户体验和服务提供的能力上限.通常那些热度比较高的热点会被用户大量访问,比如某国产手机做了个爆款,类似小米之类的一上线发布,会引发很多米粉的关注,PV就这样上去了..一种不假思索的解决方案就是使用缓存.而Redis这种基于内存的NoSQL在执行效率上远胜于MySQL,Oracle之类的基于硬盘的传统关系型数据.而且因为是内存级的所以还能避免直接操作传统数据库带来的链路层延时带来的时间浪费,真是快哉秒哉.

    理想很丰满,现实很骨感.
    就算是引入了缓存,依然能会存在一些出人意料的情况,而正是这些极端情况带来的风险还是切实威胁着生产环境的.比如缓存雪崩,缓存穿透,缓存击穿.

    缓存雪崩:
    就和滚雪球一样,雪球越滚越大最后雪崩.比如某些数据没有被成功加载入缓存当中,或者某一时间点大量缓存同时失效,此时如果又有大量的请求进来进行查询而缓存之中没有对应的副本去响应请求,必然的结果就是去查询数据库,直接导致数据库服务器压力过大响应缓慢,或者干脆直接宕机.
    如图:缓存雪崩
    解决之道:最简单的办法就是让key的失效时间不连续,比如expire设置为1分钟失效,可以在后面增加1-20秒的随机数,一定程度上能够解决大面积失效的问题.但是万一命很背,随机数全是一样的,比如都是1秒,仍然会导致雪崩,这个时候就需要在代码中使用到上锁,或者通过Queue来保证缓存通过单线程写入,避免这种情况..这里感谢原阿里P7大佬的点拨,谢谢.

    缓存穿透:
    当查询一个不存在的数据时,缓存肯定是不会被命中的,而正因为缓存不被命中,所以需要去数据库中查询并且写入缓存.缓存中没有的数据就去数据库查询,如果查询到了就写入缓存同时返回结果给用户,等待下次访问直接走缓存,但是如果查询失败了必然是不会写入缓存中的,那么如果人家频繁操或者说并发量大的情况下(也可能是攻击),DB依然容易宕机.
    如图:
    缓存穿透
    解决之道:在爱品宝跨境商城当中采用的是一种比较原始但是可靠的土方法(我的馊主意),如果一个查询结果为空,照样缓存这个空结果,但是过期时间设置得比较短就30秒.减少数据库服务器压力,把压力转嫁给Redis集群.还有StackOverFlow上的一个老外给出的方法是使用一个BloomFilter,但是很惭愧,这个我并没有过深研究,有时间我会补全 //TODO 研究BloomFilter并且熟悉其原理

    缓存击穿:
    某一时刻有一个key快要过期了,此时一波洪峰正在路上,key过期以后自然是不存在了的,此时洪峰到了,高并发带来的就是一顿骚操作,所有的人都查询了数据库,并且都往缓存中写..想想也是很可怕的.
    如图:
    缓存击穿
    解决之道:使用互斥锁,在缓存失效的时候,查询缓存必然是不命中,先通过Redis的SETNX(Set if not exits)设置一个代表互斥锁的key,例如mutex_lock_key: ,并且给其设置一个较长一点的过期时间(防止查询时间过长其他线程又进来捣乱),一旦这个互斥锁设置成功,再查询数据库,然后写入缓存,否则就重新走以上步骤..
    其他好的办法暂时也没想到..//TODO

    展开全文
  • oracle Coherence企业级缓存

    千次阅读 2013-04-28 21:30:41
    Oracle Coherence 是一个面向企业级应用的分布式缓存框架,看过它的简单介绍后,感觉是:很好很强大。   Hibernate 封装了统一的 Cache接口 CacheProvider ,可以方便的集成实现该接口的第三方Cache框架。  ...
  • oracle主键生成策略

    千次阅读 2018-07-11 11:07:04
    :表示缓存序列的个数,数据库异常终止可能会导致序列中断不连续的情况,默认值为20,如果不使用缓存可设置 NOCACHE 例  CREATE SEQUENCE SEQ_DEMO  INCREMENT BY 1  START WITH 1  NOMAXvalue  ...
  • 为了创建可扩展、高性能的基于WEB的应用,ASP.NET提供一个称为数据缓存(Data ...本文讲述一个策略,可用于采用Web Farm环境中的ASP.NET Web应用缓存Oracle数据库数据。这个技巧允许在内存中缓存频繁访问的Oracle
  • 为了创建可扩展、高性能的基于WEB的应用,ASP.NET提供一个称为数据缓存...本文讲述一个策略,可用于采用WebFarm环境中的ASP.NETWeb应用缓存Oracle数据库数据。这个技巧允许在内存中缓存频繁访问的Oracle数据库
  • 为了创建可扩展、高性能的基于Web的应用,ASP.net提供一个称为数据缓存(Data Caching)...本文讲述一个策略,可用于采用Web Farm环境中的ASP.NET Web应用缓存Oracle数据库数据。这个技巧允许在内存中缓存频繁访问的Or
  • Oracle 数据库ORA-28100策略函数问题 通过IMP导入dmp数据文件后,查询导入的表,报ora-28100策略函数错误。 查询当前用户下的定义的策略组 select OBJECT_NAME, POLICY_GROUP, POLICY_NAME from user_policies; ...
  • 微服务建构下缓存使用策略

    千次阅读 2018-05-26 17:34:34
    缓存由于其适应高并发和高性能的特性,已经在项目中被广泛...本文由以下三个部分组成1、讲解缓存更新策略2、对每种策略进行缺点分析3、针对缺点给出改进方案 从理论上来说,给缓存设置过期时间,是保证最终一致性的...
  • 了创建可扩展、高性能的基于WEB的应用,ASP.NET提供一个称为数据缓存(Data Caching)的...本文讲述一个策略,可用于采用Web Farm环境中的ASP.NET Web应用缓存Oracle数据库数据。这个技巧允许在内存中缓存频繁访问的Ora
  • 缓存数据来源 ... 选择一个高速缓存策略  创建一个缓存存储实现  在缓存存储实施堵漏  样品的缓存存储  可控的缓存存储范例  实施注意事项 14.1缓存数据源概述 Coherence支持透明,读/写
  •  最近hyddd一直看Oracle的资料,今天特地总结一下这段时间了解到的关于Oracle性能优化的下手点。   一.数据库优化的方向 1.程序设计(这点最重要,如果程序本身设计有问题,再怎么进行下面的优化都是徒劳的。...
  • 15秒:为ASP.NET应用缓存Oracle数据

    千次阅读 2004-01-15 14:36:00
    15秒:为ASP.NET应用缓存Oracle数据原作:Narayan Veeramani时间:12/29 /2003为了创建可扩展、高性能的基于WEB的应用,ASP.NET提供一个称为数据缓存(Data Caching)的特性。数据缓存支持将频繁访问的数据对象可...
  • Oracle 11g:SQL查询结果集缓存

    万次阅读 2014-08-12 11:41:34
    【IT专家网独家】随着过去三十年Oracle关系型数据库管理系统的发展,它提供给数据库管理员几个选项将经常被访问的数据保存在内存中以减少或消除从数据库的I/O子系统读取不必要的数据。下面是使数据库管理员可以在她...
  • Oracle 高级教程

    万次阅读 多人点赞 2018-06-06 11:10:15
    目录 一、Oracle体系结构的概览... 2 二、Oracle高级查询... 10 三、Oracle触发器... 18 四、Oracle存储过程... 26 五、Oracle事务... 30 六、Oracle锁... 37 七、Oracle包... 40 八、Oracle游标... 42 九、Oracle...
  • 高速缓存简介 本章提供了一个概述和比较连贯性提供基本的缓存类型。本章包括以下几个部分:  分布式缓存  复制缓存  乐观缓存  近缓存  本地缓存  远程缓存  缓存类型摘要 11.1...
  • 本文讨论如果一个Cache Table设定了Aging策略,那么Aging导致的缓存中数据的删除是否会影响到Oracle数据库?如果是只读缓存组,当然是不会影响到Oracle的。如果是AWT缓存组,答案也是不会影响,即Aging导致的数据删除...
  • 配置高速缓存 本章提供了详细说明如何配置高速缓存在缓存配置部署描述符。请参阅附录B,“缓存配置元素”,为一个完整的描述符中的所有元素的参考。此外,请参见第17章,“例”,为各种样品的缓存配置的高速缓存...
  • Oracle RAC

    2013-01-31 09:52:49
    Oracle RAC 目录 RAC 《Oracle RAC》 Oracle RAC组件 展开 RAC 《Oracle RAC》 Oracle RAC组件 展开 编辑本段RAC 〔RAC提供的优缺点〕  Oracle RAC 集群示意图 ...
  • Oracle高级教程

    千次阅读 2018-10-28 10:17:34
    一、OracleOracle体系结构的概览  二、Oracle高级查询 三、Oracle触发器 四、Oracle存储过程 五、Oracle事务 六、Oracle锁 七、Oracle包 八、Oracle游标 九、Oracle函数 十、Oracle备份与...
  • Oracle自动性能统计

    千次阅读 2017-04-13 10:10:15
    高效诊断性能问题,需要提供完整可用的...Oracle数据库为系统、会话以及单独的sql语句生成多种类型的累积统计信息。本文主要描述Oracle性能统计涉及到的相关概念及统计对象,以更好的利用统计信息为性能调整奠定基础。
  • Oracle--数据量增长过快策略

    千次阅读 2016-03-24 09:56:32
    Oracle数据量增长太长过快策略可能会照成的问题: - [ ] 查询可能会缓慢 1. 减少表关联,多张表数据是否可以全部整合建立一张落地数据表 2. 按查询时间分块,必须选择时间进行查询。 3. 添加分区表,...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 43,451
精华内容 17,380
关键字:

oracle缓存策略