JPA,EclipseLink 缓存机制学习(一) 树节点搜索问题引发的思考
最近在项目在使用JPA+EclipseLink 的方式进行开发,其中EclipseLink使用版本为2.5.1。遇到一些缓存方面使用不当造成的问题,从本篇开始逐步学习EclipseLink的缓存机制。
一、树节点搜索问题出现
故事是这样的:项目中有一个对树节点搜索的需求,如下图中所示,按照前缀匹配查询节点名称中包含OK的节点,将返回下图中的数据结构。基本实现就是:1.先查找出OK节点,然后根据该节点的父节点id获得父节点,也就是PERSON2结点,同样按照PERSON2的父结点id找到节点ROOT。结点的类图大概是这样子的,每获得父节点后,会将本身节点加入到父结点children集合中去。
(图a)
一开始按照特定顺序在操作中出现奇怪的现象。
step 1. 检索包含OK的结点 显示正常,如图b所示。
step 2. 检索包含PERSON名称的节点,结果如图c所示。
step 3. 又一次检索OK的节点,结果奇怪的结果出现了,见图d。理想中的结果数据是不包含PERSON1的,这显然是一个缓存导致的问题。
(图b)
(图c)
(图d)
二、EclipseLink 缓存架构

其中几个基本概念:
1. Session Cache,即 Persistence Unit Cache,属于系统级的缓存,所有client共享数据。数据来源主要有两种:
a)通过EntityManager进行数据读入时,会将读入的数据拷贝一份放到Session Cache;
b) Unit of Work Cache 进行Commit操作成功后,将数据同步到Session Cache中;
2. Unit of Work Cache,即Persistence Context Cache,属于隔离型缓存,属于单个client。由EntityManager进行管理,Persistence Context中保存着读取过的Entity实例。当对Entity执行了Commit操作后会将更改后对数据同步到Session Cache。Flush,Clear操作不会触发同步Session Cache操作。
3. 数据查询过程。EntityManager根据实例唯一标识查看Persistence Context Cache中是否存在该实例,如果存在则返回该实例。如果不存在,则查询Persistence Unit Cache中是否存在该实例,如果存在则直接返回该实例,如果不存在则访问数据库进行查询,得到数据结果后将此数据保存到Persistence Unit Cache 和 Persistence Context Cache中。
三、Entity缓存隔离类型(决定是否将查询数据存入到共享缓存中)
1.SHARED
共享缓存,也是系统默认的配置。EnittyManger在进行查询操作的时后,返回的数据会在Session Cache/EntityManagerFactory中保存一部分。
2.PROTECTED
被保护的。EnittyManger在进行查询操作的时后,返回的数据并不会在Session Cache中保存完整的Entity实例,只会保存Entity状态信息。
3.ISOLATED
隔离的。EnittyManger在进行查询操作的时后,返回的数据不会在Session Cache/EntityManagerFactory中保存,只保存在Persistence Context Cache中。
四、Entity缓存类型(决定缓存有效时间)
1.FULL
将缓存该Entity的所有查询到的实例,随时可以通过唯一标识通过缓存进行获取。由于该缓存数据不会被gc清除,所以要当心,对于数量庞大的Entity实例对象有可能导致Out Of Memory。
2.WEAK
将该缓存的所有Entity实例以Weak Reference的引用方式进行缓存。当gc执行的时候,缓存将被清除。
3.SOFT
将该缓存的所有Entity实例以Soft Reference的引用方式进行缓存。当JVM可用内存过低时将对这部分缓存进行释放。
4.SOFT_WEAK
此为系统默认配置。将该缓存的所有Entity实例以Soft Reference 与 Weak Reference 相结合的方式进行缓存。此缓存类型和WEAK类型的区别在于,存在一个基于Soft Reference的most-frequently-used的子缓存,读取最频繁的Entity实例将保存在这里,避免gc回收读取频繁的Entity实例。该子缓冲区大小可以通过设置@Cache中的size属性来调节。
5.HARD_WEAK
此缓存类型和SOFT_WEAK很相似,区别在于子缓存区用Hard Reference 进行存储,保证即使JVM内存过低,也不会清除这部分缓存数据。
6.CACHE
缓存唯一标识map维护一个指定大小的缓存,对象被移出缓存策略按照LRU ( Least Recently Used)。
7.NONE
对该Entity不进行任何缓存。
五、搜索问题出现的原因
使用了Spring-data-JPA,持久化层通过继承接口org.springframework.data.jpa.repository.JpaRepository实现。根据debug结果发现实现类为org.springframework.data.jpa.repository.support.SimpleJpaRepository。其中每个SimpleJpaRepository实例有一个EntityManager,且在该Repository实例化后服务期间EntityManager类型的成员变量没有变化(HashCode相同)。可以认为使用的是同一个Persistence Context Cache。根据前文对缓存的分析,Node类型缓存隔离类型为SHARED,缓存类型为SOFT_WEAK,每次根据id对Node的查询都要先从Persistence Context Cache中找,结果step 2中对ROOT节点设置完Children,step 3中直接又从Persistence Context Cache中得到了该ROOT对象,导致无效数据(step 3)的出现。
六、解决方案
我第一想到的就是不要让Node类型被缓存起来,即在Entity类型中增加@Cache(type=CacheType.NONE)。问题虽然按照这种方式解决了,但是带来效率的下降。比如Node节点有获取整棵树数据的方法。具体优化方案将在后续JPA后续文章中给出。
七、参考
EclipseLink Cache
https://www.eclipse.org/eclipselink/documentation/2.5/concepts/cache001.htm
EclipseLink 源码地址:
https://www.eclipse.org/eclipselink/downloads/previous_releases.php
八、结语
欢迎大家吐槽、拍砖、指正 ^_^
JPA,EclipseLink 缓存机制学习(一) 树节点搜索问题引发的思考的更多相关文章
- JPA,EclipseLink 缓存机制学习——树节点搜索问题引发的思考
最近在项目在使用JPA+EclipseLink 的方式进行开发,其中EclipseLink使用版本为2.5.1.遇到一些缓存方面使用不当造成的问题,从本篇开始逐步学习EclipseLink的缓存机制. ...
- Linux 系统缓存机制学习
前言:本文为参考他人的文章,是一篇学习记录型博客.理解linux的系统缓存机制有助于理解elasticsearch实时更新的原理. 一.缓存机制 为了提高文件系统性能,内核利用一部分物理内存分配出缓冲 ...
- H5缓存机制学习记录
参考文章:http://mp.weixin.qq.com/s?__biz=MTEwNTM0ODI0MQ==&mid=404724239&idx=1&sn=e0a2887f9ff ...
- RecyclerView 缓存机制学习笔记2
RecyclerView 初始化所有的视图后,调用 去缓存(StaggeredGridLayoutManager), 而不是初始化一次缓存一次 存储后系统又会去调用tryGetViewHolderFo ...
- RecyclerView 缓存机制学习笔记1
盗用别人图片 获取VIew的方法的流程 最先调用 其次调用 这个方法调用会先去缓存 这个是是否有动画,有动画就去里面取. 如果取不到就接着调用 如果在没有继续调用 都取不到就去实例化 调用的次数取决于 ...
- MongoDB一次节点宕机引发的思考(源码剖析)
目录 简介 日志分析 副本集 如何实现 Failover 心跳的实现 electionTimeout 定时器 业务影响评估 参考链接 声明:本文同步发表于 MongoDB 中文社区,传送门: http ...
- MongoDB一次节点宕机引发的思考(源码剖析)【华为云分享】
目录 简介 日志分析 副本集 如何实现 Failover 心跳的实现 electionTimeout 定时器 业务影响评估 参考链接 声明:本文同步发表于 MongoDB 中文社区,传送门:http: ...
- CDN缓存机制
CDN也叫内容分发网络,是一个经策略性部署的整体系统,包括分布式储存.负载均衡.网络请求的重定向和内容管理4个要件.而其中内容管理和全局的网络流量管理是CDN的核心所在.通过用户就进行和服务器负载的判 ...
- C#无限极分类树-创建-排序-读取 用Asp.Net Core+EF实现之方法二:加入缓存机制
在上一篇文章中我用递归方法实现了管理菜单,在上一节我也提到要考虑用缓存,也算是学习一下.Net Core的缓存机制. 关于.Net Core的缓存,官方有三种实现: 1.In Memory Cachi ...
随机推荐
- C++笔记(一)
一.动态数组 一般我们定义数组都是用下面的方式: int str[100]; 但对于一些需要手动输入数组大小的情况,这种定义方式就行不通了.因为上面这种方式要求事先知道数组大小,并且给数组中括号中必须 ...
- WEB启动时就加载servlet的dopost方法
web启动的时候可以加载servlet的init方法,无法加载dopost方法,如果你需要什么内容在启动的时候执行,可以将内容放到init方法里面,dopost方法,是在客户端使用post请求的时候才 ...
- Server.MapPath和Request.PhysicalApplicationPath的异同
很多人对它们都不陌生,在众多的WEB程序中,使用Server.MapPath和Request.PhysicalApplicationPath来操作目录/文件的几率参半,我曾经也经常混用,然而时间久了. ...
- 2.多线程-GCD
1.基本概念 同步任务:在当前线程按顺序执行,不开启新的线程 异步任务:有开新线程的欲望 串行队列:一个一个执行 并行队列:多个任务同时执行 --------------------------- ...
- apiCloud又拍云数据库操作经验
1.在数据库表中有个relation字段,表示一对多(对应其他表的多个记录),这个字段比较奇怪,因为实际上这个字段里并未保存实际值.而是保存在对应表的字段中.但是对应表显示出来并没有这个字段.导出数据 ...
- APICloud开发App总结(一)
apiCloud app 开发是最近一两年刚刚兴起的一种混合开发方式.常用的模块以原生方式开发好,然后用js进行粘合.组织,完成整个的app的逻辑.这种开发方式极大的提高了软件模块的复用率,加快了ap ...
- python 学习(json)(转)
Json简介:Json,全名 JavaScript Object Notation,是一种轻量级的数据交换格式.Json最广泛的应用是作为AJAX中web服务器和客户端的通讯的数据格式.现在也常用于h ...
- babel 解构赋值无法问题
这个东西需要第二级, babel-preset-stage-2,然后再presets里引入stage-2的设置,再plugins离引入对应的插件 { "presets": [&qu ...
- Mvc 模块化开发
在Mvc中,标准的模块化开发方式是使用Areas,每一个Area都可以注册自己的路由,使用自己的控件器与视图.但是在具体使用上它有如下两个限制 1.必须把视图文件放到主项目的Areas文件夹下才能生效 ...
- 关于HandlerThread的分析
Android中的Thread没有对java中的Thread做任何封装,而Android提供了一个遍历方法HandlerThread,他继承于Thread,实现了对遍历系统的一些封装,下面研究一下Ha ...