Hazelcast系列(九):Map(一)加载和存储

news2024/11/26 15:22:34

系列文章

Hazelcast系列(一):初识hazelcast

Hazelcast系列(二):hazelcast集成(嵌入式)

Hazelcast系列(三):hazelcast集成(服务器/客户端)

Hazelcast系列(四):hazelcast管理中心

Hazelcast系列(五):Auto-Detection发现机制

Hazelcast系列(六):Multicast发现机制

Hazelcast系列(七):TCP-IP发现机制

Hazelcast系列(八):数据结构

Hazelcast系列(九):Map(一)加载和存储

目录

        前言

        加载和存储

                加载

                存储

        应用

                环境

                实战

        测试 

                测试加载

                测试存储

        其他 


前言

Hazelcast的数据结构众多,这里就以 Map 为例,探讨一下 Hazelcast 数据结构操作相关的东西。

Hazelcast 针对数据结构的东西还是蛮多,这里咱们拆分为多个节点来阐述。

Hazelcast系列(十):Map(一)主要探讨 Map 加载、存储以及配置

Hazelcast系列(十):Map(二)主要探讨 Map 监听器、拦截器以及谓词

Hazelcast系列(九):Map(三)主要探讨 Map 备份、过期和驱逐以及内存格式

正式开始前,还是对 Map 做一个大概的描述。

Hazelcast Map( IMap) 扩展了接口java.util.concurrent.ConcurrentMap ,因此 java.util.Map,它是Java地图的分布式实现。可以使用众所周知的 get put 方法执行诸如从 Hazelcast Map读取和写入 Hazelcast Map等操作。

当前 Hazelcast版本:5.1.7,Hazelcast模式:嵌入式

加载和存储

在某些情况下,可能希望从关系数据库中加载数据和存储数据在关系数据库中。

Hazelcast 最常见的实现之一是作为外部存储系统(例如关系数据库)的前端缓存。Hazelcast 提供了一种方法,可以自动将数据加载到内存映射中,以实现更快的应用程序访问,并自动将更新写回外部存储以保持系统同步。

访问内存中数据的应用程序使用获取、放置或执行查询。当使用外部数据存储时,Hazelcast 集群成员独立于应用程序从该外部数据存储检索数据或向该外部数据存储写入数据。

Hazelcast 提供了两个接口用于加载和存储:

MapLoader: 加载数据,当应用程序从地图请求值时,将调用该接口方法。如果内存中不存在请求的值,MapLoader.load() 方法会尝试从数据存储中加载它。一旦加载到内存中,映射数据就会一直保留,直到它被更改、movd 或逐出

MapStore:存储数据,继承了 MapLoader,所以该接口有 MapLoader 的全部功能,此外,该接口方法将对内存中映射数据的更新复制到数据存储

加载

通读持久性

实现 MapLoader 时,如果内存中不存在所请求的数据,map.get() 则会触发该方法,然后 load() 检索请求的数据,将其添加到 Hazelcast 内存中,这种自动加载称为通读持久性

Map初始化

通读持久性将从外部数据存储中检索所请求的映射数据,但按请求检索每个单独的数据效率很低。相反,使用 MapLoader.loadAllKeys() 方法来预先填充内存中的映射效率很高。使用时,每个 Hazelcast 集群成员都会连接到数据库以检索其拥有的地图部分,这种并行处理是从数据存储中检索数据的最快方法。

Map 初始化方式有两种:EAGER 和 LAZY

EAGER:第一次创建或获取地图时会调用 MapLoader.loadAllKeys() 方法。所有集群成员都连接到数据库并加载其所有本地活动分区。这是一个阻塞操作;在所有集群成员加载其分区之前,应用程序将无法读取或写入映射。

LAZY:获取或创建地图后,当您首次使用的 IMap 操作之一访问地图时,会触发 MapLoader.loadAllKeys() 方法。LAZY 是默认模式。

加载过程

1. 初始化根据 initial-mode 属性的值开始。如果设置为 EAGER,则一旦创建映射(即调用时map.get() ),就会在所有分区上开始初始化。如果设置为 LAZY,则当某个操作或其他操作尝试从映射中读取时,将加载数据。

2. 接下来,MapLoader.loadAllKeys() 以获取其中一名成员的所有 Key。

3. 该集群成员将密钥批量分发给集群中其他成员。

4. 每个成员通过调用 MapLoader.loadAll(keys) 自己的密钥来加载其所有密钥的值。

5. 每个成员通过调用将其拥有的数据放入映射中 IMap.putTransient(key,value)

存储

直写持久性

直写持久性对映射和外部数据存储执行同步更新。实施后,map.put(key,value) 调用会按顺序触发以下操作:

1. 调用 MapStore.store(key,value) 这会将数据入外部数据存储。

2. 将数据写入内存中的主map

3. backup-count 如果已配置(即如果属性大于 0),则将数据写入备份map

后写持久性

后写持久性对外部数据存储执行异步更新。触发后,map.put(key,value) 调用会按顺序触发以下操作:

1. 将数据写入内存中的主map

2. 将数据写入备份map(如果已配置)。

3. 将数据标记为“脏数据”,这时数据尚未写入外部存储。

4. 经过一段时间后,即 write-delay-seconds 时间后,调用 MapStore.storeAll 将数据写入外部存储 

应用

环境

Pom

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.16</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.hazelcast</groupId>
    <artifactId>HazelCastSpringBootDemo1</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>HazelCastSpringBootDemo1</name>
    <description>HazelCastSpringBootDemo1</description>
    <properties>
        <java.version>8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>com.hazelcast</groupId>
            <artifactId>hazelcast</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.31</version>
        </dependency>

        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.3.2</version>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

服务器 

两台,当前是两个项目在两个不同的端口启动

实战

  • 配置 hazelcast.yml
hazelcast:
  cluster-name: hazelcast-cluster
  instance-name: hzInstance_local
  network:
    port:
      auto-increment: true
      port-count: 100
      port: 5701
    outbound-ports:
      - 0
    join:
      auto-detection:
        enabled: false
      multicast:
        enabled: false
        multicast-group: 224.2.2.3
        multicast-port: 54327
      tcp-ip:
        enabled: true
        member-list:
          - 192.168.119.1
    interfaces:
      enabled: true
      interfaces:
        - 192.168.*.*
  map:
    mapcache:
      backup-count: 1
      async-backup-count: 1
      map-store:
        enabled: true
        initial-mode: LAZY
        class-name: com.hazelcast.hazelcastspringbootdemo1.config.HazelcastMapDataLoader
        write-delay-seconds: 2
        write-batch-size: 1000
        write-coalescing: true

map之前的都是集群配置,这里不再赘述

说明:map.mapcache  生成一个名称为mapcache的map

           map.mapcache.backup-count  同步备份数量,默认为1,最大为6

           map.mapcache.async-backup-count  异步备份数量,默认为0

           map.mapcache.map-store.enabled  启用此MapStore配置

           map.mapcache.map-store.initial-mode  加载模式,默认为LAZY

           map.mapcache.map-store.class-name  实现MapLoader/MapStore配置类

           map.mapcache.map-store.write-delay-seconds  延迟写入毫秒数,为0为直写,否则为后写

           map.mapcache.map-store.write-batch-size  批处理大小,后写模式下生效,最小为2

           map.mapcache.map-store.write-coalescing 合并键更新操作,后写模式下生效,只保留最后一次更新

  注意:1. Hazelcast 有一个默认名称为 default 的 Map,没额外需求可直接使用默认的,并配置

             2. Hazelcast.map 下可以有多个map配置

  • 配置MapStore


/**
 * Hazelcast缓存map加载配置类
 */
public class HazelcastMapDataLoader implements MapStore<Long, StudentPo> {


    @Override
    public void store(Long no, StudentPo resumePo) {
        SpringUtils.getBean(StudentMapper.class).insert(resumePo);
    }

    @Override
    public void storeAll(Map<Long, StudentPo> map) {
        if (Objects.nonNull(map) && map.isEmpty()) {
            return;
        }
        //TODO 可批量插
        StudentMapper studentMapper = SpringUtils.getBean(StudentMapper.class);
        map.values().stream().forEach(item -> {
            studentMapper.insert(item);
        });
    }

    @Override
    public void delete(Long no) {
        LambdaQueryWrapper<StudentPo> queryWrapper = Wrappers.<StudentPo>lambdaQuery()
                .eq(StudentPo::getNo, no);
        StudentMapper studentMapper = SpringUtils.getBean(StudentMapper.class);
        studentMapper.delete(queryWrapper);
    }

