繁体   English   中英

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

[英]how to avoid “lock timeout” when updating DB using multiple threads?

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

我正在使用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>

这是我的堆栈跟踪:

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

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

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

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

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

也许你可以尝试降低隔离级别 如果有帮助你可以挖掘更多。 它应该加快多线程环境中的执行速度。

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

这似乎是数据库端的超时。 我猜数据库是限制因素,因此在应用程序中添加线程无济于事。

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM