1. 简介

Apache Ignite 是一个开源的内存优先分布式平台。我们可以将其用作数据库、缓存系统或内存数据处理引擎。

该平台以内存作为存储层,因此性能表现极为出色。简单说,这是目前生产环境中速度最快的原子数据处理平台之一

2. 安装与配置

首先,参考快速入门页面完成初始安装配置。

构建项目所需的 Maven 依赖:

<dependency>
    <groupId>org.apache.ignite</groupId>
    <artifactId>ignite-core</artifactId>
    <version>${ignite.version}</version>
</dependency>
<dependency>
    <groupId>org.apache.ignite</groupId>
    <artifactId>ignite-indexing</artifactId>
    <version>${ignite.version}</version>
</dependency>

ignite-core 是项目的唯一强制依赖。由于需要支持 SQL 功能,额外添加了 ignite-indexing${ignite.version} 表示 Apache Ignite 的最新版本。

最后启动 Ignite 节点:

Ignite node started OK (id=53c77dea)
Topology snapshot [ver=1, servers=1, clients=0, CPUs=4, offheap=1.2GB, heap=1.0GB]
Data Regions Configured:
^-- default [initSize=256.0 MiB, maxSize=1.2 GiB, persistenceEnabled=false]

控制台输出表明环境已就绪。

3. 内存架构

平台基于持久化内存架构(Durable Memory Architecture)。该架构支持在磁盘和内存中同时存储和处理数据,通过高效利用集群内存资源显著提升性能。

内存与磁盘中的数据采用相同的二进制表示格式,这意味着数据在层级间移动时无需额外转换。

持久化内存架构将数据划分为固定大小的块(称为页)。页存储在 Java 堆外,并在内存中组织,通过 FullPageId 唯一标识。

页通过 PageMemory 抽象层与内存交互,负责页的读写和 ID 分配。在内存内部,Ignite 将页与 Memory Buffers 相关联

4. 内存页

页存在以下状态:

  • Unloaded – 未加载页缓冲区
  • Clear – 页缓冲区已加载且与磁盘数据同步
  • Dirty – 页缓冲区数据与磁盘不一致
  • Dirty in checkpoint – 首次修改持久化前发生二次修改时,检查点启动,PageMemory 为每个页维护两个内存缓冲区

**持久化内存分配本地内存段(称为 Data Region)**。默认容量为集群内存的 20%。多区域配置允许将常用数据保留在内存中。

区域的最大容量是内存段(Memory Segment),可以是物理内存或连续字节数组。

为避免内存碎片,单个页可存储多个键值对。新条目会被添加到最优页中。若键值对大小超过页容量,Ignite 会将其分散存储在多个页中。更新数据时遵循相同逻辑。

SQL 和缓存索引存储在 B+ 树结构中,缓存键按键值排序。

5. 生命周期

每个 Ignite 节点运行在单个 JVM 实例中,但也可配置在单个 JVM 进程中运行多个节点。

生命周期事件类型包括:

  • BEFORE_NODE_START – 节点启动前
  • AFTER_NODE_START – 节点启动后立即触发
  • BEFORE_NODE_STOP – 节点停止前
  • AFTER_NODE_STOP – 节点停止后

启动默认 Ignite 节点:

Ignite ignite = Ignition.start();

或通过配置文件启动:

Ignite ignite = Ignition.start("config/example-cache.xml");

若需更精细控制初始化过程,可通过 LifecycleBean 接口实现:

public class CustomLifecycleBean implements LifecycleBean {
 
    @Override
    public void onLifecycleEvent(LifecycleEventType lifecycleEventType) 
      throws IgniteException {
 
        if(lifecycleEventType == LifecycleEventType.AFTER_NODE_START) {
            // 节点启动后执行逻辑
        }
    }
}

通过生命周期事件类型在节点启动/停止前后执行操作。将配置实例与 CustomLifecycleBean 传递给启动方法:

IgniteConfiguration configuration = new IgniteConfiguration();
configuration.setLifecycleBeans(new CustomLifecycleBean());
Ignite ignite = Ignition.start(configuration);

6. 内存数据网格

Ignite 数据网格是分布式键值存储,与分区式 HashMap 高度相似。它支持水平扩展——集群节点越多,可缓存/存储的数据量越大。

