mysql 快速删除大表 count in exists区别 (四)

一、删除表的几种方式和快速删除大表

  1. mysql表的存储

    1. innoDB表存储分为表结构和表数据,表结构存储在.frm结尾的文件中,表数据存储在.ibd结尾的文件中
    2. 可以通过innodb_file_per_table 控制,为YES,是放在系统共享空间和数据词典放在一起,为NO时是分开放置,5.6.6版本默认为NO.这里建议分开放置,放在独立表空间,我们删除表数据时可以直接删除.ibd结尾的文件
  2. delete from 表名称

    1. delete 语句用于删除表中的行。delete语句执行删除的过程是每次从表中删除一行,并且同时将该行的删除操作作为事务记录在日志中保存以便进行进行回滚操作。
    2. delete 命令删除数据,只会把数据所在的数据页标记为可复用,空间不会释放掉,在磁盘上,文件不会变小
  3. truncate table 表名称

    1. (清空表中所有的数据):删除内容、释放空间但不删除定义(保留表的数据结构)。与drop不同的是,只是清空表数据而已。 注意:truncate 不能删除行数据,要删就要把表清空。
    2. truncate与不带where的delete :只删除数据,而不删除表的结构(定义)
  4. drop table 表名称

    1. 删除内容和定义,释放空间。简单来说就是把整个表去掉.以后要新增数据是不可能的,除非新增一个表。
    2. 也会直接删除这个文件,会直接释放空间
  5. alter table A engine=InnoDB 利用重建表命令,可以收缩表空间,这个命令比较耗时

  6. 执行速度,一般来说: drop> truncate > delete。

  7. 利用硬链接和myslq把ibd文件单独存储,配合删除

    1. 假设,我们有datadir = /data/mysql/,另外,我们有一个database,名为mytest。在数据库mytest中,有一个表,名为erp,执行下列命令
mysql> system ls -l /data/mysql/mytest/
-rw-r----- 1 mysql mysql          9023  8 18 05:21 erp.frm
-rw-r----- 1 mysql mysql 2356792000512  8 18 05:21 erp.ibd
  1. frm和ibd的作用,上面介绍过了。现在就是erp.ibd文件太大,所以删除卡住了。如何解决这个问题呢?这里需要利用了linux中硬链接的知识,来进行快速删除。 至于这个硬链接,就是对于真正存储的文件来说,有一个Inode Index指向存储文件,然后呢有一个文件名指向的Inode Index,那么,所谓的硬链接,就是不止一个文件名指向Inode Index,有好几个文件名指向Inode Index。假设,这会又有一个文件名指向上面的Inode Index,即这个时候,你做了删除文件名(1)的操作,linux系统检测到,还有一个文件名(2)指向Inode Index,因此并不会真正的把文件删了,而是把文件名(1)的引用给删了,这步操作非常快,毕竟只是删除引用。 接下来,你再做删除文件名(2)的操作,linux系统检测到,没有其他文件名指向该Inode Index,就会删除真正的存储文件,这步操作,是删真正的文件,所以比较慢
  2. 先给erp.ibd建立一个硬链接,利用ln命令
mysql> system ln /data/mysql/mytest/erp.ibd /data/mysql/mytest/erp.ibd.hdlk 
#此时,文件目录如下所示
-rw-r----- 1 mysql mysql          9023  8 18 05:21 erp.frm
-rw-r----- 2 mysql mysql 2356792000512  8 18 05:21 erp.ibd
-rw-r----- 2 mysql mysql 2356792000512  8 18 05:21 erp.ibd.hdlk 
# 你会发现,多了一个erp.ibd.hdlk文件,且erp.ibd和erp.ibd.hdlk的innode均为2。
#此时,你执行drop table操作
mysql> drop table erp;
Query OK, 0 rows affected (0.99 sec)
  1. 你会发现,不到1秒就删除了。因为,此时有两个文件名称(erp.ibd和erp.ibd.hdlk),同时指向一个innode.这个时候,执行删除操作,只是把引用给删了,所以非常快。
    那么,这时的删除,已经把table从mysql中删除。但是磁盘空间,还没释放,因为还剩一个文件erp.ibd.hdlk。

  2. 如何正确的删除erp.ibd.hdlk呢?
    如果你没啥经验,一定会回答我,用rm命令来删。这里需要说明的是,在生产环境,直接用rm命令来删大文件,会造成磁盘IO开销飙升,CPU负载过高,是会影响其他程序运行的。
    那么,这种时候,就是应该用truncate命令来进行删除。需要说明的是,truncate命令在coreutils工具集中,需要另外安装。详情,大家可以去百度一下安装教程

  3. 参考链接:如何快速删除大表

二、效率: count(*)= count(1)> count(id) > count(具体字段)

  1. 先说结论:
    1. 一般情况下,三者执行的效率为 COUNT()= COUNT(1)> COUNT(字段)。我们尽量使用COUNT(),当然如果你要统计的是某个字段的非空数据行数,则另当别论,毕竟比较执行效率的前提是结果一样才可以。
    2. 如果要统计COUNT(),尽量在数据表上建立二级索引,系统会自动采用key_len小的二级索引进行扫描,这样当我们使用SELECT COUNT()的时候效率就会提升,有时候可以提升几倍甚至更高。
  2. count(*) : 查询包括null值, 在 MySQL InnoDB 存储引擎中,COUNT()和COUNT(1)都是对所有结果进行COUNT。如果有 WHERE 子句,则是对所有符合筛选条件的数据行进行统计;如果没有 WHERE 子句,则是对数据表的数据行数进行统计。因此COUNT()和COUNT(1)本质上并没有区别,执行的复杂度都是O(N),也就是采用全表扫描,进行循环 + 计数的方式进行统计。
  3. count(1):查询包括null值 ,InnoDB 引擎遍历整张表,但不取值。server 层对于返回的每一行,放一个数字“1”进去,判断是不可能为空的,按行累加。
  4. count(具体字段),则表示返回满足条件的数据行里面,参数“字段”不为 NULL 的总个数 而count(字段)的话,如果字段上没有索引,就只能选主键索引 count(id)可能会选择最小的索引来遍历
  5. count(id): InnoDB 引擎会遍历整张表,把每一行的 id 值都取出来,返回给 server 层。server 层拿到 id 后,判断是不可能为空的,就按行累加。count(id) 也是走普通索引 ,普通索引比主键索引要小,遍历起来更快

