You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Apache Ignite写入Oracle BLOB的POJO存储性能问题及正确方式咨询

Apache Ignite BLOB 持久化性能与实现问题

背景

我们的应用采用Apache Ignite作为数据库前置缓存层,使用嵌入式Ignite,基于POJO存储数据。对应的数据库表结构如下:

SAMPLE_TABLE
ID NUMBER
NAME VARCHAR2(50)
PYLD BLOB 

向缓存插入含BLOB的数据时,耗时在100ms-300ms之间,而插入无BLOB的表数据速度极快。当前使用的CacheConfiguration配置如下:

<bean
            class="org.apache.ignite.configuration.CacheConfiguration">
            <property name="name" value="SampleBlobBackedCache" />
            <property name="cacheMode" value="PARTITIONED" />
            <property name="atomicityMode" value="ATOMIC" />
            <property name="expiryPolicyFactory">
                <bean class="javax.cache.expiry.CreatedExpiryPolicy"
                    factory-method="factoryOf">
                    <constructor-arg>
                        <bean class="javax.cache.expiry.Duration">
                            <constructor-arg value="SECONDS" />
                            <constructor-arg value="30" />
                        </bean>
                    </constructor-arg>
                </bean>
            </property>

            <property name="cacheStoreFactory">
                <bean
                    class="org.apache.ignite.cache.store.jdbc.CacheJdbcPojoStoreFactory">
                    <property name="dataSourceBean" value="dataSourceMR" />
                    <property name="dialect">
                        <bean
                            class="org.apache.ignite.cache.store.jdbc.dialect.BasicJdbcDialect">
                        </bean>
                    </property>

                    <property name="types">
                        <list>
                            <bean class="org.apache.ignite.cache.store.jdbc.JdbcType">
                                <property name="cacheName" value="MessageDataCache" />
                                <property name="keyType" value="java.lang.String" />
                                <property name="valueType"
                                    value="com.SampleData" />
                                <property name="databaseSchema" value="SampleBlobDB" />   <!-- Changed name for public posting -->
                                <property name="databaseTable" value="SampleBlobTable" />

                                <property name="keyFields">
                                    <list>
                                        <bean
                                            class="org.apache.ignite.cache.store.jdbc.JdbcTypeField">
                                            <constructor-arg>
                                                <util:constant
                                                    static-field="java.sql.Types.VARCHAR" />
                                            </constructor-arg>
                                            <constructor-arg value="MESSAGE_ID" />
                                            <constructor-arg value="java.lang.String" />
                                            <constructor-arg value="messageId" />
                                        </bean>
                                    </list>
                                </property>

                                <property name="valueFields">
                                    <list>
                                        <bean
                                            class="org.apache.ignite.cache.store.jdbc.JdbcTypeField">
                                            <constructor-arg>
                                                <util:constant
                                                    static-field="java.sql.Types.BLOB" />
                                            </constructor-arg>
                                            <constructor-arg value="RAW_DATA" />
                                            <constructor-arg value="byte[]" />
                                            <constructor-arg value="rawData" />
                                        </bean>

                                        <bean
                                            class="org.apache.ignite.cache.store.jdbc.JdbcTypeField">
                                            <constructor-arg>
                                                <util:constant
                                                    static-field="java.sql.Types.TIMESTAMP" />
                                            </constructor-arg>
                                            <constructor-arg value="CREATED_ON" />
                                            <constructor-arg value="java.sql.Timestamp" />
                                            <constructor-arg value="createdOn" />
                                        </bean>
                                    </list>
                                </property>
                            </bean>
                        </list>
                    </property>
                </bean>
            </property>

            <property name="readThrough" value="true" />
            <property name="writeThrough" value="true" />

            <property name="queryEntities">
                <list>
                    <bean class="org.apache.ignite.cache.QueryEntity">
                        <property name="keyType" value="java.lang.String" />
                        <property name="valueType"
                            value="com.SampleData" />
                        <property name="tableName" value="SampleBlobTable" />
                        <property name="keyFieldName" value="messageId" />

                        <property name="keyFields">
                            <list>
                                <value>messageId</value>
                            </list>
                        </property>

                        <property name="fields">
                            <map>
                                <entry key="rawData" value="[B" />
                                <entry key="createdOn" value="java.sql.Timestamp" />
                                <entry key="messageId" value="java.lang.String" />
                            </map>
                        </property>

                        <property name="aliases">
                            <map>
                                <entry key="messageId" value="MESSAGE_ID" />
                                <entry key="rawData" value="RAW_DATA" />
                                <entry key="createdOn" value="CREATED_ON" />
                            </map>
                        </property>
                    </bean>
                </list>
            </property>
        </bean>

