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>
技术问询
- 采用POJO存储方式是否必然会导致此类BLOB插入延迟?
- 在不使用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
相关产品推荐
相关产品推荐

