MySQL-刪除數據和count(*)原理


delete刪除數據原理

在InndoDB存儲引擎中,delete刪除操作是把需要刪除的數據或者頁標記為已刪除,后面如果有需要,直接復用即可。這些被標記為已經刪除的數據,看起來就像空洞一樣。所以看起來雖然delete了,但是表文件大小並不會改變。

如果想刪除這些空洞,達到收縮表空間的目的,可以使用alter table t engine=InnoDB來重建表,內部流程如下:

  1. 新建一個表結構相同的表b

  2. 把數據,從表a按照主鍵遞增的順序一行一行讀出來然后插入表b

  3. 操作完畢后,用表b替換表a

在mysql5.6之前,這個操作在整個過程中,不能有更新操作。mysql5.6以后引入的Online DDL,可以對上面的步驟進行優化。

  • 首先,復習一下onlineDDL的概念:在之前的筆記MySQL-全句鎖、表鎖和元數據鎖中,學習元數據鎖的時候也提到過online DDL的概念,核心就是先申請一個MDL寫鎖,成功以后會降級為MDL讀鎖,然后做真正的DDL操作,操作完畢后再申請MDL寫鎖。

  • 其次,在做真正的DDL操作時,既然不阻塞DML操作,那么對表的DML操作如何體現到新表中呢?MySQL的實現是把對表的更新操作記錄在一個日志文件(row log)中,等到拷貝完原數據到到臨時表后,再對這個臨時表應用row log中的修改。


count(*)原理

在不同的存儲引擎中,對count(*)的實現是不同的

  • MyISAM中,會保存總行數到磁盤中,每次select count(*)會直接返回這個值

  • InnoDB中,計算count(*)的時候,需要把數據從引擎中一行一行讀出來,然后計算累加值

為什么InnoDB不把數據保存起來?

在之前的文章 MySQL-事務中的一致性讀和鎖定讀的具體原理中,我們學習過事務,了解了MVCC,知道不同時刻啟動的事務,拿到的事務視圖是不一樣的,在可重復讀的隔離級別設置下,事務中讀取數據始終是一致的。這個問題其實就和MVCC有關,既然每個事務視圖拿到的數據可能是不一樣的,那么就無法只存儲一個值,來代表行數。而且,MyISAM看起來把行數存儲為一個值,查詢的時候性能較高,但是一旦查詢條件中使用了where,那么就無法使用這個值了。

不同count寫法的性能對比

首先需要明確,count(XX),統計的是XX不為null的行數。

在實際統計時,有人會用count(*),有人用count(1),還有其他用count(字段)的,那么這些寫法有什么差別呢?

我們假設沒有where條件,直接查詢

整個過程分兩步:

  1. InnoDB存儲引擎查詢數據結果集

  2. Server層根據結果集進行遍歷統計

InnoDB存儲引擎查詢數據結果集時

InnoDB會在所有的索引中,選擇一個最小的索引來進行數據查詢

  • 如果有普通索引,就用最小的普通索引

  • 如果沒有普通索引,用主鍵索引

針對不同的查詢寫法,返回的數據結果集中的取值也不同

  • count(1)和count(*):InnoDB存儲引擎返回對應的數據列表,但是不取值,可以理解為返回一個List 但是data中沒有任何字段的。

  • count(字段):InnoDB存儲引擎返回對應的數據列表,需要取得對應字段的值

Server層根據結果集進行遍歷統計
  • count(1)和count(*):server層拿到數據后

    • 如果是count(1):server層在每行插入一個1,因為1肯定不為null,所以直接遍歷統計行數

    • 如果是count(*):server層直接遍歷統計行數

  • count(字段):

    • 如果是主鍵id和定義時不為空的字段:server層直接進行遍歷統計,和count(1)和count(*)比,還有復制字段的開銷

    • 定義時可以為空的字段:server層需要取出字段,再判斷一下是否為null,不為空的才統計

總結

在沒有where查詢條件時,我們可以看到,不同count寫法的性能優劣如下:

count(*) ≈count(1) > count(字段)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM