## 问题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中索引示意图来分析:
:-: ![](images/B+数据索引示意图.webp)
如果,删掉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。