三 、 哪种情况下应该使用 EXISTS,哪种情况应该用 IN。选择的标准是看能否使用表的索引吗?

  1. 索引是个前提,其实选择与否还是要看表的大小。你可以将选择的标准理解为小表驱动大表。在这种方式下效率是最高的。
  2. 先说结论 in 后跟小表,exist后跟大表 小表驱动大表 ,简记:in小,exists大。
    1. IN表是外边和内表进行hash连接,是先执行子查询。
    2. EXISTS是对外表进行循环,然后在内表进行查询。
    3. 因此如果外表数据量大,则用IN,如果外表数据量小,也用EXISTS。
    4. IN有一个缺陷是不能判断NULL,因此如果字段存在NULL值,则会出现返回,因为最好使用NOT EXISTS。
  3. 比如下面这样:
 SELECT * FROM A WHERE cc IN (SELECT cc FROM B)
 SELECT * FROM A WHERE EXISTS (SELECT cc FROM B WHERE B.cc=A.cc)

当 A 小于 B 时,用 EXISTS。因为 EXISTS 的实现,相当于外表循环,实现的逻辑类似于:

 for i in A
     for j in B
         if j.cc == i.cc then ...

当 B 小于 A 时用 IN,因为实现的逻辑类似于:

 for i in B
     for j in A
         if j.cc == i.cc then ...

哪个表小就用哪个表来驱动,A 表小就用 EXISTS,B 表小就用 IN。

  1. 实际上在查询过程中,在我们对 cc 列建立索引的情况下,我们还需要判断表 A 和表 B 的大小。

  2. 如果表 A 比表 B 大,那么 IN 子查询的效率要比 EXIST 子查询效率高,因为这时 B 表中如果对 cc 列进行了索引,那么 IN 子查询的效率就会比较高。

  3. 同样,如果表 A 比表 B 小,那么使用 EXISTS 子查询效率会更高,因为我们可以使用到 A 表中对 cc 列的索引,而不用从 B 中进行 cc 列的查询。

  4. 优化原则
    在这里插入图片描述

  5. 参考链接

    1. MySQL高级知识(十六)——小表驱动大表

四、mysql中的几种连接

  1. 内连接:将多个表之间满足连接条件的数据行查询出来。它包括了等值连接、非等值连接和自连接(NATURAL JOIN)。
  2. 外连接:会返回一个表中的所有记录,以及另一个表中匹配的行。它包括了左外连接(left join on)、右外连接(right left on)和全连接(full join on mysql不支持)。
  3. 交叉连接:也称为笛卡尔积,返回左表中每一行与右表中每一行的组合。在 SQL99 中使用的 CROSS JOIN。
  4. 建议
    1. 使用自连接而不是子查询
    2. 控制连接表的数量
    3. . 在连接时不要忘记 WHERE 语句

五、关于 SELECT 语句内部的执行步骤

  1. 完整的SELECT语句内部执行顺序是:
    1、FROM子句组装数据(包括通过ON进行连接)
    2、WHERE子句进行条件筛选
    3、GROUP BY分组
    4、使用聚集函数进行计算;
    5、HAVING筛选分组;
    6、计算所有的表达式;
    7、SELECT 的字段;
    8、ORDER BY排序
    9、LIMIT筛选

六、可以理解在 WHERE 条件字段上加索引,但是为什么在 ORDER BY 字段上还要加索引呢?

  1. 在 MySQL 中,支持两种排序方式,分别是 FileSort 和 Index 排序。

    1. 在 Index 排序中,索引可以保证数据的有序性,不需要再进行排序,效率更高。
    2. FileSort 排序则一般在内存中进行排序,占用 CPU 较多。如果待排结果较大,会产生临时文件 I/O 到磁盘进行排序的情况,效率较低。
    3. 所以使用 ORDER BY 子句时,应该尽量使用 Index 排序,避免使用 FileSort 排序。当然你可以使用 explain 来查看执行计划,看下优化器是否采用索引进行排序。
  2. 优化建议:

    1. SQL 中,可以在 WHERE 子句和 ORDER BY 子句中使用索引,目的是在 WHERE 子句中避免全表扫描,
    2. 在 ORDER BY 子句避免使用 FileSort 排序。当然,某些情况下全表扫描,或者 FileSort 排序不一定比索引慢。但总的来说,我们还是要避免,以提高查询效率。一般情况下,优化器会帮我们进行更好的选择,当然我们也需要建立合理的索引。
    3. 尽量使用 Index 完成 ORDER BY 排序。如果 WHERE 和 ORDER BY 后面是相同的列就使用单索引列;如果不同就使用联合索引。
    4. 无法使用 Index 时,需要对 FileSort 方式进行调优。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章