Session问题以及解决方案

Mysql事务锁等待超时 Lock wait timeout exceeded; try restarting transaction

问题场景
问题出现环境:
1、在同一事务内先后对同一条数据进行插入和更新操作;
2、多台服务器操作同一数据库;
3、瞬时出现高并发现象;

原因分析
在高并发的情况下,Spring事物造成数据库死锁,后续操作超时抛出异常。
Mysql数据库采用InnoDB模式,默认参数:innodb_lock_wait_timeout设置锁等待的时间是50s,一旦数据库锁超过这个时间就会报错。

临时解决方案
我们可以通过到information_schema 中来进行查找被锁的语句。

我们可以用下面三张表来查原因:
innodb_trx 当前运行的所有事务
innodb_locks 当前出现的锁
innodb_lock_waits 锁等待的对应关系
如果数据库中有锁的话,我们可以使用这条语句来查看:
select * from information_schema.innodb_trx
LOCK WAIT为占用系统资源的语句,我们需要杀掉这个锁,执行 kill 线程id号

终极解决方案
1.细化Session操作的时间
将验证逻辑,获取数据逻辑等内容单独拉出,数据准备好之后,打开session后进行数据库操作,然后提交session。
2.数据一致性要求比较低的话,可以设置session自动提交。
sqlSessionFactory.openSession(true)
3.及时关闭session,也可以采用以下方法新建session
try(SqlSession sqlSession = sqlSessionFactory.openSession()) { // run sth. }
4.增加锁等待时间,即增大下面配置项参数值,单位为秒(s)
innodb_lock_wait_timeout=500

Error querying database. Cause: java.lang.ClassCastException: org.apache.ibatis.executor.ExecutionPlaceholder cannot be cast to java.util.List

问题场景&原因分析
以 SqlSession sqlSession = sqlSessionFactory.openSession() 方式创建的sqlSession 在多线程下并发查询数据时报错
终极解决方案
1.继续使用sqlSession ,但是不同线程,使用不同的session,不公用一个session
2.不使用sqlSession,包装成SqlSessionTemplate,使用spring提供的代理。

Druid异常:wait millis 40000, active 600, maxActive 600

问题场景
session连接数创建过多,没有及时关闭
原因分析
1.session没有主动关闭
2.session打开后代码报错,无法关闭session
终极解决方案
在 try catch finally 语句块内,在finally 内关闭session

全面解决问题代码示例

综合上面各种问题,采取如下方式:
1.使用ThreadLocal存储session,线程的所有session都从ThreadLocal获取
2.finally内 进行 session的提交,session的关闭,ThreadLocal的删除
3.最大线程数 小于等于 最大连接数
整体处理方法如下:

// 最大线程数为200,此数据对应数据库配置的 maxActive ,最大线程数要<=maxActive 
private ExecutorService threadPool = new ThreadPoolExecutor(0, 200, 60,
            TimeUnit.SECONDS, new ArrayBlockingQueue<>(10),
            new ThreadFactoryBuilder().setNameFormat("thread-" + this.getClass().getSimpleName() + "-kafka-metrics-%d").setDaemon(true).build(),
            new ThreadPoolExecutor.DiscardOldestPolicy());;
private static final ThreadLocal<SqlSession> threadSessionLocal = new ThreadLocal<>();
for(){
  threadPool.submit(() -> {
     this.dosth(params);
  });
}


  public void dosth(Object params){
    if(threadSessionLocal.get() == null){
        SqlSession sqlSession = sessionFactory.openSession();
        threadSessionLocal.set(sqlSession);
    }
    try{
    SqlSession sqlSession = threadSessionLocal.get();
    //sqlSession 进行后续操作
    }catch (Exception e){
    }finally{
          threadSessionLocal.get().commit();
          threadSessionLocal.get().close();
          threadSessionLocal.remove();
    }
  }
posted @ 2020-07-09 09:46  CalronLoveRonnie  阅读(1168)  评论(0编辑  收藏  举报
AmazingCounters.com