技术问询

  1. 采用POJO存储方式是否必然会导致此类BLOB插入延迟?
  2. 在不使用Ignite原生持久化的情况下,通过Ignite将BLOB数据持久化到数据库的正确方式是什么?

问题解答

1. POJO存储是否必然导致BLOB插入延迟?

不是必然的。POJO本身只是数据载体,延迟的核心原因通常不是POJO存储方式,而是以下几点:

  • 默认JDBC POJO Store的BLOB处理逻辑:当前使用的BasicJdbcDialect对BLOB的处理未做针对性优化,可能是同步写入、未采用流式传输,导致大字节数组一次性写入时产生IO阻塞。
  • Write-Through模式的同步性:配置了writeThrough=true,每次缓存插入都会同步触发数据库写入,BLOB数据的IO开销直接反映到插入耗时上。
  • 数据序列化开销:嵌入式Ignite会对POJO进行序列化(默认用BinaryMarshaller),大BLOB数组的序列化/反序列化会额外消耗CPU和时间。

只要优化上述几点,POJO存储的BLOB插入性能可以显著提升,因此POJO不是延迟的根本原因。

2. 不使用原生持久化时,Ignite持久化BLOB到数据库的正确方式

针对BLOB场景,需从配置、Dialect选型、写入模式三个维度优化:

(1)替换为数据库专属Dialect

不要使用通用的BasicJdbcDialect,改用对应数据库的专属Dialect(比如Oracle用OracleJdbcDialect)。这些专属Dialect针对BLOB有优化,比如支持流式写入、数据库特定的BLOB处理API,能有效减少IO开销。
修改配置中的dialect部分:

<property name="dialect">
    <bean class="org.apache.ignite.cache.store.jdbc.dialect.OracleJdbcDialect" />
</property>

(2)优化写入模式,避免同步阻塞

如果业务允许,将writeThrough=true改为writeBehind=true,启用异步写入。配置Write-Behind参数控制批量写入和线程池:

<property name="writeBehindEnabled" value="true" />
<property name="writeBehindBatchSize" value="100" />
<property name="writeBehindFlushFrequency" value="5000" /> <!-- 5秒刷新一次 -->
<property name="writeBehindThreadCount" value="4" />

这样BLOB数据会先写入缓存,再由后台线程批量异步写入数据库,大幅降低前端插入延迟。

(3)调整POJO的BLOB字段序列化策略

  • 确保POJO的BLOB字段(rawData)被Ignite的BinaryMarshaller高效处理,可在POJO类上添加@BinaryName注解,或配置BinaryMarshaller忽略不必要的字段,减少序列化开销。
  • 如果BLOB数据极大(超过10MB),可考虑拆分存储:将元数据(如messageId、createdOn)放在主缓存,BLOB单独存一个分布式缓存,或通过自定义CacheStore单独处理BLOB写入,避免大对象序列化影响整体性能。

(4)自定义CacheStore(进阶方案)

如果默认JDBC POJO Store的优化仍不足,可实现自定义CacheStore,手动处理BLOB的流式写入:

  • 使用JDBC的PreparedStatement.setBinaryStream()方法,将BLOB数据以流的形式写入数据库,减少内存占用和一次性IO开销。
  • 结合数据库的BLOB优化特性(比如Oracle的BFILE或分区存储),进一步提升写入性能。

内容的提问来源于stack exchange,提问作者Karthik_Rajendiran

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 20:26:59