Hazelcast原理及使用

本文涉及的产品
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
简介: Hazelcast原理及使用

大家在平常写代码用缓存的时候是不是要么是内存缓存,要么是redis的缓存?像内存缓存那种,最大的好处是不用去搭建什么缓存中间件,引入个jar包就能直接用了,缺点也很明显,分布式环境下内存只是单机的不能多个实例共享。redis那种好处就是解决了内存的不共享的问题,缺点也很明显,配置复杂还需要服务器啥的比较复杂。


有没有一个中间件能既可以用于分布式缓存,又不用搭建服务器就可以使用的呢?当然有,最近小编接触到了一个分布式缓存工具,结合了上面说的两大特性,感觉很牛逼,接下来给大家介绍一下:


Redis和Hazelcast进行了效率的对比,红色是Redis,蓝色是Hazelcast,根据结果。Redis在低数据负载的时候响应比 Hazelcast 表现更好,而在数据负载和并发请求增加时则表现相反。在不常见的大环境(如我们在脚本4)我们可以看 Redis的平均响应时间剧烈的增长。Hazelcast 响应时间虽然也随着线程数增加而增长,但是这种增长要稳定得多,而且不像 Redis 表现的那样是指数级。


1. Hazelcast原理

Hazelcast 提供了 Map、Queue、MultiMap、Set、List、Semaphore、Atomic 等常用接口的分布式实现。

2. Hazelcast存储数据的实现过程

2.1 Hazelcast分区

由于Hazelcast 服务之间是端对端的,没有主从之分,集群中所有的节点都存储等量的数据以及进行等量的计算。

Hazelcast 默认情况下把数据存储在 271 个区上,这个值可以通过系统属性 hazelcast.partition.count来配置。

2.2 Hazelcast分区存储原理

对于一个给定的键,在经过序列化、哈希并对分区总数取模之后能得到此键对应的分区号,所有的分区等量的分布与集群中所有的节点中,每个分区对应的备份也同样分布在集群中。

也就是说 Hazelcast 会使用哈希算法对数据进行分区,比如对于一个给定的map中的键,或者topic和list中的对象名称,分区存储的过程如下:

  • 先序列化此键或对象名称,得到一个byte数组;
  • 然后对上面得到的byte数组进行哈希运算;
  • 再进行取模后的值即为分区号;
  • 最后每个节点维护一个分区表,存储着分区号与节点之间的对应关系,这样每个节点都知道如何获取数据。

2.3 Hazelcast集群实现原理

Hazelcast通过分片来存储和管理所有进入集群的数据,采用分片的方案目标是保证数据可以快速被读写、通过冗余保证数据不会因节点退出而丢失、节点可线性扩展存储能力。下面将从理论上说明Hazelcast是如何进行分片管理的。

2.3.1 分片

Hazelcast的每个数据分片(shards)被称为一个分区(Partitions)。分区是一些内存段,根据系统内存容量的不同,每个这样的内存段都包含了几百到几千项数据条目,默认情况下,Hazelcast会把数据划分为271个分区,并且每个分区都有一个备份副本。当启动一个集群成员时,这271个分区将会一起被启动。下图展示了集群只有一个节点时的分区情况。

从一个节点的分区情况可以看出,当只启动一个节点时,所有的271个分区都存放在一个节点中。然后我们启动第二个节点,会出现下面这样的集群分区方式。

其中黑色的字体表示分区,蓝色的字体表示备份。节点1存储了标号为1到135的分区,这些分区会同时备份到节点2中。而节点2则存储了136到271的分区,并备份到了节点1中。

此时如果再添加2个新的节点到集群中,Hazelcast会一个一个的移动分区和备份到新的节点中,使得集群数据分布平衡。

注意:
实际中分区并不是有序的分布,而是随机分布,上面的示例只是为了方便理解,重要的是理解 Hazelcast 的平均分布分区以及备份。

2.4 重分区

集群中最老的节点(或者说最先启动)负责定时发送分区表到其他节点,这样如果有其他节点加入或者离开集群,所有的节点也能更新分区表。

这个定时任务时间间隔可以通过系统属性 hazelcast.partition.table.send.interval来配置,缺省值为15秒。重分区会发生在如下时间:

  • 节点加入集群时;
  • 节点离开集群时。

此时最老节点会更新分区表,然后分发,再接着集群开始移动分区,或者从备份恢复分区。

注意:

如果最老的节点挂了,次老节点会接手这个任务。

3. Hazelcast的使用方式

