关于JDBC与MySQL临时表空间的深入解析

背景

临时表空间用来管理数据库排序操作以及用于存储临时表、中间排序结果等临时对象,相信大家在开发中经常会遇到相关的需求,下面本文将给大家详细JDBC与MySQL临时表空间的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧

应用 JDBC 连接参数采用 useCursorFetch=true,查询结果集存放在 mysqld 临时表空间中,导致ibtmp1 文件大小暴增到90多G,耗尽服务器磁盘空间。为了限制临时表空间的大小,设置了:

innodb_temp_data_file_path = ibtmp1:12M:autoextend:max:2G

问题描述

在限制了临时表空间后,当应用仍按以前的方式访问时,ibtmp1文件达到2G后,程序一直等待直到超时断开连接。 SHOW PROCESSLIST显示程序的连接线程为sleep状态,state和info信息为空。 这个对应用开发来说不太友好,程序等待超时之后要分析原因也缺少提示信息。

问题分析过程

为了分析问题,我们进行了以下测试

测试环境:

mysql:5.7.16

java:1.8u162

jdbc 驱动:5.1.36

OS:Red Hat 6.4

1.手工模拟临时表超过最大限制的场景

模拟以下环境:

ibtmp1:12M:autoextend:max:30M

将一张 500万行的 sbtest 表的 k 字段索引删除

运行一条 group by 的查询,产生的临时表大小超过限制后,会直接报错:

select sum(k) from sbtest1 group by k;
ERROR 1114 (HY000): The table '/tmp/#sql_60f1_0' is full

2.检查驱动对 mysql 的设置

我们上一步看到,sql 手工执行会返回错误,但是 jdbc 不返回错误,导致连接一直 sleep,怀疑是 mysql 驱动做了特殊设置,驱动连接 mysql,通过 general_log 查看做了哪些设置。未发现做特殊设置。

3.测试 JDBC 连接

问题的背景中有对JDBC做特殊配置:useCursorFetch=true,不知道是否与隐藏报错有关,接下来进行测试:

发现以下现象:

·加参数 useCursorFetch=true时,做同样的查询确实不会报错

这个参数是为了防止返回结果集过大而采用分段读取的方式。即程序下发一个 sql 给 mysql 后,会等 mysql 可以读结果的反馈,由于 mysql 在执行sql时,返回结果达到 ibtmp 上限后报错,但没有关闭该线程,该线程处理 sleep 状态,程序得不到反馈,会一直等,没有报错。如果 kill 这个线程,程序则会报错。

·不加参数 useCursorFetch=true时,做同样的查询则会报错

结论

1.正常情况下,sql 执行过程中临时表大小达到 ibtmp 上限后会报错;

2.当JDBC设置 useCursorFetch=true,sql 执行过程中临时表大小达到 ibtmp 上限后不会报错。

解决方案

进一步了解到使用 useCursorFetch=true 是为了防止查询结果集过大撑爆 jvm;

但是使用 useCursorFetch=true 又会导致普通查询也生成临时表,造成临时表空间过大的问题;

临时表空间过大的解决方案是限制 ibtmp1 的大小,然而 useCursorFetch=true 又导致JDBC不返回错误。

所以需要使用其它方法来达到相同的效果,且 sql 报错后程序也要相应的报错。除了 useCursorFetch=true 这种段读取的方式外,还可以使用流读取的方式。流读取程序详见附件部分。

·报错对比

·段读取方式,sql 报错后,程序不报错

·流读取方式,sql 报错后,程序会报错

·内存占用对比

这里对比了普通读取、段读取、流读取三种方式,初始内存占用 28M 左右:

·普通读取后,内存占用 100M 多

·段读取后,内存占用 60M 左右

·流读取后,内存占用 60M 左右

补充知识点

MySQL共享临时表空间知识点

MySQL 5.7在 temporary tablespace上做了改进,已经实现将 temporary tablespace 从 ibdata(共享表空间文件)中分离。并且可以重启重置大小,避免出现像以前 ibdata 过大难以释放的问题。
其参数为:innodb_temp_data_file_path

