文档章节

Cache与Fetch(二)

猪刚烈
 猪刚烈
发布于 2014/10/12 11:40
字数 1447
阅读 12
收藏 0

这两天一直百思不得其解的问题终于解决了,这个问题如下:

通过HQL:“select distinct forumGroup from ForumGroup as forumGroup left join fetch forumGroup.forums”查询所有ForumGroup,并将它们的Forum一并抓取出来。查询启用了查询缓存,ForumGroup和Forum都被映射为可缓存的。

第一次执行时,自然不会命中,生成了如下SQL:

    /* select
        distinct forumGroup
    from
        ForumGroup as forumGroup
    left join
        fetch forumGroup.forums */ select
            distinct forumgroup0_.id as id3_0_,
            forums1_.id as id2_1_,
            forumgroup0_.creationTime as creation2_3_0_,
            forumgroup0_.description as descript3_3_0_,
            forumgroup0_.modifiedTime as modified4_3_0_,
            forumgroup0_.name as name3_0_,
            forums1_.creationTime as creation2_2_1_,
            forums1_.description as descript3_2_1_,
            forums1_.groupId as groupId2_1_,
            forums1_.modifiedTime as modified4_2_1_,
            forums1_.name as name2_1_,
            forums1_.groupId as groupId0__,
            forums1_.id as id0__
        from
            ForumGroup forumgroup0_
        left outer join
            Forum forums1_
                on forumgroup0_.id=forums1_.groupId

 

第二次执行时,按理是应该命中,不会有任何SQL生成,但是实际结果却是:

DEBUG [13178395@qtp-12191562-0] StandardQueryCache.get(142) | returning cached query results

   /* load one-to-many oobbs.domainmodel.forum.ForumGroup.forums */ select
        forums0_.groupId as groupId1_,
        forums0_.id as id1_,
        forums0_.id as id2_0_,
        forums0_.creationTime as creation2_2_0_,
        forums0_.description as descript3_2_0_,
        forums0_.groupId as groupId2_0_,
        forums0_.modifiedTime as modified4_2_0_,
        forums0_.name as name2_0_
    from
        Forum forums0_
    where
        forums0_.groupId=?

.

.

.

重复出现上述SQL.


分析上面的日志我发现:1.查询已经命中,这一点是确认的。2.重复出现的SQL是在迭代ForumGroup中试图访问它的Fourm集合时生成的,这是一个典型的N+1次查询问题。

这里让我迷惑的是:Forum被标记为了可缓存,明明是被Fetch出来的,在第一次查询时它们就被加载出来并进入到二级缓存了,为什么在第二次第三次查询时却找不到这些对象,还要重新查数据库呢?经过排查发现,在ForumGroup的Forum集合字段上没有将该集合配制为可缓存。这样,虽然第一次这些Forum都被抓取出来并进入了二级缓存,但是ForumGroup对象的forums集合(一个存放Forum的ID的集合,不是Forum对象的集合)在上次查询时就没有进入二级缓存,现在,这些集合没有保存forum的ID(在第一次查询的结果集中是有这些ID的,但是因为这个forums集合没有配为可缓存的,所以在ForumGroup对象进入二级缓存时,这些forum集合的信息就被舍弃了)。 这样,下一次查询时,虽然查询命中,但是查询结果中的ForumGroup的fourms集合是空的,因而会重新生成SQL查询。

 

另外,通过Debug,我发现,Query Cache缓存一个查询,其key就不多说了,在log中都会打出,其Value很有意思。上面的这个查询这的value是[5228208135548928, 1, 1, 2],第一个数据是一个时间戳,第二,三,四就是ForumGroup的ID。因为做过表连接,所以ForumGroup_1重复出现了一次。Forum的ID并没有在缓存的value中。由此可以确定,Query Cache只缓存结果集中对象的ID。被Fetch出来的Forum不被视为结果集的一部分,因而没有出现在结果集中。

 

还会有一些相似的问题可能会出现,比如:

 

相同条件第一次 list的时候,因为查询缓存中找不到,不管class缓存是否存在数据,总是发送一条sql语句到数据库获取全部数据,然后填充查询缓存和class缓存。但是第二次执行的时候,问题就来了,如果你的class缓存的超时时间比较短,现在class缓存都超时了,但是查询缓存还在,那么list方法在获取id串以后,将会一个一个去数据库load( N+1次 查询问题 )!因此,class缓存的超时时间一定不能短于查询缓存设置的超时时间!如果还设置了发呆时间的话,保证class缓存的发呆时间也大于查询缓存的生存时间。这里还有其他情况,比如class缓存被程序强制evict了,这种情况就请自己注意了。如果以上问题没有处理好,就必然会出现N+1次查询问题。

 