有两种方式,嵌入式和客户端服务器。

  • 嵌入式: Hazelcast 服务器的 jar 包被导入到宿主应用程序中,服务器启动后缓存数据会被存在于各个宿主应用中,优点是可以更低延迟的数据访问。

  • 客户端服务器: Hazelcast 客户端的 jar 包被导入宿主应用程序中,服务器 jar 包独立运行于 JVM 中。优点是更容易调试以及有更可靠的性能,最重要的是有更好的扩展性。



使用:


第一步:引入jar包

<dependency>
    <groupId>com.hazelcast</groupId>
    <artifactId>hazelcast-all</artifactId>
    <version>4.2.7</version>
</dependency>


第二步:创建一个工具类

package com.example.team6.util;
import com.hazelcast.core.HazelcastInstance;
import com.hazelcast.map.IMap;
import java.util.concurrent.TimeUnit;
public class HazelCastUtils {
    private final HazelcastInstance hazelcastInstance;
    private static final String MAP_NAME = "test:cache";
    public HazelCastUtils(HazelcastInstance hazelcastInstance) {
        this.hazelcastInstance = hazelcastInstance;
    }
    public Object get(String key) {
        IMap<Object, Object> map = hazelcastInstance.getMap(MAP_NAME);
        return map.get(key);
    }
    public void set(String key, String value, Integer ttl, TimeUnit timeUnit) {
        IMap<Object, Object> map = hazelcastInstance.getMap(MAP_NAME);
        map.put(key, value, ttl, timeUnit);
    }
    public void del(String key) {
        IMap<Object, Object> map = hazelcastInstance.getMap(MAP_NAME);
        map.remove(key);
    }
    public void delAllCache() {
        IMap<Object, Object> map = hazelcastInstance.getMap(MAP_NAME);
        map.clear();
    }
}


第三步:配置一个配置类

package com.example.team6.config;
import com.example.team6.util.HazelCastUtils;
import com.hazelcast.config.*;
import com.hazelcast.instance.impl.HazelcastInstanceFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
@Configuration
public class HazelcastConfiguration {
    @Bean
    @Primary
    public Config config() {
        EvictionConfig evictionConfig = new EvictionConfig();
        //数据释放策略[NONE|LRU|LFU]。这是Map作为缓存的一个参数,用于指定数据的回收算法,默认为NONE。
        //
        //NONE:当设置为NONE时,不会发生数据回收,同时max-size会失效。但是任然可以使用time-to-live-seconds和max-idle-seconds参数来控制数据留存时间。
        //
        //LRU:“最近最少使用“策略。
        //
        //LFU:“最不常用的使用”策略。
        evictionConfig.setEvictionPolicy(EvictionPolicy.LRU);
        evictionConfig.setMaxSizePolicy(MaxSizePolicy.FREE_HEAP_SIZE);
        Config config = new Config();
        config.setInstanceName("testInstanceName")
                .setClusterName("testClusterName")
                .addMapConfig(new MapConfig()
                        .setName("testName")
                        .setEvictionConfig(evictionConfig)
                        //数据留存时间[0~Integer.MAX_VALUE]。缓存相关参数,单位秒,
                        // 默认为0。这个参数决定了一条数据在map中的停留时间。
                        // 当数据在Map中留存超过这个时间并且没有被更新时,它会根据指定的回收策略从Map中移除。值为0时,意味着无求大。
                        .setTimeToLiveSeconds(10));
        return config;
    }
    @Bean
    public HazelCastUtils hazelCastUtils(Config config) {
        return new HazelCastUtils(HazelcastInstanceFactory.getOrCreateHazelcastInstance(config));
    }
}


第四步:创建一个controller来测试(只测试类String其他类型没有测)

package com.example.team6.controller;
import com.alibaba.fastjson.JSONObject;
import com.example.team6.util.HazelCastUtils;
import com.example.team6.util.UserDTO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.concurrent.TimeUnit;
@Slf4j
@RestController
@RequestMapping("/hazelcast")
public class HazelcastController {
    @Autowired
    private HazelCastUtils hazelCastUtils;
    @PostMapping(value = "/save")
    public String saveMapData(@RequestParam String key, @RequestParam String value) {
        UserDTO userDTO=UserDTO.builder().userId(1).userName(value).build();
        hazelCastUtils.set(key, JSONObject.toJSONString(userDTO), 60, TimeUnit.SECONDS);
        return "success";
    }
    @GetMapping(value = "/get")
    public Object getMapData(@RequestParam String key) {
        return hazelCastUtils.get(key);
    }
    @GetMapping(value = "/del")
    public String del(@RequestParam String key) {
        hazelCastUtils.del(key);
        return "success";
    }
    @GetMapping(value = "/clear")
    public String clear() {
        hazelCastUtils.delAllCache();
        return "success";
    }
}