1.表现

MySQL启动时 datadir 下会创建一个 ibtmp1 文件,初始大小为 12M,默认值下会无限扩展:

通常来说,查询导致的临时表(如group by)如果超出 tmp_table_size、max_heap_table_size 大小限制则创建 innodb 磁盘临时表(MySQL5.7默认临时表引擎为 innodb),存放在共享临时表空间;

如果某个操作创建了一个大小为100 M的临时表,则临时表空间数据文件会扩展到 100M大小以满足临时表的需要。当删除临时表时,释放的空间可以重新用于新的临时表,但 ibtmp1 文件保持扩展大小。

2.查询视图

可查询共享临时表空间的使用情况:

SELECT FILE_NAME, TABLESPACE_NAME, ENGINE, INITIAL_SIZE, TOTAL_EXTENTS*EXTENT_SIZE AS TotalSizeBytes, DATA_FREE,MAXIMUM_SIZE FROM INFORMATION_SCHEMA.FILES WHERE TABLESPACE_NAME = 'innodb_temporary'\G
*************************** 1. row ***************************
    FILE_NAME: /data/mysql5722/data/ibtmp1
TABLESPACE_NAME: innodb_temporary
      ENGINE: InnoDB
    INITIAL_SIZE: 12582912
   TotalSizeBytes: 31457280
    DATA_FREE: 27262976
  MAXIMUM_SIZE: 31457280
1 row in set (0.00 sec)

3.回收方式

重启 MySQL 才能回收

4.限制大小

为防止临时数据文件变得过大,可以配置该 innodb_temp_data_file_path (需重启生效)选项以指定最大文件大小,当数据文件达到最大大小时,查询将返回错误:

innodb_temp_data_file_path=ibtmp1:12M:autoextend:max:2G

5. 临时表空间与 tmpdir 对比

共享临时表空间用于存储非压缩InnoDB临时表(non-compressed InnoDB temporary tables)、关系对象(related objects)、回滚段(rollback segment)等数据;

tmpdir 用于存放指定临时文件(temporary files)和临时表(temporary tables),与共享临时表空间不同的是,tmpdir存储的是compressed InnoDB temporary tables。

可通过如下语句测试:

CREATE TEMPORARY TABLE compress_table (id int, name char(255)) ROW_FORMAT=COMPRESSED;
CREATE TEMPORARY TABLE uncompress_table (id int, name char(255)) ;

附件

SimpleExample.java

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicLong;
public class SimpleExample {
 public static void main(String[] args) throws Exception {
  Class.forName("com.mysql.jdbc.Driver");
  Properties props = new Properties();
  props.setProperty("user", "root");
  props.setProperty("password", "root");
  SimpleExample engine = new SimpleExample();
//  engine.execute(props,"jdbc:mysql://10.186.24.31:3336/hucq?useSSL=false");
  engine.execute(props,"jdbc:mysql://10.186.24.31:3336/hucq?useSSL=false&useCursorFetch=true");
 }
 final AtomicLong tmAl = new AtomicLong();
 final String tableName="test";
 public void execute(Properties props,String url) {
  CountDownLatch cdl = new CountDownLatch(1);
  long start = System.currentTimeMillis();
  for (int i = 0; i < 1; i++) {
   TestThread insertThread = new TestThread(props,cdl, url);
   Thread t = new Thread(insertThread);
   t.start();
   System.out.println("Test start");
  }
  try {
   cdl.await();
   long end = System.currentTimeMillis();
   System.out.println("Test end,total cost:" + (end-start) + "ms");
  } catch (Exception e) {
  }
 }