    @Override
    public void deleteAll(Collection<Long> collection) {
        LambdaQueryWrapper<StudentPo> queryWrapper = Wrappers.<StudentPo>lambdaQuery()
                .in(StudentPo::getNo, collection);
        StudentMapper studentMapper = SpringUtils.getBean(StudentMapper.class);
        studentMapper.delete(queryWrapper);
    }

    @Override
    public StudentPo load(Long no) {
        LambdaQueryWrapper<StudentPo> queryWrapper = Wrappers.<StudentPo>lambdaQuery()
                .eq(StudentPo::getNo, no);
        StudentMapper studentMapper = SpringUtils.getBean(StudentMapper.class);
        return studentMapper.selectOne(queryWrapper);
    }

    @Override
    public Map<Long, StudentPo> loadAll(Collection<Long> collection) {
        LambdaQueryWrapper<StudentPo> queryWrapper = Wrappers.<StudentPo>lambdaQuery()
                .in(StudentPo::getNo, collection);
        StudentMapper studentMapper = SpringUtils.getBean(StudentMapper.class);
        List<StudentPo> resultPoList = studentMapper.selectList(queryWrapper);
        return resultPoList.parallelStream().collect(Collectors.toMap(StudentPo::getNo, Function.identity()));
    }

    @Override
    public Iterable<Long> loadAllKeys() {
        //TODO 可自定义sql查
        StudentMapper studentMapper = SpringUtils.getBean(StudentMapper.class);
        List<StudentPo> resultPoList = studentMapper.selectList(new QueryWrapper<>());
        return resultPoList.parallelStream().map(StudentPo::getNo).collect(Collectors.toList());
    }
}

  说明:1. 这里类必须要实现MapLoader/MapStore

             2. 这里配合 Mybatis-Plus 操作数据库,通过 SpringUtils 获取 Mapper Bean 调用

             3. 这里操作的 key 必须唯一并且一致 

  • 下面就是一些项目相关类和配置,可跳过,项目相关Po
@Data
@TableName("student")
public class StudentPo implements Serializable {

    @TableId(type = IdType.AUTO)
    private Long id;

    @TableField
    private Long no;

    @TableField
    private String name;

    @TableField
    private String sex;

    @TableField
    private Integer score;
}
  • 项目相关常量
public interface HazelcastConstants {

    String HAZECAST_MAP_CACHE = "mapcache";
}
  • 项目相关Controller
@RestController
@RequestMapping("/student")
public class StudentController {

    @Autowired
    private HazelcastInstance hazelcastInstance;

    private ConcurrentMap<Long, StudentPo> retrieveMap() {
        return hazelcastInstance.getMap(HazelcastConstants.HAZECAST_MAP_CACHE);
    }


    @PostMapping("/one")
    public String addOne(@RequestBody StudentPo studentPo) {
        retrieveMap().put(studentPo.getNo(), studentPo);
        return "插入成功";
    }

    @GetMapping("/one/{no}")
    public StudentPo getOne(@PathVariable Long no) {
        return retrieveMap().get(no);
    }

}
  • 项目相关Mapper 
public interface StudentMapper extends BaseMapper<StudentPo> {

}
  • 项目相关yml
server:
  port: 8084
  servlet:
    context-path: /hazelcast
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://192.168.119.141:3307/demo
    username: root
    password: 123456

测试 

同一份代码复制两份进行测试,开启不同的端口

测试加载

  • 分别启动 Node1 和 Node2,登录 management-center 查看当前状态

  • 当前管理中心有两个成员,但是没有对应的 map ,为啥?因为是 LAZY 模式。接下来,发起一个 GET 请求 localhost:8083/hazelcast/student/one/3,然后再查看管理中心相应值

  • 获取到了数据库中的所有数据,并且已经放在不同的节点不同的分区里面,图上面还有 获取次数 和 命中次数,测试成功

测试存储

  • 发起一个 POST 请求 localhost:8083/hazelcast/student/one,查看管理中心

  • 数据总数 和 Puts 都已经增加,此外,也可查看对应的数据,测试成功

其他 

  1. Hazelcast 的加载和存储就是缓存一致性的一种解决方式之一
  2. hazelcast 的 mapStore,其所有方法中操作的 key 必须一致
  3. MapLoader.loadAllKeys() 方法的实现返回一个 null 值,则不会加载任何内容
  4. 放在缓存中的类必须实现序列化,此外,如果需要优化,序列化可选择,可自定义序列化器

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1208073.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

3. 【自动驾驶和机器人中的SLAM技术】实现基于预积分和图优化的GNSS+IMU+Odom的融合定位系统

目录 1. 公式推导2. GNSSIMUOdom融合定位3. 利用数值求导工具&#xff0c;验证本书实验中的雅可比矩阵的正确性4. 也欢迎大家来我公众号读书--“过千帆” 1. 公式推导 2. GNSSIMUOdom融合定位 程序实现以及运行效果&#xff1a; ①首先是在预积分程序中记录了预积分积累的IMU数…

竞赛 题目:基于python的验证码识别 - 机器视觉 验证码识别

文章目录 0 前言1 项目简介2 验证码识别步骤2.1 灰度处理&二值化2.2 去除边框2.3 图像降噪2.4 字符切割2.5 识别 3 基于tensorflow的验证码识别3.1 数据集3.2 基于tf的神经网络训练代码 4 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 基于pyt…

超详细!必看!!STM32--时钟树原理

一、什么是时钟&#xff1f; 时钟是单片机的脉搏&#xff0c;是系统工作的同步节拍。单片机上至CPU&#xff0c;下至总线外设&#xff0c;它们工作时序的配合&#xff0c;都需要一个同步的时钟信号来统一指挥。时钟信号是周期性的脉冲信号。 二、什么是时钟树&#xff1f; S…

Pikachu(皮卡丘靶场)初识XSS(常见标签事件及payload总结)

目录 1、反射型xss(get) 2、反射性xss(post) 3、存储型xss 4、DOM型xss 5、DOM型xss-x XSS又叫跨站脚本攻击&#xff0c;是HTML代码注入&#xff0c;通过对网页注入浏览器可执行代码&#xff0c;从而实现攻击。 ​ 1、反射型xss(get) Which NBA player do you like? 由…

【Mycat2实战】一、Mycat简介

1. 什么是Mycat 什么是Mycat Mycat是数据库中间件&#xff0c;所谓中间件数据库中间件是连接Java应用程序和数据库中间的软件。 为什么要用Mycat 遇到问题&#xff1a; Java与数据库的紧耦合高访问量高并发对数据库的压力读写请求数据不一致 2. Mycat与其他中间件区别 目前的…

全面解读Asana项目管理软件:功能、成本与顶级国内替代方案

Asana好用吗&#xff1f;Asana作为一款办公软件的话&#xff0c;其应用范围和受众范围是极为有限。支持这款软件的人把它夸上天&#xff0c;认为其他同类型产品根本不值一提&#xff1b;不支持这款软件的人又把它“束之高阁”&#xff0c;根本不想再用它。 Asana正是近些年具有…

玩转ChatGPT:ARIMA模型定制GPT-1.0

一、写在前面 好久不更新咯&#xff01; OpenAI又推出了GPT的一系列重大更新&#xff0c;其中GPTs深得我心啊。 GPTs允许用户创建自定义的ChatGPT版本&#xff0c;以满足自己各种特定需求。其核心理念在于&#xff0c;用户可以为不同的场景和任务创建定制化的ChatGPT。这意味…

为什么数据安全很重要?哪些措施保护数据安全?

数据安全很重要的原因是因为数据是现代社会的重要财产之一。很多组织和企业依赖数据来做出商业决策&#xff0c;管理客户关系&#xff0c;进行财务规划等等。如果这些数据泄露或遭到黑客攻击&#xff0c;那么就会影响企业的经济利益&#xff0c;甚至影响到个人的隐私和安全。此…

接口测试需要验证数据库么?

&#x1f4e2;专注于分享软件测试干货内容&#xff0c;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff01;&#x1f4e2;交流讨论&#xff1a;欢迎加入我们一起学习&#xff01;&#x1f4e2;资源分享&#xff1a;耗时200小时精选的「软件测试」资…

Git用pull命令后再直接push有问题