最后,大家想获取更多知识的,可以继续关注公众号,不定时推送。分享了这么牛逼的知识,还不请小编喝个水吗,哈哈哈,欢迎土豪直接赏赞,谢谢,您的支持就是小编最大的动力。

相关文章
|
存储 缓存 Java
Infinispan篇(一):一个被遗忘了的分布式集群缓存系统
Infinispan 是一个开源内存数据网格,提供灵活的部署选项和强大的数据存储、管理和处理功能。
2722 0
|
Java API 网络架构
深入理解 Spring Boot 中的 @RestController 注解:概念与实践
【4月更文挑战第20天】在现代Web开发中,创建RESTful服务已成为常态。Spring Boot通过提供@RestController注解,极大简化了REST API的开发过程。本篇博客旨在详细介绍@RestController的概念、优势以及在Spring Boot项目中的具体应用方法。
982 8
|
存储 分布式计算 监控
Java一分钟之-Hazelcast:内存数据网格
【6月更文挑战第17天】**Hazelcast是开源的内存数据网格(IMDG),加速分布式环境中的数据访问,提供内存存储、分布式计算、线性扩展及高可用性。常见挑战包括内存管理、网络分区和数据分布不均。通过配置内存限制、优化网络和分区策略可避免问题。示例展示如何创建Hazelcast实例并使用分布式Map。使用Hazelcast提升性能和扩展性,关键在于理解和调优。**
509 1
|
缓存 Java Spring
Spring Boot中如何集成Hazelcast实现分布式缓存
Spring Boot中如何集成Hazelcast实现分布式缓存
|
Java 应用服务中间件 API
Vertx高并发理论原理以及对比SpringBoot
Vertx 是一个基于 Netty 的响应式工具包,不同于传统框架如 Spring,它的侵入性较小,甚至可在 Spring Boot 中使用。响应式编程(Reactive Programming)基于事件模式,通过事件流触发任务执行,其核心在于事件流 Stream。相比多线程异步,响应式编程能以更少线程完成更多任务,减少内存消耗与上下文切换开销,提高 CPU 利用率。Vertx 适用于高并发系统,如 IM 系统、高性能中间件及需要较少服务器支持大规模 WEB 应用的场景。随着 JDK 21 引入协程,未来 Tomcat 也将优化支持更高并发,降低响应式框架的必要性。
433 6
Vertx高并发理论原理以及对比SpringBoot
|
NoSQL 网络协议 Java
Redis客户端Lettuce深度分析介绍(上)
Spring Boot自2.0版本开始默认使用Lettuce作为Redis的客户端(注1)。Lettuce客户端基于Netty的NIO框架实现,对于大多数的Redis操作,只需要维持单一的连接即可高效支持业务端的并发请求 —— 这点与Jedis的连接池模式有很大不同。同时,Lettuce支持的特性更加全面,且其性能表现并不逊于,甚至优于Jedis。本文通过分析Lettuce的特性和内部实现(基于6.0版本),及其与Jedis的对照比较,对这两种客户端,以及Redis服务端进行深度探讨。
103092 8
|
存储 监控 安全
JVM工作原理与实战(四十):ZGC原理
JVM作为Java程序的运行环境,其负责解释和执行字节码,管理内存,确保安全,支持多线程和提供性能监控工具,以及确保程序的跨平台运行。本文主要介绍了ZGC、ZGC核心技术、ZGC的内存划分、ZGC的执行流程、分代ZGC的设计等内容。
881 1
|
存储 安全 Java
CopyOnWriteArrayList底层原理全面解析【建议收藏】
CopyOnWriteArrayList是Java中的一个线程安全的集合类,是ArrayList线程安全版本,主要通过Copy-On-Write(写时复制,简称COW)机制来保证线程安全。 Copy-On-Write机制核心思想:向一个数组中添加数据时,不直接操作原始数组,而是拷贝原始数组生成一份原始数组副本,将需要添加的数据添加到原始数组副本中,操作完成后再用原始数组副本直接替换原始数组,从而保证多个线程同时操作原始数组时的线程安全。
|
消息中间件 Java 网络架构
穿越消息之路:深入探讨Spring Integration的魅力
穿越消息之路:深入探讨Spring Integration的魅力
2304 0
|
存储 Java 大数据
Apache Arrow 新手上路
# 什么是Arrow [Apache Arrow](https://https://arrow.apache.org/)是一个开源的跨平台数据层开发框架,主要提供高效的、硬件加速的内存中数据计算能力。Apache Arrow的设计初衷是作为“新一代大数据系统的共享基础”,可以作为不同系统之间进行高效数据交换的媒介,同时提供快速、低延迟的数据访问接口。 Apache Arrow的主要目标是通过提
Apache Arrow 新手上路