 class TestThread implements Runnable {
  Properties props;
  private CountDownLatch countDownLatch;
  String url;
  public TestThread(Properties props,CountDownLatch cdl,String url) {
   this.props = props;
   this.countDownLatch = cdl;
   this.url = url;
  }
  public void run() {
   Connection connection = null;
   PreparedStatement ps = null;
   Statement st = null;
   long start = System.currentTimeMillis();
   try {
    connection = DriverManager.getConnection(url,props);
    connection.setAutoCommit(false);
    st = connection.createStatement();

    //st.setFetchSize(500);
    st.setFetchSize(Integer.MIN_VALUE); //仅修改此处即可

    ResultSet rstmp;

    st.executeQuery("select sum(k) from sbtest1 group by k");
    rstmp = st.getResultSet();
    while(rstmp.next()){

    }
   } catch (Exception e) {
    System.out.println(System.currentTimeMillis() - start);
    System.out.println(new java.util.Date().toString());
    e.printStackTrace();
   } finally {
    if (ps != null)
     try {
      ps.close();
     } catch (SQLException e1) {
      e1.printStackTrace();
     }
    if (connection != null)
     try {
      connection.close();
     } catch (SQLException e1) {
      e1.printStackTrace();
     }
    this.countDownLatch.countDown();
   }
  }
 }
}

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对我们的支持。

(0)

