Hazelcast源码剖析之Eviction
v:* { }
o:* { }
w:* { }
.shape { }p.MsoNormal,li.MsoNormal,div.MsoNormal { margin: 0cm; margin-bottom: .0001pt; text-align: justify; font-size: 10.5pt; font-family: "Calibri", "sans-serif" }
h2 { margin-top: 13.0pt; margin-right: 0cm; margin-bottom: 13.0pt; margin-left: 0cm; text-align: justify; line-height: 173%; page-break-after: avoid; font-size: 16.0pt; font-family: "Calibri Light", "sans-serif" }
h3 { margin-top: 13.0pt; margin-right: 0cm; margin-bottom: 13.0pt; margin-left: 0cm; text-align: justify; line-height: 173%; page-break-after: avoid; font-size: 16.0pt; font-family: "Calibri", "sans-serif" }
span.tag { }
span.1
{ }
span.attribute { }
span.value { }
span.2Char
{ font-family: "Calibri Light", "sans-serif"; font-weight: bold }
span.3Char
{ font-weight: bold }
.MsoChpDefault { font-family: "Calibri", "sans-serif" }
div.WordSection1 { }table.MsoNormalTable { font-size: 10.5pt; font-family: "Calibri", "sans-serif" }
1 奇怪的现象
在使用Hazelcast的Eviction时,发现观察到的现象与想象的不同。按照官方文档介绍,Eviction有这样几个配置选项:
<hazelcast>
<map name="default">
...
<time-to-live-seconds>0</time-to-live-seconds>
<max-idle-seconds>0</max-idle-seconds>
<eviction-policy>LRU</eviction-policy>
<max-size policy="PER_NODE">5000</max-size>
<eviction-percentage>25</eviction-percentage>
...
</map>
</hazelcast>
看后三项,按照参数的描述应为:当每个结点的entry数达到5000时,使用LRU策略,剔除25%的entry,即1250个。可是观察到的现象一般是entry数达到4200左右就止步不前,继续大量高并发的insert测试,既不会增长也不会减少。这是怎么回事?而且相比Redis,Hazelcast在达到eviction临界条件后继续并发插入和读写时,性能表现依旧良好,就像没有发生eviction一样。是使用了多线程还是什么神奇的算法?源码之前,了无秘密,还是从代码中寻找答案吧。
2 代码剖析
我们客户端使用的,也是最顶层的API,就是IMap了,这里以比put()更为高效的set()作为分析的起点。后面其实能够看到,因为使用了命令模式(command),两者都是继承一个父类,evict都是在一个地方触发的。
2.1 MapProxyImpl包装器
IMap的直接实现类是MapProxyImpl,但它只是个Wrapper,负责转换key和value。
2.2 MapProxySupport封装命令对象
真正的实现都是在它继承的MapProxySupport类中,例如set()调用的setInternal()就能在Support类中找到。各种internal方法将操作包装成Operation类,这方便了远程调用的实现,例如set()要执行的结点不在本地。
2.3 SetOperation命令模式
invokeOperation()中会确定操作应该在哪个分区执行,这个分区位于哪个结点上。这里Hazelcast维护了一个线程池,每个分区都有对应的线程去执行本分区的操作。因为过程比较复杂,所以这里直接略过,继续关注我们重点想知道的eviction过程的实现。那么直接看一下SetOperation中的逻辑。SetOperation很简单,直接调用RecordStore保存键值对,但afterRun()中有一些隐含的后处理。
2.4 BasePutOperation触发eviction
果然,在afterRun()中除了广播事件、使Near缓存失效外,还有触发eviction过程。Evict()调用的就是RecordStore的evictEntries()方法。
2.5 AbstractEvictableRecordStore控制eviction
真正的evict控制逻辑就在这里。首先,shouldEvict()会判断是否满足了我们之前配置的eviction的触发条件,如PER_NODE=5000。如果满足则调用removeEvictableRecords()开始剔除数据。
2.6 EvictionOperator事有蹊跷
最终removeEvictableRecords调用的是EvictionOperator,具体的实现都在这里。但仔细看这段代码却看不出有什么高明之处,只是简单地迭代RecordStore的记录,将满足条件的entry剔除掉。既没用多线程,也没什么特殊的算法,这到底是怎么回事?
2.7 答案揭晓
谜底其实就在Operation类对应的RecordStore初始化上。我们知道,默认情况下,Hazelcast将map分为271个partition。其实RecordStore也是按这些partition划分的,而不是使用一个大的RecordStore。所以从BasePutOperation的evict()到后续处理的都只是当前key对应分区的RecordStore。也就是说:当key要被处理时,Eviction发生在对应的partition里,而不会evict所有数据的25%(Redis就是处理database中的所有数据,所以延迟会有所增加)。所以,当我们继续压力测试时,不断有key继续插入,这些分区就会不断发生eviction,导致整体的内存使用会保持不变。
3 类图全貌
梳理了上面的执行流程后,我们最后整理一下这些类之间的关系。
Hazelcast源码剖析之Eviction的更多相关文章
- jQuery之Deferred源码剖析
一.前言 大约在夏季,我们谈过ES6的Promise(详见here),其实在ES6前jQuery早就有了Promise,也就是我们所知道的Deferred对象,宗旨当然也和ES6的Promise一样, ...
- Nodejs事件引擎libuv源码剖析之:高效线程池(threadpool)的实现
声明:本文为原创博文,转载请注明出处. Nodejs编程是全异步的,这就意味着我们不必每次都阻塞等待该次操作的结果,而事件完成(就绪)时会主动回调通知我们.在网络编程中,一般都是基于Reactor线程 ...
- Apache Spark源码剖析
Apache Spark源码剖析(全面系统介绍Spark源码,提供分析源码的实用技巧和合理的阅读顺序,充分了解Spark的设计思想和运行机理) 许鹏 著 ISBN 978-7-121-25420- ...
- 基于mybatis-generator-core 1.3.5项目的修订版以及源码剖析
项目简单说明 mybatis-generator,是根据数据库表.字段反向生成实体类等代码文件.我在国庆时候,没事剖析了mybatis-generator-core源码,写了相当详细的中文注释,可以去 ...
- STL"源码"剖析-重点知识总结
STL是C++重要的组件之一,大学时看过<STL源码剖析>这本书,这几天复习了一下,总结出以下LZ认为比较重要的知识点,内容有点略多 :) 1.STL概述 STL提供六大组件,彼此可以组合 ...
- SpringMVC源码剖析(四)- DispatcherServlet请求转发的实现
SpringMVC完成初始化流程之后,就进入Servlet标准生命周期的第二个阶段,即“service”阶段.在“service”阶段中,每一次Http请求到来,容器都会启动一个请求线程,通过serv ...
- 自己实现多线程的socket,socketserver源码剖析
1,IO多路复用 三种多路复用的机制:select.poll.epoll 用的多的两个:select和epoll 简单的说就是:1,select和poll所有平台都支持,epoll只有linux支持2 ...
- Java多线程9:ThreadLocal源码剖析
ThreadLocal源码剖析 ThreadLocal其实比较简单,因为类里就三个public方法:set(T value).get().remove().先剖析源码清楚地知道ThreadLocal是 ...
- JS魔法堂:mmDeferred源码剖析
一.前言 avalon.js的影响力愈发强劲,而作为子模块之一的mmDeferred必然成为异步调用模式学习之旅的又一站呢!本文将记录我对mmDeferred的认识,若有纰漏请各位指正,谢谢.项目请见 ...
随机推荐
- java中关于&、&&、|、||之间的区别和运算
关于&.&&.|.||之间的区别和运算 在逻辑运算中: 普通与&:要判断所有的判断的条件 短路与&&:如果前面一个判断条件出现false,则后续的判断条 ...
- 报错django.db.migrations.exceptions.InconsistentMigrationHistory
Pycharm强大的功能总是让我很是着迷,比如它的makemigrations 和 migrate. 然而某一次,当我再次敲下这熟悉的命令时,它报错了.... Traceback (most rece ...
- codevs 搜索题汇总(青铜+白银级)
1792 分解质因数 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 青铜 Bronze 题目描述 Description 编写一个把整数N分解为质因数乘积的程序. 输入描 ...
- bzoj4919 [Lydsy1706月赛]大根堆
Description 给定一棵n个节点的有根树,编号依次为1到n,其中1号点为根节点.每个点有一个权值v_i. 你需要将这棵树转化成一个大根堆.确切地说,你需要选择尽可能多的节点,满足大根堆的性质: ...
- [HNOI2014]画框
题目描述 小T准备在家里摆放几幅画,为此他买来了N幅画和N个画框.为了体现他的品味,小T希望能合理地搭配画与画框,使得其显得既不过于平庸也不太违和. 对于第 幅画与第 个画框的配对,小T都给出了这个配 ...
- 2015 多校联赛 ——HDU5305(搜索)
Friends Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others) Total Su ...
- solr6.6初探之分词篇
关于solr6.6搭建与配置可以参考 solr6.6初探之配置篇 在这里我们探讨一下分词的配置 一.关于分词 1.分词是指将一个中文词语拆成若干个词,提供搜索引擎进行查找,比如说:北京大学 是一个词那 ...
- Java正则过滤
import java.util.regex.Matcher; import java.util.regex.Pattern; public class LongStringtonumber { pu ...
- Nginx+Tomca+Redis实现负载均衡、资源分离、session共享
目标实现:Nginx作为负载均衡后端多Tomcat实例,通过Redis实现Session共享. 操作系统环境:CentOS 6.8 SSH:SecureCRT 其中 Nginx服务:80端口 Tomc ...
- html文本encode后,js获取参数失败的bug
html中的空格encodeURIComponent后变成%C2%A0,而js中的空格是'%20',二者无法匹配,所以要进行一次替换