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 方式進行調優。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章