相关推荐

  • MySQL中关于临时表的一些基本使用方法

    临时表可能是非常有用的,在某些情况下,保持临时数据.最重要的是应该知道的临时表是,他们将当前的客户端会话终止时被删除. 临时表中添加MySQL版本3.23.如果您使用的是旧版本的MySQL比3.23,可以不使用临时表,但可以使用堆表. 如前所述临时表将只持续只要的会话是存在的.如果运行一个PHP脚本中的代码,该临时表将被销毁时,会自动执行完脚本后.如果已连接到MySQL数据库的服务器上,通过MySQL的客户端程序的临时表将一直存在,直到关闭客户端或手动破坏的表. 实例 下面是一个例子,使用临时表

  • MySQL问答系列之什么情况下会用到临时表

    临时表介绍 什么是临时表:MySQL用于存储一些中间结果集的表,临时表只在当前连接可见,当关闭连接时,Mysql会自动删除表并释放所有空间.为什么会产生临时表:一般是由于复杂的SQL导致临时表被大量创建 临时表分为两种,一种是内存临时表,一种是磁盘临时表.内存临时表采用的是memory存储引擎,磁盘临时表采用的是myisam存储引擎(磁盘临时表也可以使用innodb存储引擎,通过internal_tmp_disk_storage_engine参数来控制使用哪种存储引擎,从mysql5.7.6之后

  • MySQL中临时表的基本创建与使用教程

    当工作在非常大的表上时,你可能偶尔需要运行很多查询获得一个大量数据的小的子集,不是对整个表运行这些查询,而是让MySQL每次找出所需的少数记录,将记录选择到一个临时表可能更快些,然后在这些表运行查询. 创建临时表很容易,给正常的CREATE TABLE语句加上TEMPORARY关键字: CREATE TEMPORARY TABLE tmp_table ( name VARCHAR(10) NOT NULL, value INTEGER NOT NULL ) 临时表将在你连接MySQL期间存在.当

  • MySQL 5.7临时表空间如何玩才能不掉坑里详解

    导读 MySQL 5.7的目标是成为发布以来最安全的MySQL服务器,其在SSL/TLS和全面安全开发方面有一些重要的改变. MySQL 5.7起支持独立临时表空间,但个别时候也可能会踩坑的. MySQL 5.7起,开始采用独立的临时表空间(和独立的undo表空间不是一回事哟),命名ibtmp1文件,初始化12M,且默认无上限. 选项 innodb_temp_data_file_path 可配置临时表空间相关参数. innodb_temp_data_file_path = ibtmp1:12M:

  • mysql临时表用法分析【查询结果可存在临时表中】

    本文实例讲述了mysql临时表用法.分享给大家供大家参考,具体如下: 一.创建临时表可以将查询结果寄存.报表制作的查询sql中可以用到. (1)关于寄存方式,mysql不支持: select * into tmp from maintenanceprocess (2)可以使用: create table tmp (select ...) 举例: #单个工位检修结果表上部 drop table if EXISTS tmp_单个工位检修结果表(检查报告)上部; create table tmp_单个

  • 浅谈MySQL临时表与派生表

    关于派生表 当主查询中包含派生表,或者当select 语句中包含union字句,或者当select语句中包含一个字段的order by 子句(对另一个字段的group by 子句)时,MySQL为了完成查询,则需要自动创建临时表存储临时结果集,这种临时表由MySQL自行创建,自行维护,成为自动创建的临时表.对于自动创建的临时表,由于内存临时表的性能更为优越,mysql总是首先使用内存临时表,而当内存临时表变得太大时,达到某个阈值的时候,内存临时表就转存为外存临时表.也就是说,外存临时表是内存临时

  • MySQL中Update、select联用操作单表、多表,及视图与临时表的区别

    一.MySQL中使用从表A中取出数据来更新表B的内容 例如:要update表data中的一些列属性,但是修改属性的内容来源是来自表chanpin.SQL语言中不要显示的出现select关键字 update data d,chanpin c set d.zhulei=c.zhulei,d.xiaolei=c.xiaolei,d.fenxiang=c.fenxiang,d.zhuanye=c.zhuanye,d.jiliang=c.jiliang,d.gs=c.zgs,d.xzgs=c.zgs,d.

  • 深度解析MySQL 5.7之临时表空间

    临时表 临时表顾名思义,就是临时的,用完销毁掉的表. 数据既可以保存在临时的文件系统上,也可以保存在固定的磁盘文件系统上. 临时表有下面几种: 1.全局临时表 这种临时表从数据库实例启动后开始生效,在数据库实例销毁后失效.在MySQL里面这种临时表对应的是内存表,即memory引擎. 2.会话级别临时表 这种临时表在用户登录系统成功后生效,在用户退出时失效.在MySQL里的临时表指的就是以create temporary table 这样的关键词创建的表. 3.事务级别临时表 这种临时表在事务开

  • MySQL两种临时表的用法详解

    外部临时表 通过CREATE TEMPORARY TABLE 创建的临时表,这种临时表称为外部临时表.这种临时表只对当前用户可见,当前会话结束的时候,该临时表会自动关闭.这种临时表的命名与非临时表可以同名(同名后非临时表将对当前会话不可见,直到临时表被删除). 内部临时表 内部临时表是一种特殊轻量级的临时表,用来进行性能优化.这种临时表会被MySQL自动创建并用来存储某些操作的中间结果.这些操作可能包括在优化阶段或者执行阶段.这种内部表对用户来说是不可见的,但是通过EXPLAIN或者SHOW S

  • mysql复制中临时表的运用技巧

    我们知道,临时表有以下特性: 1. SESSION 级别,SESSION 一旦断掉,就被自动DROP 了. 2. 和默认引擎有关.如果默认引擎是INNODB,而你又疯狂的使用临时表.那么,你的IBDATA会被无限的增大. 3. 和磁盘表一样,默认写入到binlog, 而且被动的加入了rollback 计划. 幻想如下场景: 数据库损坏,也没有备份.刚好二进制日志全部保存完好,幸福了.导入二进制日志到MYSQL. 有两种方法: 1) mysqlbinlog ..... *.log | mysql

  • MySQL使用临时表加速查询的方法

    本文实例讲述了MySQL使用临时表加速查询的方法.分享给大家供大家参考.具体分析如下: 使用MySQL临时表,有时是可以加速查询的,下面就为您详细介绍使用MySQL临时表加速查询的方法. 把表的一个子集进行排序并创建MySQL临时表,有时能加速查询.它有助于避免多重排序操作,而且在其他方面还能简化优化器的工作.例如: 复制代码 代码如下: SELECT cust.name,rcVBles.balance,--other columns  SELECT cust.name,rcVBles.bala

  • 解析Mysql临时表及特点

    临时表是当连接没有断开时存在,一旦断开就不会存在,临时表的数据和结构都在内存中,可以做个测验,你创建一个临时表,但是到响应的数据目录下并不会找到.frm文件mysql> CREATE TEMPORARY TABLE tmp_table (    ->    -> name VARCHAR(10) NOT NULL,    -> value INTEGER NOT NULL    ->    -> ) ;Query OK, 0 rows affected (0.38 se

随机推荐