Mybatis SQL会话提交速度似乎比下面的代码慢



背景

我们有两个用Java编写的服务——一个处理不同文件上的数据库操作(数据库上的CRUD),另一个处理这些记录的长时间处理(复杂的后台任务)。我们可以简单地说他们是生产者和消费者。

假定行为如下:

服务1(使用以下代码):

  1. 将文件存储到DB 中

  2. 如果文件类型为"C",则将其放入消息队列以进一步处理

服务2:

  1. 从消息队列接收消息

  2. 从数据库加载文件(按ID)

  3. 执行的进一步处理

服务1的代码如下(我出于公司原因更改了一些名称)

private void persist() throws Exception {
try (SqlSession sqlSession = sessionFactory.openSession()) {
FileType fileType = FileType.fromFileName(filename);
FileEntity dto = new FileEntity(filename, currentTime(), null, user.getName(), count, data);
oracleFileStore.create(sqlSession, dto);
auditLog.logFileUploaded(user, filename, count);
sqlSession.commit();
if (fileType == FileType.C) {
mqClient.submit(new Record(dto.getId(), dto.getName(), user));
auditLog.logCFileDetected(user, filename);
}
}
}

附加信息

ActiveMQ 5.15用于消息队列

数据库是Oracle 12c

数据库由Mybatis 3.4.1处理

问题

服务2不时地从MQ接收消息,尝试从数据库读取文件,但令人惊讶的是,文件不在。这件事很少见,但它确实发生了。当我们检查数据库时,文件就在那里。看起来文件的后台处理是在文件被放入数据库之前开始的。

问题

MQ调用是否可能比数据库提交更快?我在DB中创建了名为commit的文件,然后才将消息放入MQ中。MQ甚至包含由数据库本身生成的ID(序列)。

是否需要关闭连接以确保提交已执行?我一直认为,当我提交时,不管我的事务是否结束,它都会在数据库中。

问题会是Mybatis吗?我读到了一些关于Mybatis交易/会话的问题,但它似乎与我的问题不相似

更新

我可以提供一些额外的代码,但请理解,由于公司原因,我不能分享所有内容。如果你看不到任何明显的东西,那也没关系。不幸的是,我无法继续进行比这更深入的分析。

此外,我基本上想确认我对SQL和Mybatis的理解是否正确,我也可以将这样的响应标记为正确。

SessionFactory.java(摘录)

private SqlSessionFactory createLegacySessionFactory(DataSource dataSource) throws Exception
{
Configuration configuration = prepareConfiguration(dataSource);
return new SqlSessionFactoryBuilder().build(configuration);
}
//javax.sql.DataSource
private Configuration prepareConfiguration(DataSource dataSource)
{
//classes from package org.apache.ibatis
TransactionFactory transactionFactory = new JdbcTransactionFactory();
Environment environment = new Environment("development", transactionFactory, dataSource);
Configuration configuration = new Configuration(environment);
addSettings(configuration);
addTypeAliases(configuration);
addTypeHandlers(configuration);
configuration.addMapper(PermissionMapper.class);
addMapperXMLs(configuration); //just add all the XML mappers
return configuration;
}
public SqlSession openSession()
{
//Initialization of factory is above
return new ForceCommitSqlSession(factory.openSession());
}

ForceCommitSqlSession.java(摘录)

/**
* ForceCommitSqlSession is wrapper around mybatis {@link SqlSession}.
* <p>
* Its purpose is to force commit/rollback during standard commit/rollback operations. The default implementation (according to javadoc)
* does
* not commit/rollback if there were no changes to the database - this can lead to problems, when operations are executed outside mybatis
* session (e.g. via {@link #getConnection()}).
*/
public class ForceCommitSqlSession implements SqlSession
{
private final SqlSession session;
/**
* Force the commit all the time (despite "generic contract")
*/
@Override
public void commit()
{
session.commit(true);
}
/**
* Force the roll back all the time (despite "generic contract")
*/
@Override
public void rollback()
{
session.rollback(true);
}
@Override
public int insert(String statement)
{
return session.insert(statement);
}
....
}

OracleFileStore.java(摘录)

public int create(SqlSession session, FileEntity fileEntity) throws Exception
{
//the mybatis xml is simple insert SQL query
return session.insert(STATEMENT_CREATE, fileEntity);
}

MQ调用是否可能比数据库提交更快?

如果数据库提交完成,则更改在数据库中。在那之后,将在队列中创建任务。这里的主要内容是,当您在会话上调用commit时,您需要检查提交是否同步发生。从您目前提供的配置来看,这似乎还可以,除非Connection本身有一些问题。例如,我可以想象在本机Connection上有一些包装器。我会在调试器中检查commit调用是否会导致从OracleJDBC驱动程序调用实现上的Connection.commit。最好在DB端检查日志。

是否需要关闭连接以确保执行了提交?我一直认为,当我提交时,不管我的事务是否结束,它都会在数据库中。

你是对的。不需要关闭遵循JDBC规范的连接(本地JDCB连接就是这样做的)。当然,您总是可以创建一些不遵循ConnectionAPI的包装器,并做一些魔术(比如延迟提交直到连接关闭)。

问题会是Mybatis吗?我读到了一些关于Mybatis交易/会话的问题,但它似乎与我的问题不相似

我认为这不太可能。您使用的是JdbcTransactionFactory,它确实提交到数据库。您需要跟踪commit上发生的事情才能确定。

你检查过问题不在读者方面吗?例如,它可能使用具有序列化隔离级别的长事务,在这种情况下,它将无法读取数据库中的更改。

在postgres中,如果使用复制,并且副本用于读取查询,即使在master上成功完成提交,读取器也可能看到过时的数据。我对oracle不太熟悉,但如果使用复制,你可能会看到同样的问题:

表快照是其主数据在特定时间点存在时的事务一致性反映。为了使快照的数据与其主数据保持相对最新,Oracle必须定期刷新快照

我会检查DB的设置,以了解情况是否如此。如果使用复制,您需要改变对此的方法。

最新更新