java - 使用多线程更新数据库时如何避免 "lock timeout"?

标签 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>

这是我的堆栈跟踪:

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

最佳答案

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

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

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

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

关于java - 使用多线程更新数据库时如何避免 "lock timeout"?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32829655/

相关文章:

java - JLabel 无法使用鼠标运动监听器正确移动,为什么?

java - Tomcat 8 Manager war deploy 上传通过 SSL 失败

ZEND 中的 MySql SHOW 查询

c - 如何在多线程 TCP 服务器中获取每个连接的客户端地址

java - Android内存泄漏将匿名类实现与特定于线程的局部变量一起使用

java - 在网络共享上使用 sqlite

java - BlockCopy C# to java - 有什么办法可以做到这一点

mysql - 定位僵尸 mySQL 连接

MySQL 当在 Table2 中找到匹配的记录时将数据插入 Table1

multithreading - 同步 HTTP 请求的最大合理超时