## 問題1:數據庫`delete`表數據,磁盤空間會減少嗎?為什么?(以MySQL數據庫,InnoDB為例解答)
* 回答:刪除表數據,磁盤空間**并不會**減少,因為`delete`命令只是把刪除記錄的位置或者數據頁標記為**可復用**,相當于軟刪除,物理空間并不會改變。
* 原因分析:分別從以下相關知識點展開;
1. MySQL數據庫InnoDB引擎下,表數據可以存在共享表空間,也可以存在獨立表空間(單獨的文件);而這個行為是由`innodb_file_per_table`參數控制的。
* `innodb_file_per_table=OFF`: 表數據放在共享表空間,也就是跟表結構存在一個文件中(xxx **.frm** 文件)
* `innodb_file_per_table=ON`: 表數據存放在獨立表空間中,每個InnoDB表數據存放在一個以 **.ibd**為后綴的文件中
**將`innodb_file_per_table`設置為ON是推薦做法,所以接下來的討論都是基于這個設置展開的。**
2. 數據刪除流程
InnoDB里的數據都是用`B+`樹的結構組織的。這里可以借助與InnoDB中索引示意圖來分析:
:-: 
如果,刪掉R4這條記錄,InnoDB引擎只會把R4這條記錄標記為刪除。之后要是插入一個ID在300和400之間的記錄時,可能會復用這個位置。但是**磁盤文件大小不會變**。 </br>
InnoDB的數據都是按照頁存儲的,那么如果刪除了一個數據頁上的所有記錄,會怎么樣?答案就是**整個數據頁都會被標記為刪除,也就是整個數據頁都可以被復用**。 </br>
但是**數據頁的復用跟記錄的復用是不一樣的**。記錄的復用只限于復合范圍條件的數據。
</br>
## 問題2:怎么壓縮表空間?怎么才能讓表大小變小?
`DELETE`只是將數據標識為刪除,并不是整理數據文件;當有新數據插入后,會再次使用被標識為刪除的記錄空間。那么要想回收未使用的空間(增、刪、改產生的空洞),可以使用以下幾種方式:
1. `OPTIMIZE TABLE 表名` 相當于 **recreate**
2. `ALTER TABLE 表名 ENGINE=INNODB` 相當于 **recreate + analyze**
</br>
## 問題3:一個InnoDB引擎表的數據,數據量非常大;基于這個條件,會不會有根據二級索引搜索比根據主鍵索引搜索快的情況?如果有,那么是什么情況下會出現?為什么?
回答:會有這種情況發生。
InnoDB索引是按頁存儲的,每一頁是固定16KB;主鍵葉子節點存儲的是一整行的數據,二級索引存儲的是索引值+主鍵值;所以二級索引每一頁存儲的數據要比主鍵索引每一頁存儲的數據要少,自然而然二級索引所占的頁數就要少,那么整個二級索引樹就要比主鍵索引樹要矮,這樣查詢的時候磁盤I/O的次數也就會少。那么基于這個前提條件,在數據量非常大,再加上索引覆蓋的情況下,是很有可能出現上述問題中的現象。
</br>
## 問題4:下述兩條語句有什么區別?為什么提倡使用語句2?
1. `select * from t where id in (1,2,3,4,5)` **需要搜索樹5次**
2. `select * from t where id between 1 and 5` **只需要搜索樹1次**
回答:in 中的數字是未知的,不知道是否是連續的,并且是有序的,所以只能一個一個去查找比較;between ... and ... 的用法,是可以確定連續并且升序的,所以只需要搜索1次,然后循環比較就可以。
前提知識:B+樹中,索引的葉子節點是順序存儲的,兩個節點之間通過雙向指針連接。
</br>
## 問題5:改動數據量很小的表(加字段或者加索引等)會導致鎖表嗎?如何安全的給小表加字段?
回答:即使是數據量很小的表,還是有可能出現鎖表的情況。
比較理想的機制是,在 alter table 語句里面設定等待時間,如果在這個指定的等待時間里面能夠拿到 MDL 寫鎖最好,拿不到也不要阻塞后面的業務語句,先放棄。之后開發人員或者 DBA 再通過重試命令重復這個過程。
```mysql
ALTER TABLE tbl_name NOWAIT add column ...
ALTER TABLE tbl_name WAIT N add column ...
```
</br>
## 問題6:在業務上可以保證唯一性的前提下,給相應字段創建普通索引好還是唯一索引更合適?為什么?
回答:推薦使用普通索引
原因:
1. 從查詢數據性能角度分析:
普通索引查到第一個滿足條件的記錄后,繼續查找,直到第一個不滿足條件的記錄出現為止;而唯一索引由于確定唯一性,所以查到滿足條件的記錄后,就停止搜索;但是,由于在InnoDB引擎下,索引是按頁存儲的,一次性會將一個頁的數據讀入內存,所以這種差距可忽略不計。
2. 從更新數據性能角度分析:
普通索引更新數據,會引入change_buffer的概念,會大大的提高效率;但是change_buffer不適用唯一索引,從而會增加I/O次數,降低性能。
> change_buffer:在更新數據的時候,將數據存入change_buffer;等下一次讀取到這條數據的時候,先進行change_buffer里面的操作處理,這個過程稱之為merge,會減少磁盤I/O。