作为 NoSQL/RDBMS 的缓存层,可显著提升第三方软件性能。

6.1 缓存支持

数据访问 API 基于 JCache JSR 107 规范

示例:使用模板配置创建缓存

IgniteCache<Employee, Integer> cache = ignite.getOrCreateCache(
  "baeldingCache");

执行流程详解:

  1. Ignite 定位缓存所在的内存区域
  2. 根据键哈希码在 B+ 树中定位索引页
  3. 若索引存在,定位对应键的数据页
  4. 若索引为 NULL,平台使用给定键创建新数据条目

添加 Employee 对象:

cache.put(1, new Employee(1, "John", true));
cache.put(2, new Employee(2, "Anna", false));
cache.put(3, new Employee(3, "George", true));

持久化内存再次定位缓存所属内存区域,基于缓存键在 B+ 树中定位索引页。若索引页不存在,则请求并添加新页,随后为索引页分配数据页。

从缓存读取数据:

Employee employee = cache.get(1);

6.2 流处理支持

内存数据流处理为基于磁盘/文件系统的数据处理提供了替代方案。流处理 API 将高负载数据流拆分为多个阶段并路由处理

改造示例:从文件流式加载数据。首先定义数据流处理器:

IgniteDataStreamer<Integer, Employee> streamer = ignite
  .dataStreamer(cache.getName());

注册流转换器标记接收的员工为在职状态:

streamer.receiver(StreamTransformer.from((e, arg) -> {
    Employee employee = e.getValue();
    employee.setEmployed(true);
    e.setValue(employee);
    return employee;
}));

最后遍历 employees.txt 文件并转换为 Java 对象:

Path path = Paths.get(IgniteStream.class.getResource("employees.txt")
  .toURI());
Gson gson = new Gson();
Files.lines(path)
  .forEach(l -> streamer.addData(
    employee.getId(), 
    gson.fromJson(l, Employee.class)));

通过 streamer.addData() 将员工对象注入数据流

7. SQL 支持

平台提供内存优先、容错的 SQL 数据库

支持纯 SQL API 或 JDBC 连接。SQL 语法遵循 ANSI-99 标准,支持所有标准聚合函数、DML 和 DDL 操作。

7.1 JDBC

实战:创建员工表并插入数据。注册 JDBC 驱动并建立连接

Class.forName("org.apache.ignite.IgniteJdbcThinDriver");
Connection conn = DriverManager.getConnection("jdbc:ignite:thin://127.0.0.1/");

通过标准 DDL 命令创建 Employee 表:

sql.executeUpdate("CREATE TABLE Employee (" +
  " id LONG PRIMARY KEY, name VARCHAR, isEmployed tinyint(1)) " +
  " WITH \"template=replicated\"");

WITH 关键字后可设置缓存配置模板。此处使用 REPLICATED 模式。默认模板为 PARTITIONED可通过 BACKUPS 参数指定数据副本数(默认为 0)

使用 INSERT DML 语句添加数据:

PreparedStatement sql = conn.prepareStatement(
  "INSERT INTO Employee (id, name, isEmployed) VALUES (?, ?, ?)");

sql.setLong(1, 1);
sql.setString(2, "James");
sql.setBoolean(3, true);
sql.executeUpdate();

// 添加其他记录

查询记录:

ResultSet rs 
  = sql.executeQuery("SELECT e.name, e.isEmployed " 
    + " FROM Employee e " 
    + " WHERE e.isEmployed = TRUE ")

7.2 对象查询

可直接对缓存中的 Java 对象执行查询。Ignite 将 Java 对象视为独立 SQL 记录:

IgniteCache<Integer, Employee> cache = ignite.cache("baeldungCache");

SqlFieldsQuery sql = new SqlFieldsQuery(
  "select name from Employee where isEmployed = 'true'");

QueryCursor<List<?>> cursor = cache.query(sql);

for (List<?> row : cursor) {
    // 处理行数据
}

8. 总结

本教程快速介绍了 Apache Ignite 项目。指南突出了平台相比同类产品的优势:性能提升、持久性保障、轻量级 API 等。

我们学习了如何使用 SQL 和 Java API 在持久化层或内存网格中存储、检索和流式处理数据

完整代码示例请访问 GitHub 仓库


原始标题:A Guide to Apache Ignite