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");
执行流程详解:
- Ignite 定位缓存所在的内存区域
- 根据键哈希码在 B+ 树中定位索引页
- 若索引存在,定位对应键的数据页
- 若索引为 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 仓库。