文档章节

Cache与Fetch(二)

猪刚烈
 猪刚烈
发布于 2014/10/12 11:40
字数 1447
阅读 11
收藏 0
点赞 0
评论 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那样可以动态重写。

© 著作权归作者所有

共有 人打赏支持
猪刚烈
粉丝 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

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

PHP缓存的简单实现

闲着没事发个PHP缓存实现,实现了apc和文件缓存,继承Cache_Abstract即可实现调用第三方的缓存工具。 参考shindig的缓存类和apc。 <?php class CacheException extends Exception {} / 缓存抽...

墙头草 ⋅ 2012/01/13 ⋅ 0

discuz 首页四格图不显示的解决方法

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

扣丁禅师 ⋅ 2016/05/31 ⋅ 0

典型 PC 系统各种操作指令的大概时间

典型 PC 系统各种操作指令的大概时间 参考: http://news.cnblogs.com/n/155744/

hopegrace ⋅ 2012/09/02 ⋅ 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 ⋅ 2

elasticsearch aggregation 缓存

在工作中遇到一个问题,按照6个维度进行聚合,响应校验,平均需要300ms。看了相关资料后,采用以下方式解决: 启用数据压缩 transport.tcp.compress: true 2. 使用缓存 curl -XPUT 'localhos...

whk66668888 ⋅ 2017/07/14 ⋅ 0

Varnish 4.0 实战

简介 Varnish 是一款高性能且开源的反向代理服务器和 HTTP 加速器,其采用全新的软件体系机构,和现在的硬件体系紧密配合,与传统的 squid 相比,varnish 具有性能更高、速度更快、管理更加方...

jims ⋅ 2015/11/15 ⋅ 0

hibernate笔记

检索策略 区分类级别和关联级别检索 1)类级别检索:直接加载目标对象本身----Customer c = session.get(Customer.class,1); 2)关联级别检索:通过检索到的对象导航到关联的对象---Customer...

simpler ⋅ 2014/03/14 ⋅ 0

PHP加速 eAccelerator 配置和使用指南

前一段时间完成了服务器从FreeBSD4.10到6.1的升级,同时把PHP也升级到了最新的PHP5.1.4,Apache也升级到了最新的 Apache2.2,为了更好的提高系统的性能,考虑对PHP再进行一些优化,前两年接触...

红薯 ⋅ 2009/12/08 ⋅ 4

没有更多内容

加载失败,请刷新页面

加载更多

下一页

聊聊spring cloud的RequestRateLimiterGatewayFilter

序 本文主要研究一下spring cloud的RequestRateLimiterGatewayFilter GatewayAutoConfiguration @Configuration@ConditionalOnProperty(name = "spring.cloud.gateway.enabled", matchIfMi......

go4it ⋅ 52分钟前 ⋅ 0

Spring JavaConfig 注解

JavaConfig注解允许开发者将Bean的定义和配置放在Java类中。它是除使用XML文件定义和配置Bean外的另一种方案。 配置: 如一个Bean如果在XML文件可以这样配置: <bean id="helloBean" class="...

霍淇滨 ⋅ 今天 ⋅ 0

Spring clound 组件

Spring Cloud技术应用从场景上可以分为两大类:润物无声类和独挑大梁类。 润物无声,融合在每个微服务中、依赖其它组件并为其提供服务。 Ribbon,客户端负载均衡,特性有区域亲和、重试机制。...

英雄有梦没死就别停 ⋅ 今天 ⋅ 0

Confluence 6 重新获得站点备份文件

Confluence 将会创建备份,同时压缩 XML 文件后存储熬你的 <home-directory>/backups> 目录中。你需要自己访问你安装的 Confluence 服务器,并且从服务器上获得这个文件。 运行从 Confluence...

honeymose ⋅ 今天 ⋅ 0

informix的常用SQL语句

1、创建数据库 eg1. 创建不记录日志的库testdb,参考语句如下: CREATE DATABASE testdb; eg2. 创建带缓冲式的记录日志的数据库testdb(SQL语句不一定在事务之中,拥有者名字不被用于对象的解...

wangxuwei ⋅ 今天 ⋅ 0

matplotlib画图

最简单的入门是从类 MATLAB API 开始,它被设计成兼容 MATLAB 绘图函数。 from pylab import *from numpy import *x = linspace(0, 5, 10)y = x ** 2figure()plot(x, y, 'r')...

Dr_hu ⋅ 今天 ⋅ 0

RabbitMQ学习以及与Spring的集成(三)

本文介绍RabbitMQ与Spring的简单集成以及消息的发送和接收。 在RabbitMQ的Spring配置文件中,首先需要增加命名空间。 xmlns:rabbit="http://www.springframework.org/schema/rabbit" 其次是模...

onedotdot ⋅ 今天 ⋅ 0

JAVA实现仿微信红包分配规则

最近过年发红包拜年成为一种新的潮流,作为程序猿对算法的好奇远远要大于对红包的好奇,这里介绍一种自己想到的一种随机红包分配策略,还请大家多多指教。 算法介绍 一、红包金额限制 对于微...

小致dad ⋅ 今天 ⋅ 0

Python 数电表格格式化 xlutils xlwt xlrd的使用

需要安装 xlutils xlwt xlrd 格式化前 格式化后 代码 先copy读取的表格,然后按照一定的规则修改,将昵称中的学号提取出来替换昵称即可 from xlrd import open_workbookfrom xlutils.copy ...

阿豪boy ⋅ 今天 ⋅ 0

面试题:使用rand5()生成rand7()

前言 读研究生这3 年,思维与本科相比变化挺大的,这几年除了看论文、设计方案,更重要的是学会注重先思考、再实现,感觉更加成熟吧,不再像个小P孩,人年轻时总会心高气傲。有1 道面试题:给...

初雪之音 ⋅ 今天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部