Hazelcast持久化及优化

数据内存格式

参数:in-memory-format,有3种。

  • BINARY
    默认,数据k-v均二进制。
    适合绝大多数put、get等操作,效率最高。
  • OBJECT
    数据存储为对象反序列化,适合对象复杂查询操作,避免反序列时间消耗。
    k为二进制,v为对象。
  • NATIVE
    企业版,二进制格式,存储在非堆空间,避免GC。

    元数据策略

  • 参数:Metadata Policy

  • 自动预处理数据类型,以快速进行数据查询。

  • 仅支持json类型,开启时,对json对象添加额外属性。

  • 不影响其他类型的吞吐量。

  • 如果json类型不需要复杂查询,可以关闭,默认开启。

    1
    2
    3
    <map name="map-a">
    <metadata-policy>OFF</metadata-policy>
    </map>

数据更新

  • MapStore,持久化接口,将数据持久化。
  • MapLoader,数据加载接口。可以进行启动时的缓存初始化,采用多线程。
  • 实现接口不应使用hz的例如map各种操作,应使用持久化的方法,否则陷入死循环。

持久化策略

支持3种策略,Read-Through、Write-Through和Write-Behind。

  • 读时
    如不存在从数据库读,同时写入缓存

  • 写时
    写入缓存时,即时写入持久化,设置write-delay-seconds=0
    触发条件:写入、更新(修改、删除)、backup-count大于0

  • 写后
    写入缓存延迟时间后再写入持久化
    write-delay-seconds大于0
    异步执行,仅保存最后更新结果
    可以设置MapStoreConfig.setWriteCoalescingto为FALSE存储所有更新。
    超出负载抛出异常,停止异步存储。

删除方法

  • delete删除缓存
  • remove删除存储
  • evict删除缓存和存储

持久化实现

1
2
3
4
5
6
7
8
<map name="default">
<map-store enabled="true" initial-mode="LAZY">
<class-name>com.hazelcast.examples.DummyStore</class-name>
<write-delay-seconds>60</write-delay-seconds>
<write-batch-size>1000</write-batch-size>
<write-coalescing>true</write-coalescing>
</map-store>
</map>

class-name:MapLoader或MapStore的实现类

InitialLoadMode

MapLoader.loadAllKeys接口,预加载数据。

getMap()时提供InitialLoadMode模式,LAZY和EAGER。

  • LAZY,map创建时不加载所有,仅加载所触达的分区数据。

  • EAGER,map创建时所有分区全部加载。

1
2
3
4
5
6
7
8
9
10
11
12
<map name="default">
<backup-count>0</backup-count>
<async-backup-count>1</async-backup-count>
</map>
<map name="default">
<map-store enabled="true" initial-mode="LAZY">
<class-name>com.hazelcast.examples.DummyStore</class-name>
<write-delay-seconds>60</write-delay-seconds>
<write-batch-size>1000</write-batch-size>
<write-coalescing>true</write-coalescing>
</map-store>
</map>

Optimistic Locking乐观锁

使用

1
map.replace( key, oldValue, newValue )

near-cache本地cache

  • 在client端进行本地缓存,减少网络开销,提高性能
  • 同时支持member上的修改和删除等操作导致的脏数据同步过期。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
           <near-cache>
    <time-to-live-seconds>0</time-to-live-seconds>
    <max-idle-seconds>60</max-idle-seconds>
    <invalidate-on-change>true</invalidate-on-change>
    <in-memory-format>BINARY</in-memory-format>
    <cache-local-entries>false</cache-local-entries>
    <eviction size="1000" max-size-policy="ENTRY_COUNT" eviction-policy="LFU"/>
    </near-cache>
    ```

    - 适合本地大量读业务,例如client端
    - 不适合大量修改业务,否则开销更大
    - Near Cache不支持强一致性
    - member上开启时,将增大内存消耗
    - client或server均支持,各自也均需要配置
    - client hits时和server hits无关,此时server上空闲时间不受影响
    - client的name必须和member上一致,也可以用匹配符
    - 仅对get操作生效
    ### 一个机器一个member
    多节点在一个机器上性能不如一个节点一个机器,因为可能有大量的上下文切换。


    ### I/O线程
    Hazelcast采用多线程的线程池进行IO操作。

    每个member拆分3中线程类型:
    - 接收请求
    - 读取数据,从member或client
    - 写数据,到member或client

    `hazelcast.io.thread.count`配置每个member线程数默认是3.

    默认3个线程数情况下,总计是7个io线程,1个接收数据,3个读,3个写。每个io线程有自己的Selector和waits。

    ### Back Pressure背压
    解决负载和操作导致的OOM问题
    - 设置并发操作数
    - 设置定期异步备份

    ### Flake ID Generator
    在member上的操作总是本地的。

    在client上操作newId(),在指定的时间,会在本地随机一个member和获取批量的ids。

    批量数和有效期在每个client及member均可指定。

    配置
    ```xml
    <flake-id-generator name="default">
    <prefetch-count>100</prefetch-count>
    <prefetch-validity-millis>600000</prefetch-validity-millis>
    </flake-id-generator>

全局序列化

client端代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class GlobalStreamSerializer implements StreamSerializer<Object> {

@Override
public void write(ObjectDataOutput out, Object oo) throws IOException {
out.writeUTF(oo.getClass().getName());
out.writeByteArray(new ObjectMapper().writeValueAsBytes(oo));
}

@Override
public Object read(ObjectDataInput in) throws IOException {
String clazz = in.readUTF();
try {
return new ObjectMapper().readValue(in.readByteArray(), Class.forName(clazz));
} catch (ClassNotFoundException e) {
throw ExceptionUtil.peel(e);
}
}

@Override
public int getTypeId() {
return 1;
}

@Override
public void destroy() {
}
}

1
2
3
4
5
6
<user-code-deployment enabled="true">
<classNames>
<!-- for classes available in client's class path -->
<className>xx.cache.client.GlobalStreamSerializer</className>
</classNames>
</user-code-deployment>
------ 本文结束------

本文标题:Hazelcast持久化及优化

文章作者:Perkins

发布时间:2019年11月11日

原始链接:https://perkins4j2.github.io/posts/53779/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。