Apache Ignite Binary Object 调优

发布于:2025-07-22 ⋅ 阅读:(18) ⋅ 点赞:(0)

这段内容讲的是 Apache Ignite 中 Binary Object(二进制对象)的调优建议。Ignite 的 Binary Object 是一种 高效的序列化机制,它允许你在不加载类的情况下对对象进行序列化、反序列化、查询和更新。为了提高性能和内存使用效率,Ignite 对 Binary Object 的结构和 schema(模式)有特定的处理方式,因此我们需要遵循一些调优建议。

下面我们逐条解释这些调优建议,帮助你更好地理解和应用它们。


🔍 一、Binary Object 的 Schema 是什么?

定义:

  • 每个 Binary Object 都有一个 Schema(模式)
  • Schema 描述了对象中包含哪些字段,字段的顺序和类型;
  • 这些 Schema 会被 复制到所有节点,以便进行反序列化和查询。

举例:

BinaryObject obj1 = binaryFactory.create().setInt("id", 1).setString("name", "Alice");
BinaryObject obj2 = binaryFactory.create().setString("name", "Bob").setInt("id", 2);
  • 虽然两个对象都有相同的字段 idname,但字段顺序不同;
  • Ignite 会认为它们是 两个不同的 Schema
  • 这会增加内存开销,因为每个 Schema 都要保存。

✅ 建议 1:始终以相同的顺序添加字段

原文:

We strongly recommend you should always add fields to binary objects in the same order.

理由:

  • 如果字段顺序不同,Ignite 会创建 不同的 Schema
  • 多个 Schema 会占用更多内存;
  • 所有节点都会保存这些 Schema,可能造成 内存浪费或溢出

建议做法:

  • 统一字段顺序,避免因为顺序不同导致 Schema 冗余;
  • 可以通过封装统一的字段构造逻辑来保证一致性。

✅ 建议 2:避免频繁创建新的 Schema(尤其是 null 字段的组合)

原文:

If you have multiple fields that are set to null in random combinations, Ignite maintains a different Binary Object schema for each combination…

理由:

  • 每个字段是否为 null 会影响 Schema;
  • 如果你有多个字段,它们的 null 组合很多,Ignite 会为每种组合创建一个新 Schema;
  • 导致 Schema 数量激增,消耗大量堆内存

null 字段的内存开销:

  • 每个 null 字段需要 5 字节:4 字节字段 ID + 1 字节长度;
  • 但如果你 不包含这个字段,Ignite 会创建一个新 Schema;
  • 所以:
    • 如果你经常需要设置 null,建议保留字段并显式设置 null,而不是省略字段
    • 同时,统一字段集合,避免不同组合。

示例:

// 推荐:字段统一,允许 null
BinaryObject obj1 = binaryFactory.create()
    .setInt("age", null)
    .setString("name", "Alice")
    .setDouble("salary", null);

BinaryObject obj2 = binaryFactory.create()
    .setInt("age", 30)
    .setString("name", "Bob")
    .setDouble("salary", 5000);
  • 两个对象使用相同的字段集合,Schema 一致;
  • 更节省内存,更高效。

✅ 建议 3:为 null 字段指定类型

原文:

This is also the reason you need to supply field type for null field.

理由:

  • 如果字段是 null,Ignite 无法推断字段的类型;
  • 所以你必须显式提供字段类型,否则会抛出异常。

示例:

// 正确写法:显式指定类型
binaryFactory.create().setField("age", null, Integer.class);

✅ 建议 4:将可选字段封装为嵌套 Binary Object

原文:

You can also nest your Binary Objects if you have a subset of fields which are optional but either all absent or all present.

场景:

  • 有一组字段是可选的,但要么全有,要么全无;
  • 例如:用户信息中有个 “address” 部分,包含 city、street、zip;

建议做法:

  • 将这些字段封装为一个 嵌套 Binary Object
  • 作为父对象的一个字段,可以为 null;
  • 这样不会导致 Schema 爆炸。

示例:

BinaryObject address = binaryFactory.create()
    .setString("city", "Beijing")
    .setString("street", "Chang'an Ave");

BinaryObject user = binaryFactory.create()
    .setString("name", "Alice")
    .setObject("address", address); // 嵌套对象

✅ 建议 5:大量可选字段时使用 Map 字段

原文:

If you have a large number of fields which are all optional in any combinations, and very often null, you can store them in a map field.

场景:

  • 有很多字段都是可选的,组合复杂;
  • 大部分字段经常是 null;

建议做法:

  • 定义几个 固定字段
  • 使用一个 map 字段 存储其他可选字段;
  • 减少 Schema 数量,提高灵活性。

示例:

BinaryObject obj = binaryFactory.create()
    .setString("name", "Alice")
    .setMap("extra", Map.of("age", 30, "email", "alice@example.com"));

📌 总结:Binary Object 的最佳实践

建议 说明
✅ 统一字段顺序 避免不同顺序导致不同 Schema
✅ 统一字段集合 避免 null 字段组合过多造成 Schema 爆炸
✅ 显式设置 null 字段类型 否则无法确定字段类型
✅ 嵌套可选字段 使用嵌套 Binary Object,避免 Schema 冗余
✅ 使用 map 字段 处理大量可选字段,提高灵活性
⚠️ 避免频繁新增字段组合 会导致 Schema 数量激增,影响内存

🧠 小贴士:如何查看 Binary Schema?

你可以在 Ignite 中使用如下方式查看当前的 Binary Schema:

BinaryMetadata meta = ignite.binary().type("MyType");
System.out.println(meta);

