如何避免"锁定超时"使用多个线程更新数据库时?

时间:2015-09-28 18:38:42

标签: java mysql multithreading spring hibernate

我正在尝试使用多个线程更新表。但我不是同时更新相同的记录/行。我正在将表分组到不同的组中并尝试同时更新它们。但是,我一直收到锁定的超时错误。

我正在使用Hibernate,Spring MVC,ThreadPoolTask​​Executor和MySQL。我从另一个数据库架构获取数据并更新我自己的数据库。数据是巨大的,这就是为什么我想使用多线程,所以它可以更快地完成。然而,它产生了锁定超时"错误。有人可以帮忙吗?谢谢你的好心。

我调用sessionFactory.getCurrenSession()来更新数据库表。

这是我的配置:

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
        destroy-method="close" p:driverClassName="${jdbc.driverClassName}"
        p:url="${jdbc.url}" p:username="${jdbc.username}" p:password="${jdbc.password}">
    </bean>

    <bean id="sessionFactory"
        class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="configLocation">
            <value>classpath:hibernate.cfg.xml</value>
        </property>
        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
                <prop key="hibernate.show_sql">true</prop>
            </props>
        </property>
    </bean>

    <bean id="transactionManager"
        class="org.springframework.orm.hibernate4.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory" />
    </bean>

    <bean id="taskExecutor"
        class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
        <property name="corePoolSize" value="5" />
        <property name="maxPoolSize" value="10" />
        <property name="WaitForTasksToCompleteOnShutdown" value="true" />
    </bean>

这是我的stacktrace:

WARN : org.hibernate.engine.jdbc.spi.SqlExceptionHelper - SQL Error: 1205, SQLState: 41000
ERROR: org.hibernate.engine.jdbc.spi.SqlExceptionHelper - Lock wait timeout exceeded; try restarting transaction
Exception in thread "taskExecutor-5" Exception in thread "taskExecutor-4" Exception in thread "taskExecutor-2" org.hibernate.exception.LockTimeoutException: could not execute statement
    at org.hibernate.dialect.MySQLDialect$1.convert(MySQLDialect.java:407)
    at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:49)
    at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:125)
    at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:110)
    at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.executeUpdate(ResultSetReturnImpl.java:136)
    at org.hibernate.hql.internal.ast.exec.BasicExecutor.execute(BasicExecutor.java:103)
    at org.hibernate.hql.internal.ast.QueryTranslatorImpl.executeUpdate(QueryTranslatorImpl.java:413)
    at org.hibernate.engine.query.spi.HQLQueryPlan.performExecuteUpdate(HQLQueryPlan.java:282)
    at org.hibernate.internal.SessionImpl.executeUpdate(SessionImpl.java:1289)
    at org.hibernate.internal.QueryImpl.executeUpdate(QueryImpl.java:116)
   org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:317)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:183)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150)
    at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:96)
    at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:260)
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:94)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:204)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
    at java.lang.Thread.run(Unknown Source)
Caused by: java.sql.SQLException: Lock wait timeout exceeded; try restarting transaction
    at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1084)
    at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:4232)
    at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:4164)
    at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:2615)
    at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2776)
    at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2838)
    at com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:2082)
    at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2334)
    at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2262)
    at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2246)
    at org.apache.commons.dbcp.DelegatingPreparedStatement.executeUpdate(DelegatingPreparedStatement.java:105)
    at org.apache.commons.dbcp.DelegatingPreparedStatement.executeUpdate(DelegatingPreparedStatement.java:105)
    at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.executeUpdate(ResultSetReturnImpl.java:133)
    ... 25 more

3 个答案:

答案 0 :(得分:2)

“锁定等待超时”总是会发生(即使在一个事务中有大量插入),也没有解决它的灵丹妙药。但是当我试图在一个(相对较小的)表中更新所有记录的一半而另一半被另一个服务器修改时,我设法绕过它。

  • 查看事务中的所有SQL语句。
    使用explain确保尽可能使用索引。删除作为交易一部分不需要的任何陈述。
  • 优化事务中SQL语句的顺序。
    这对我来说是一个试验和错误,但试着想象来自多个线程/连接的SQL语句的哪个顺序可能更容易处理数据库。在我的情况下,只需切换两个SQL语句的顺序使“锁定等待超时”发生的频率降低。
  • 更新较小的子集。
    这最终为我解决了“锁定等待超时”。在我的例子中,有一个索引列允许我将较大的更新集划分为较小的子集。所以现在一个大的更新事务变成了大约十个更小的更新事务。请记住,您需要能够在崩溃后继续执行较小的事务(即数据必须保持一致,以便您的应用程序可以重做操作并获得相同的结果)。

多个线程是否会提高吞吐量(每秒更新的行数)还有待观察:它取决于更新集的大小(网络延迟)以及MySQL如何有效地处理表的锁定更新行。使用两个线程/连接而不是一个时,您可能只会看到一个微小的改进。

[编辑]还要注意数据库触发器/程序:它们会以不良方式影响性能。

答案 1 :(得分:1)

也许您可以尝试降低isolation level。如果有帮助你可以挖掘更多。它应该加速多线程环境中的执行。

如果您正在使用注释,则可以通过以下方式实现此目的 @Transactional(isolation=Isolation.READ_UNCOMMITTED) 在您的交易类之上。

答案 2 :(得分:1)

这似乎是数据库端的超时。我猜这个数据库是限制因素,所以在你的应用程序中添加线程并没有帮助。

如果你想使用线程来加快速度,我建议只使用两个线程。当一个线程从另一个数据库读取时,第二个线程写入MySQL数据库。

请注意,如果两个数据库都在同一个数据库服务器上,那么即使这样也无法提供帮助。您需要更快的数据库或更强大的数据库机器。