在gitlab新建一个项目&#xff0c;然后拉取到本地&#xff0c;用&#xff1a; git init git pull <远程主机名> 然后就是在本地工作区增加所有文件及文件夹。再添加、提交&#xff0c;都没问题&#xff1a; 但是&#xff0c;git push出问题&#xff1a; 说明本地仓库和…

解密图像处理中的利器——直方图与均衡化

直方图与均衡化是数字图像处理中常用的重要工具&#xff0c;它们能够帮助我们更好地理解和改善图像的亮度分布。本文将首先介绍直方图的基本概念以及其在图像处理中的意义&#xff0c;接着详细阐述直方图均衡化的原理和算法。同时&#xff0c;文章将探讨直方图均衡化在图像增强…

在 Android 上简单安全地登录——使用凭证管理器和密钥

我踏马很高兴地听说&#xff0c; Credential Manager的公开版本将于 11 月 1 日开始提供。Credential Manager 为 Android 带来了身份验证的未来&#xff0c;简化了用户登录应用程序和网站的方式&#xff0c;同时使其更加安全。 登录可能具有挑战性 - 密码经常使用&#xff0c…

【无线网络技术】——无线传输技术基础(学习笔记)

目录 &#x1f552; 1. 无线传输媒体&#x1f558; 1.1 地面微波&#x1f558; 1.2 卫星微波&#x1f558; 1.3 广播无线电波&#x1f558; 1.4 红外线&#x1f558; 1.5 光波 &#x1f552; 2. 天线&#x1f558; 2.1 辐射模式&#x1f558; 2.2 天线类型&#x1f564; 2.2.1 …

react Antd3以下实现年份选择器 YearPicker

项目antd版本低&#xff0c;没有直接可使用的年份选择器&#xff0c;参考此篇&#xff08;使用antd实现年份选择器控件 - 掘金&#xff09; 一开始在state里设置了time&#xff1a; this.state {isopen: false,time: null } 在类似onChange事件里this.setState({time: valu…

Linux系统软件安装方式

Linux系统软件安装方式 1. 绿色安装2. yum安装3. rpm安装3.1 rpm常用命令 4. 源码安装4.1 安装依赖包4.2 执行configure脚本4.3 编译、安装4.4 安装4.5 操作nginx4.6 创建服务器 1. 绿色安装 Compressed Archive压缩文档包&#xff0c;如Java软件的压缩文档包&#xff0c;只需…

图形学中的噪声

1 value noise 四个点取随机数然后做插值。 float random (in vec2 st) {return fract(sin(dot(st.xy,vec2(12.9898,78.233)))* 43758.5453123); }float noise (in vec2 st) {vec2 i floor(st);vec2 f fract(st);float a random(i);float b random(i vec2(1.0, 0.0));fl…

centralwidget 不能布局

必须要在QT ui中添加一个任意的子控件&#xff08;比如添加了一个pushButton&#xff09;&#xff0c;然后在centralwidget 才能右键设置布局&#xff0c;成功去掉centralwidget 右下角的红色的标记。

如何制作出高级感满满的的照片书

随着数码相机的普及&#xff0c;越来越多的人喜欢将生活中的点滴美好记录下来&#xff0c;其中照片书就是一种非常受欢迎的方式。但是&#xff0c;如何制作出高级感满满的“照片书”呢&#xff1f;今天&#xff0c;我们就来分享几个小技巧&#xff0c;帮助你轻松打造出令人惊艳…

RT-DETR算法优化改进:Backbone改进|RIFormer:无需TokenMixer也能达成SOTA性能的极简ViT架构 | CVPR2023

💡💡💡本文独家改进:RIFormer助力RT-DETR ,替换backbone, RIFormer-M36的吞吐量可达1185,同时精度高达82.6%;而PoolFormer-M36的吞吐量为109,精度为82.1%。 推荐指数:五星 RT-DETR魔术师专栏介绍: https://blog.csdn.net/m0_63774211/category_12497375.html …

2023企业如何挑选智能工单系统?选亿发工单管理解决方案提供商,移动派单

在企业运作中&#xff0c;工单管理是一项至关重要的工作流程&#xff0c;可以使用标准化、系统化的方式对问题和请求进行全面管理、维护和追踪。 然而&#xff0c;传统的工单处理方式常常受到办公地点和时间的限制&#xff0c;存在工单录入繁琐易错、工作流程曲折耗时、跨部门协…