Mysql临时表

    xiaoxiao2025-04-26  9

    当你创建临时表的时候,你可以使用temporary关键字。如:

      create temporary table tmp_table(name varchar(10) not null,passwd char(6) not null)‘

         CREATE TEMPORARY TABLE IF NOT EXISTS sp_output_tmp ENGINE = MEMORYSELECT …from … where ID=current_id;

       临时表只在当前连接可见,当这个连接关闭的时候,会自动drop。这就意味着你可以在两个不同的连接里使用相同的临时表名,并且相互不会冲突,或者使用 已经存在的表,但不是临时表的表名。(当这个临时表存在的时候,存在的表被隐藏了,如果临时表被drop,存在的表就可见了)。创建临时表你必须有

      create temporary table 权限。

      下面几点是临时表的限制:

      临时表只能用在 memory,myisam,merge,或者innodb

      临时表不支持mysql cluster(簇)

      在同一个query语句中,你只能查找一次临时表。例如:下面的就不可用

      mysql> SELECT * FROM temp_table, temp_table AS t2;

      ERROR 1137: Can't reopen table: 'temp_table'

      如果在一个存储函数里,你用不同的别名查找一个临时表多次,或者在这个存储函数里用不同的语句查找,这个错误都会发生。

      show tables 语句不会列举临时表

      你不能用rename来重命名一个临时表。但是,你可以alter table代替:

      mysql>ALTER TABLE orig_name RENAME new_name;

    临时表用完后要记得drop掉:

        DROP TEMPORARY TABLE IF EXISTS sp_output_tmp;

    =========================================================================================

    近日,线上MySQL查出一个慢sql,每次都要查询1000ms以上,严重影响用户体验

    今得空去诊断一番,记录如下:

    sql原句:

    [html]  view plain  copy  print ? SELECT r.object_id AS cardId, count(1) AS attachs FROM hzresource_object r         LEFT JOIN        ( SELECT card_id FROM card_member WHERE user_id = #uid# and card_member.deleted=0        UNION       SELECT card_id FROM card_subscribed where user_id = #uid# and card_subscribed.deleted=0       ) m ON r.object_id = m.card_id       WHERE r.object_type = #objectType# AND r.deleted = 0       GROUP BY r.object_id;  

    解决问题:

    由于对数据库优化一知半解,完全无从下手,只能求助度娘和谷哥了,试验了各种方法,都不见效果

    几番周折之后,最终把注意力集中到了临时表上,因为explain查看执行计划,可以看到Using temporary

    MySQL在执行SQL查询时可能会用到临时表,一般情况下,用到临时表就意味着性能较低。

    于是想办法修改sql语句,摒弃临时表,修改如下:

    [html]  view plain  copy  print ? SELECT r.object_id AS cardId, count(1) AS attachs FROM hzresource_object r         WHERE r.object_type = #objectType#  AND r.deleted = 0 and r.object_id in (       SELECT card_id FROM card_member WHERE user_id = #uid# and card_member.deleted=0           UNION         SELECT card_id FROM card_subscribed where user_id = #uid# and card_subscribed.deleted=0       )       GROUP BY r.object_id;  

    即把语句给拆分成两个sql语,用in操作拼接

    本机测试:

    优化前执行时间1040ms,优化后执行时间:85ms,执行速度是原来的12倍多!赞

    PS:

    常理我们都会排斥用in操作,用union替换,那为什么这里用in会更快呢?

    带着问题,接着去网上找,原来:

    sql执行会生成一个巨大的临时表,当内存放不下时,要全部copy 到磁盘,导致IO飙升,时间开销增大。

    额外收获知识收藏如下:

    临时表存储

    MySQL临时表分为“内存临时表”和“磁盘临时表”,其中内存临时表使用MySQL的MEMORY存储引擎,磁盘临时表使用MySQL的MyISAM存储引擎; 一般情况下,MySQL会先创建内存临时表,但内存临时表超过配置指定的值后,MySQL会将内存临时表导出到磁盘临时表;

    使用临时表的场景 1)ORDER BY子句和GROUP BY子句不同, 例如:ORDERY BY price GROUP BY name; 2)在JOIN查询中,ORDER BY或者GROUP BY使用了不是第一个表的列 例如:SELECT * from TableA, TableB ORDER BY TableA.price GROUP by TableB.name 3)ORDER BY中使用了DISTINCT关键字 ORDERY BY DISTINCT(price) 4)SELECT语句中指定了SQL_SMALL_RESULT关键字 SQL_SMALL_RESULT的意思就是告诉MySQL,结果会很小,请直接使用内存临时表,不需要使用索引排序 SQL_SMALL_RESULT必须和GROUP BY、DISTINCT或DISTINCTROW一起使用 一般情况下,我们没有必要使用这个选项,让MySQL服务器选择即可。

    直接使用磁盘临时表的场景 1)表包含TEXT或者BLOB列; 2)GROUP BY 或者 DISTINCT 子句中包含长度大于512字节的列; 3)使用UNION或者UNION ALL时,SELECT子句中包含大于512字节的列;

    表的设计原则 使用临时表一般都意味着性能比较低,特别是使用磁盘临时表,性能更慢,因此我们在实际应用中应该尽量避免临时表的使用。 常见的避免临时表的方法有: 1)创建索引:在ORDER BY或者GROUP BY的列上创建索引; 2)分拆很长的列:一般情况下,TEXT、BLOB,大于512字节的字符串,基本上都是为了显示信息,而不会用于查询条件, 因此表设计的时候,应该将这些列独立到另外一张表。 SQL优化 如果表的设计已经确定,修改比较困难,那么也可以通过优化SQL语句来减少临时表的大小,以提升SQL执行效率。 常见的优化SQL语句方法如下: 1)拆分SQL语句 临时表主要是用于排序和分组,很多业务都是要求排序后再取出详细的分页数据,这种情况下可以将排序和取出详细数据拆分成不同的SQL,以降低排序或分组时临时表的大小,提升排序和分组的效率,我们的案例就是采用这种方法。 2)优化业务,去掉排序分组等操作 有时候业务其实并不需要排序或分组,仅仅是为了好看或者阅读方便而进行了排序,例如数据导出、数据查询等操作,这种情况下去掉排序和分组对业务也没有多大影响。 如何判断使用了临时表? 使用explain查看执行计划,Extra列看到Using temporary就意味着使用了临时表。

    小结:

    可见, 完全颠覆了对in操作符的认识,凡事儿都是要分情况讨论的

    ========================================================================================

    转载请注明原文地址: https://ju.6miu.com/read-1298460.html
    最新回复(0)