这段内容讲的是 如何在 Apache Ignite 中配置 Binary Object(二进制对象)的行为,包括 字段和类型 ID 的生成方式自定义序列化器(Serializer) 等高级配置。虽然大多数情况下你不需要配置这些内容,但在一些特定场景(如避免哈希冲突、优化序列化、支持自定义类型)下,这些配置非常有用。

下面我们逐句解释这段内容,并结合示例帮助你理解。


🔍 一、Binary Object 的基本机制回顾

在 Ignite 中,Binary Object 是一种 高效的二进制序列化机制,它允许你在不加载类的情况下操作对象的字段,非常适合分布式环境中使用。

Binary Object 的内部结构依赖于:

  • Type ID:标识类的唯一 ID;
  • Field ID:标识字段的唯一 ID;
  • 这些 ID 是通过字段名或类名的 字符串哈希值 计算得到的;
  • 默认情况下,Ignite 使用字符串的 hashCode() 方法生成 ID;
  • 为了避免哈希冲突或实现更灵活的映射逻辑,你可以 自定义 ID 生成方式

🛠 二、Binary Object 的可配置项

Ignite 提供了以下配置接口来定制 Binary Object 的行为:

1. Name Mapper(名称映射器)

  • 用于将类名或字段名进行 预处理转换
  • 例如:将全限定类名转为简写、统一大小写等;
  • 接口:BinaryNameMapper

2. ID Mapper(ID 映射器)

  • 用于将经过 Name Mapper 处理后的名称 转换为唯一的 ID
  • 可以自定义 ID 的生成逻辑,如使用 CRC32、SHA1、固定值等;
  • 接口:BinaryIdMapper

3. Serializer(序列化器)

  • 用于对特定类进行 自定义的序列化/反序列化
  • 当你想对某些类使用自己的序列化方式,而不是 Ignite 默认的 Binary Object 机制时使用;
  • 接口:BinarySerializer

🧩 三、全局配置 vs 每个类型配置

你可以配置:

  • 全局配置:适用于所有 Binary Object;
  • 每个类型(per-type)配置:只适用于某些特定类(支持通配符);

🧱 四、XML 配置详解

<bean class="org.apache.ignite.configuration.IgniteConfiguration">

    <property name="binaryConfiguration">
        <bean class="org.apache.ignite.configuration.BinaryConfiguration">
            <property name="nameMapper" ref="globalNameMapper"/>
            <property name="idMapper" ref="globalIdMapper"/>
            <property name="typeConfigurations">
                <list>
                    <bean class="org.apache.ignite.binary.BinaryTypeConfiguration">
                        <property name="typeName" value="org.apache.ignite.examples.*"/>
                        <property name="serializer" ref="exampleSerializer"/>
                    </bean>
                </list>
            </property>
        </bean>
    </property>

</bean>

含义说明:

配置项 描述
nameMapper 全局名称映射器,用于转换类名/字段名
idMapper 全局 ID 映射器,用于将名称转换为 ID
typeConfigurations 类型配置列表,可以为特定类设置不同的序列化器
typeName 类型名,支持通配符(如 org.apache.ignite.examples.*
serializer 对应类型的序列化器

🧪 五、Java 示例:如何实现自定义组件

1. 自定义 ID Mapper 示例

public class MyIdMapper implements BinaryIdMapper {
    @Override
    public int typeId(String typeName) {
        return typeName.hashCode(); // 可以改为 CRC32 或其他算法
    }

    @Override
    public int fieldNameId(int typeId, String fieldName) {
        return fieldName.hashCode();
    }
}

2. 自定义 Name Mapper 示例

public class MyNameMapper implements BinaryNameMapper {
    @Override
    public String mapTypeName(String typeName) {
        return typeName.replace("com.mycompany.", ""); // 去掉包名
    }

    @Override
    public String mapFieldName(String fieldName) {
        return fieldName.toUpperCase(); // 字段名转大写
    }
}

3. 自定义 Serializer 示例

public class MyCustomSerializer implements BinarySerializer {
    @Override
    public void writeBinary(Object obj, BinaryWriter writer) throws BinaryObjectException {
        MyCustomClass myObj = (MyCustomClass) obj;
        writer.writeString("name", myObj.getName());
        writer.writeInt("id", myObj.getId());
    }

    @Override
    public void readBinary(Object obj, BinaryReader reader) throws BinaryObjectException {
        MyCustomClass myObj = (MyCustomClass) obj;
        myObj.setName(reader.readString("name"));
        myObj.setId(reader.readInt("id"));
    }
}

📌 六、通配符匹配(Wildcards)

你可以在 typeName 中使用通配符来匹配多个类:

<property name="typeName" value="com.example.model.*"/>

表示对 com.example.model 包下的所有类应用此配置。


✅ 七、总结:Binary Object 配置要点

配置项 用途 是否推荐自定义
Name Mapper 转换类名/字段名 ✅ 在需要统一命名时使用
ID Mapper 生成字段/类的唯一 ID ✅ 在避免哈希冲突时使用
Serializer 自定义类的序列化方式 ✅ 在需要特殊序列化逻辑时使用
通配符配置 对多个类统一配置 ✅ 提高配置灵活性
全局 vs per-type 控制配置作用范围 ✅ 合理使用,避免冲突

📚 建议使用场景

场景 建议配置
避免字段名哈希冲突 自定义 ID Mapper
统一字段命名风格 自定义 Name Mapper
支持非 POJO 类型 使用 BinarySerializer
多个类统一配置 使用通配符 + per-type 配置
性能敏感场景 自定义 ID 生成算法(如 CRC32)

如果你还有关于 Ignite 的 Binary Object、序列化机制、类加载、缓存查询、集群部署 等方面的问题,欢迎继续提问!


网站公告

今日签到

点亮在社区的每一天
去签到