通过这个问题,总结两点:


1.查询缓存只保存储查询结果集中对象的ID,对象的实例存放在二级缓存中。这种缓存格局可能有两种不一致的情况发生:一.二级缓存中有实体对象,但是查询缓存中没有缓存某些关联对象的ID,这时个会导致重新生成SQL查询数据库。比如上面提到的Forums集合问题。二.查询缓存中有对象的ID,但是这些对象的实例却不在二级缓存中了。比如二级缓存已经失效等等,这也会导致生成SQL查询数据库。


2.对于集合字段,必须显式地使用@Cache标记,集合才会被缓存。注意,这里缓存的并不是集合的元素,而是元素的ID。集合元素能否被缓存取决于元素类有没有声明为可缓存的。 如果没有配制集合为可缓存,那么,即使在第一次查询时它们都进行入了二级缓存,下一次通过宿主类导航这个集合时还是会生成SQL,因为集合没有缓存,也就是所有元素的ID没有缓存,Hibernate不知道宿主对象关联的是那一些元素。虽然这些元素都已经在缓存中了。

 

从这个例子中我们可以看到:Cache的设置总是静态的全局的,不像Fetch那样可以动态重写。

本文转载自:http://blog.csdn.net/bluishglc/article/details/5667265

共有 人打赏支持
猪刚烈
粉丝 22
博文 708
码字总数 110
作品 1
海淀
程序员
私信 提问
Oracle 10046事件 介绍(二) ---tkprof

之前简单的写了10046事件介绍的文章http://hbxztc.blog.51cto.com/1587495/1898624,当然收集10046 trace不是最终的目的,能够读懂并且通过分析10046 trace进而分析相应sql的性能问题才是100...

技术小胖子
2017/11/07
0
0
5、Approximate timing for various operations on a typical PC

execute typical instruction 1/1,000,000,000 sec = 1 nanosec fetch from L1 cache memory 0.5 nanosec branch misprediction 5 nanosec fetch from L2 cache memory 7 nanosec Mutex lock......

hopegrace
2012/03/29
0
0
discuz 首页四格图不显示的解决方法

低版本升级导致,preforumthreadimage 表格为空 代码有bug,按如下修改代码: ~~~.phpdiff --git a/source/module/forum/forumindex.php b/source/module/forum/forumindex.phpindex 3bc08e5......

扣丁禅师
2016/05/31
15
0
执行apt-get upgrade遇到的错误

Failed to fetch http://172.16.19.246/ubuntuprecise163/pool/main/g/gcc-4.4/libstdc++64.4.3-4ubuntu5.1i386.deb rename failed, Input/output error (/var/cache/apt/archives/partial/l......

yandong
2013/01/15
805
2
Workbox 3:Service Worker 可以如此简单

如果你追求极致的 Web 体验,你一定在站点中使用过 PWA,也一定面临过在编写 Service Worker 代码时的犹豫不决,因为 Service Worker 太重要了,一旦注册在用户的浏览器,全站的请求都会被 ...

作者: 虚笔
08/08
0
0

没有更多内容

加载失败,请刷新页面

加载更多

Vue重要知识小结

vue sync修饰 (1)双向数据绑定,父子组件之间信息的交互 1⃣️在自组件中使用this.emmit('toFather'),子组件产生一个tofather事件,然后在父组件中通过@进行监听,那么可以实现通信过程 2⃣...

peakedness丶
35分钟前
1
0
1024我们的码农节-向自己致敬!

一、blog主有话要说 作为(真正)入赘程序届的第一年, 对明天的1024码农节有很多话想说.比如: 给各位辛苦大佬们讲几个咱们程序届段子 给自己立一个flag, 明年的1024争取少掉点甚至不掉头发! ...

Ala6
37分钟前
10
0
solr使用规范

0. 目的 规范solr设计、用法,避免bug,提高性能 1. 设计规范 solr的用途是查询,不是存储,建议查询结果尽量都为id主键,而后再拿该id主键到缓存或者db中再查询相关信息,例如:请勿将经销商...

andersChow
49分钟前
1
0
11-《深度拆解JVM》之Java对象的内存布局

一、问题引入 在 Java 程序中,我们拥有多种新建对象的方式。除了最为常见的 new 语句之外,我们还可以通过反射机制、Object.clone 方法、反序列化以及 Unsafe.allocateInstance 方法来新建对...

飞鱼说编程
53分钟前
1
0
Windows Install Docker

win7、win8 win7、win8 等需要利用 docker toolbox 来安装,国内可以使用阿里云的镜像来下载,下载地址:http://mirrors.aliyun.com/docker-toolbox/windows/docker-toolbox/ docker toolbox...

linuxprobe16
58分钟前
1
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部