auto commit
This commit is contained in:
parent
b04f8e35a4
commit
49370134c3
187
notes/MySQL.md
187
notes/MySQL.md
|
@ -9,10 +9,10 @@
|
||||||
* [字符串](#字符串)
|
* [字符串](#字符串)
|
||||||
* [时间和日期](#时间和日期)
|
* [时间和日期](#时间和日期)
|
||||||
* [三、索引](#三索引)
|
* [三、索引](#三索引)
|
||||||
|
* [B-Tree 和 B+Tree 原理](#b-tree-和-btree-原理)
|
||||||
* [索引分类](#索引分类)
|
* [索引分类](#索引分类)
|
||||||
* [索引的优点](#索引的优点)
|
* [索引的优点](#索引的优点)
|
||||||
* [索引优化](#索引优化)
|
* [索引优化](#索引优化)
|
||||||
* [B-Tree 和 B+Tree 原理](#b-tree-和-btree-原理)
|
|
||||||
* [四、查询性能优化](#四查询性能优化)
|
* [四、查询性能优化](#四查询性能优化)
|
||||||
* [使用 Explain 进行分析](#使用-explain-进行分析)
|
* [使用 Explain 进行分析](#使用-explain-进行分析)
|
||||||
* [优化数据访问](#优化数据访问)
|
* [优化数据访问](#优化数据访问)
|
||||||
|
@ -34,7 +34,7 @@ InnoDB 是 MySQL 默认的事务型存储引擎,只有在需要 InnoDB 不支
|
||||||
|
|
||||||
采用 MVCC 来支持高并发,并且实现了四个标准的隔离级别,默认级别是可重复读(REPEATABLE READ),并且通过间隙锁(next-key locking)策略防止幻读的出现。间隙锁使得 InnoDB 不仅仅锁定查询涉及的行,还会对索引中的间隙进行锁定,以防止幻影行的插入。
|
采用 MVCC 来支持高并发,并且实现了四个标准的隔离级别,默认级别是可重复读(REPEATABLE READ),并且通过间隙锁(next-key locking)策略防止幻读的出现。间隙锁使得 InnoDB 不仅仅锁定查询涉及的行,还会对索引中的间隙进行锁定,以防止幻影行的插入。
|
||||||
|
|
||||||
表是基于聚簇索引建立的,它对主键的查询性能有很高的提升。
|
表是基于聚簇索引建立的,它对主键的查询性能有很大的提升。
|
||||||
|
|
||||||
内部做了很多优化,包括从磁盘读取数据时采用的可预测性读、能够自动在内存中创建哈希索引以加速读操作的自适应哈希索引、能够加速插入操作的插入缓冲区等。
|
内部做了很多优化,包括从磁盘读取数据时采用的可预测性读、能够自动在内存中创建哈希索引以加速读操作的自适应哈希索引、能够加速插入操作的插入缓冲区等。
|
||||||
|
|
||||||
|
@ -42,7 +42,7 @@ InnoDB 是 MySQL 默认的事务型存储引擎,只有在需要 InnoDB 不支
|
||||||
|
|
||||||
## MyISAM
|
## MyISAM
|
||||||
|
|
||||||
MyISAM 提供了大量的特性,包括全文索引、压缩表、空间数据索引等。应该注意的是,MySQL 5.6.4 也添加了对 InnoDB 存储引擎的全文索引支持。
|
MyISAM 提供了大量的特性,包括压缩表、空间数据索引等。
|
||||||
|
|
||||||
不支持事务。
|
不支持事务。
|
||||||
|
|
||||||
|
@ -120,6 +120,58 @@ MySQL 提供了 FROM_UNIXTIME() 函数把 UNIX 时间戳转换为日期,并提
|
||||||
|
|
||||||
对于非常小的表、大部分情况下简单的全表扫描比建立索引更高效。对于中到大型的表,索引就非常有效。但是对于特大型的表,建立和使用索引的代价将会随之增长。这种情况下,需要用到一种技术可以直接区分出需要查询的一组数据,而不是一条记录一条记录地匹配,例如可以使用分区技术。
|
对于非常小的表、大部分情况下简单的全表扫描比建立索引更高效。对于中到大型的表,索引就非常有效。但是对于特大型的表,建立和使用索引的代价将会随之增长。这种情况下,需要用到一种技术可以直接区分出需要查询的一组数据,而不是一条记录一条记录地匹配,例如可以使用分区技术。
|
||||||
|
|
||||||
|
## B-Tree 和 B+Tree 原理
|
||||||
|
|
||||||
|
### 1. B-Tree
|
||||||
|
|
||||||
|
<div align="center"> <img src="../pics//5ed71283-a070-4b21-85ae-f2cbfd6ba6e1.jpg"/> </div><br>
|
||||||
|
|
||||||
|
定义一条数据记录为一个二元组 [key, data],B-Tree 是满足下列条件的数据结构:
|
||||||
|
|
||||||
|
- 所有叶节点具有相同的深度,也就是说 B-Tree 是平衡的;
|
||||||
|
- 一个节点中的 key 从左到右非递减排列;
|
||||||
|
- 如果某个指针的左右相邻 key 分别是 key<sub>i</sub> 和 key<sub>i+1</sub>,且不为 null,则该指针指向节点的所有 key 大于等于 key<sub>i</sub> 且小于等于 key<sub>i+1</sub>。
|
||||||
|
|
||||||
|
查找算法:首先在根节点进行二分查找,如果找到则返回对应节点的 data,否则在相应区间的指针指向的节点递归进行查找。
|
||||||
|
|
||||||
|
由于插入删除新的数据记录会破坏 B-Tree 的性质,因此在插入删除时,需要对树进行一个分裂、合并、转移等操作以保持 B-Tree 性质。
|
||||||
|
|
||||||
|
### 2. B+Tree
|
||||||
|
|
||||||
|
<div align="center"> <img src="../pics//63cd5b50-d6d8-4df6-8912-ef4a1dd5ba13.jpg"/> </div><br>
|
||||||
|
|
||||||
|
与 B-Tree 相比,B+Tree 有以下不同点:
|
||||||
|
|
||||||
|
- 每个节点的指针上限为 2d 而不是 2d+1(d 为节点的出度);
|
||||||
|
- 内节点不存储 data,只存储 key;
|
||||||
|
- 叶子节点不存储指针。
|
||||||
|
|
||||||
|
### 3. 顺序访问指针
|
||||||
|
|
||||||
|
<div align="center"> <img src="../pics//1ee5f0a5-b8df-43b9-95ab-c516c54ec797.jpg"/> </div><br>
|
||||||
|
|
||||||
|
一般在数据库系统或文件系统中使用的 B+Tree 结构都在经典 B+Tree 基础上进行了优化,在叶子节点增加了顺序访问指针,做这个优化的目的是为了提高区间访问的性能。
|
||||||
|
|
||||||
|
### 4. B+Tree 和 B-Tree 优势
|
||||||
|
|
||||||
|
红黑树等平衡树也可以用来实现索引,但是文件系统及数据库系统普遍采用 B+Tree 和 B-Tree 作为索引结构,主要有以下两个原因:
|
||||||
|
|
||||||
|
**(一)更少的检索次数**
|
||||||
|
|
||||||
|
平衡树检索数据的时间复杂度等于树高 h,而树高大致为 O(h)=O(log<sub>d</sub>N),其中 d 为每个节点的出度。
|
||||||
|
|
||||||
|
红黑树的出度为 2,而 B+Tree 与 B-Tree 的出度一般都非常大。红黑树的树高 h 很明显比 B+Tree 和 B-Tree 大非常多,因此检索的次数也就更多。
|
||||||
|
|
||||||
|
B+Tree 相比于 B-Tree 更适合外存索引,因为 B+Tree 内节点去掉了 data 域,因此可以拥有更大的出度,检索效率会更高。
|
||||||
|
|
||||||
|
**(二)利用计算机预读特性**
|
||||||
|
|
||||||
|
为了减少磁盘 I/O,磁盘往往不是严格按需读取,而是每次都会预读。这样做的理论依据是计算机科学中著名的局部性原理:当一个数据被用到时,其附近的数据也通常会马上被使用。预读过程中,磁盘进行顺序读取,顺序读取不需要进行磁盘寻道,并且只需要很短的旋转时间,因此速度会非常快。
|
||||||
|
|
||||||
|
操作系统一般将内存和磁盘分割成固态大小的块,每一块称为一页,内存与磁盘以页为单位交换数据。数据库系统将索引的一个节点的大小设置为页的大小,使得一次 I/O 就能完全载入一个节点,并且可以利用预读特性,临近的节点也能够被预先载入。
|
||||||
|
|
||||||
|
更多内容请参考:[MySQL 索引背后的数据结构及算法原理](http://blog.codinglabs.org/articles/theory-of-mysql-index.html)
|
||||||
|
|
||||||
## 索引分类
|
## 索引分类
|
||||||
|
|
||||||
### 1. B+Tree 索引
|
### 1. B+Tree 索引
|
||||||
|
@ -130,13 +182,9 @@ MySQL 提供了 FROM_UNIXTIME() 函数把 UNIX 时间戳转换为日期,并提
|
||||||
|
|
||||||
B+Tree 索引是大多数 MySQL 存储引擎的默认索引类型。
|
B+Tree 索引是大多数 MySQL 存储引擎的默认索引类型。
|
||||||
|
|
||||||
因为不再需要进行全表扫描,只需要对树进行搜索即可,因此查找速度快很多。
|
因为不再需要进行全表扫描,只需要对树进行搜索即可,因此查找速度快很多。除了用于查找,还可以用于排序和分组。
|
||||||
|
|
||||||
可以指定多个列作为索引列,多个索引列共同组成键。B+Tree 索引适用于全键值、键值范围和键前缀查找,其中键前缀查找只适用于最左前缀查找。
|
可以指定多个列作为索引列,多个索引列共同组成键。B+Tree 索引适用于全键值、键值范围和键前缀查找,其中键前缀查找只适用于最左前缀查找。如果不是按照索引列的顺序进行查找,则无法使用索引。
|
||||||
|
|
||||||
除了用于查找,还可以用于排序和分组。
|
|
||||||
|
|
||||||
如果不是按照索引列的顺序进行查找,则无法使用索引。
|
|
||||||
|
|
||||||
### 2. 哈希索引
|
### 2. 哈希索引
|
||||||
|
|
||||||
|
@ -163,9 +211,9 @@ MyISAM 存储引擎支持空间数据索引,可以用于地理数据存储。
|
||||||
|
|
||||||
### 4. 全文索引
|
### 4. 全文索引
|
||||||
|
|
||||||
MyISAM 存储引擎支持全文索引,用于查找文本中的关键词,而不是直接比较索引中的值。
|
MyISAM 存储引擎支持全文索引,用于查找文本中的关键词,而不是直接比值是否相等。查找条件使用 MATCH AGAINST,而不是普通的 WHERE。
|
||||||
|
|
||||||
查找条件使用 MATCH AGAINST,而不是普通的 WHERE。
|
InnoDB 存储引擎在 MySQL 5.6.4 版本中也开始支持全文索引。
|
||||||
|
|
||||||
## 索引的优点
|
## 索引的优点
|
||||||
|
|
||||||
|
@ -187,13 +235,7 @@ MyISAM 存储引擎支持全文索引,用于查找文本中的关键词,而
|
||||||
SELECT actor_id FROM sakila.actor WHERE actor_id + 1 = 5;
|
SELECT actor_id FROM sakila.actor WHERE actor_id + 1 = 5;
|
||||||
```
|
```
|
||||||
|
|
||||||
### 2. 前缀索引
|
### 2. 多列索引
|
||||||
|
|
||||||
对于 BLOB、TEXT 和 VARCHAR 类型的列,必须使用前缀索引,只索引开始的部分字符。
|
|
||||||
|
|
||||||
对于前缀长度的选取需要根据 **索引选择性** 来确定:不重复的索引值和记录总数的比值。选择性越高,查询效率也越高。最大值为 1,此时每个记录都有唯一的索引与其对应。
|
|
||||||
|
|
||||||
### 3. 多列索引
|
|
||||||
|
|
||||||
在需要使用多个列作为条件进行查询时,使用多列索引比使用多个单列索引性能更好。例如下面的语句中,最好把 actor_id 和 film_id 设置为多列索引。
|
在需要使用多个列作为条件进行查询时,使用多列索引比使用多个单列索引性能更好。例如下面的语句中,最好把 actor_id 和 film_id 设置为多列索引。
|
||||||
|
|
||||||
|
@ -202,9 +244,11 @@ SELECT film_id, actor_ id FROM sakila.film_actor
|
||||||
WhERE actor_id = 1 AND film_id = 1;
|
WhERE actor_id = 1 AND film_id = 1;
|
||||||
```
|
```
|
||||||
|
|
||||||
### 4. 索引列的顺序
|
### 3. 索引列的顺序
|
||||||
|
|
||||||
让选择性最强的索引列放在前面,例如下面显示的结果中 customer_id 的选择性比 staff_id 更高,因此最好把 customer_id 列放在多列索引的前面。
|
让选择性最强的索引列放在前面,索引的选择性是指:不重复的索引值和记录总数的比值。最大值为 1,此时每个记录都有唯一的索引与其对应。选择性越高,查询效率也越高。
|
||||||
|
|
||||||
|
例如下面显示的结果中 customer_id 的选择性比 staff_id 更高,因此最好把 customer_id 列放在多列索引的前面。
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
SELECT COUNT(DISTINCT staff_id)/COUNT(*) AS staff_id_selectivity,
|
SELECT COUNT(DISTINCT staff_id)/COUNT(*) AS staff_id_selectivity,
|
||||||
|
@ -219,7 +263,23 @@ customer_id_selectivity: 0.0373
|
||||||
COUNT(*): 16049
|
COUNT(*): 16049
|
||||||
```
|
```
|
||||||
|
|
||||||
### 5. 聚簇索引
|
### 4. 前缀索引
|
||||||
|
|
||||||
|
对于 BLOB、TEXT 和 VARCHAR 类型的列,必须使用前缀索引,只索引开始的部分字符。
|
||||||
|
|
||||||
|
对于前缀长度的选取需要根据索引选择性来确定。
|
||||||
|
|
||||||
|
### 5. 覆盖索引
|
||||||
|
|
||||||
|
索引包含所有需要查询的字段的值。
|
||||||
|
|
||||||
|
**优点**
|
||||||
|
|
||||||
|
- 因为索引条目通常远小于数据行的大小,所以若只读取索引,能大大减少数据访问量。
|
||||||
|
- 一些存储引擎(例如 MyISAM)在内存中只缓存索引,而数据依赖于操作系统来缓存。因此,只访问索引可以不使用系统调用(通常比较费时)。
|
||||||
|
- 对于 InnoDB 引擎,若二级索引能够覆盖查询,则无需访问聚簇索引。
|
||||||
|
|
||||||
|
### 6. 聚簇索引
|
||||||
|
|
||||||
<div align="center"> <img src="../pics//e800b001-7779-495b-8459-d33a7440d7b8.jpg"/> </div><br>
|
<div align="center"> <img src="../pics//e800b001-7779-495b-8459-d33a7440d7b8.jpg"/> </div><br>
|
||||||
|
|
||||||
|
@ -231,77 +291,16 @@ customer_id_selectivity: 0.0373
|
||||||
|
|
||||||
**优点**
|
**优点**
|
||||||
|
|
||||||
1. 可以把相关数据保存在一起,减少 I/O 操作。例如电子邮件表可以根据用户 ID 来聚集数据,这样只需要从磁盘读取少数的数据也就能获取某个用户的全部邮件,如果没有使用聚聚簇索引,则每封邮件都可能导致一次磁盘 I/O。
|
- 可以把相关数据保存在一起,减少 I/O 操作。例如电子邮件表可以根据用户 ID 来聚集数据,这样只需要从磁盘读取少数的数据也就能获取某个用户的全部邮件,如果没有使用聚聚簇索引,则每封邮件都可能导致一次磁盘 I/O。
|
||||||
2. 数据访问更快。
|
- 数据访问更快。
|
||||||
|
|
||||||
**缺点**
|
**缺点**
|
||||||
|
|
||||||
1. 聚簇索引最大限度提高了 I/O 密集型应用的性能,但是如果数据全部放在内存,就没必要用聚簇索引。
|
- 聚簇索引最大限度提高了 I/O 密集型应用的性能,但是如果数据全部放在内存,就没必要用聚簇索引。
|
||||||
2. 插入速度严重依赖于插入顺序,按主键的顺序插入是最快的。
|
- 插入速度严重依赖于插入顺序,按主键的顺序插入是最快的。
|
||||||
3. 更新操作代价很高,因为每个被更新的行都会移动到新的位置。
|
- 更新操作代价很高,因为每个被更新的行都会移动到新的位置。
|
||||||
4. 当插入到某个已满的页中,存储引擎会将该页分裂成两个页面来容纳该行,页分裂会导致表占用更多的磁盘空间。
|
- 当插入到某个已满的页中,存储引擎会将该页分裂成两个页面来容纳该行,页分裂会导致表占用更多的磁盘空间。
|
||||||
5. 如果行比较稀疏,或者由于页分裂导致数据存储不连续时,聚簇索引可能导致全表扫描速度变慢。
|
- 如果行比较稀疏,或者由于页分裂导致数据存储不连续时,聚簇索引可能导致全表扫描速度变慢。
|
||||||
|
|
||||||
### 6. 覆盖索引
|
|
||||||
|
|
||||||
索引包含所有需要查询的字段的值。
|
|
||||||
|
|
||||||
**优点**
|
|
||||||
|
|
||||||
1. 因为索引条目通常远小于数据行的大小,所以若只读取索引,能大大减少数据访问量。
|
|
||||||
2. 一些存储引擎(例如 MyISAM)在内存中只缓存索引,而数据依赖于操作系统来缓存。因此,只访问索引可以不使用系统调用(通常比较费时)。
|
|
||||||
3. 对于 InnoDB 引擎,若二级索引能够覆盖查询,则无需访问聚簇索引。
|
|
||||||
|
|
||||||
## B-Tree 和 B+Tree 原理
|
|
||||||
|
|
||||||
### 1. B-Tree
|
|
||||||
|
|
||||||
<div align="center"> <img src="../pics//5ed71283-a070-4b21-85ae-f2cbfd6ba6e1.jpg"/> </div><br>
|
|
||||||
|
|
||||||
定义一条数据记录为一个二元组 [key, data],B-Tree 是满足下列条件的数据结构:
|
|
||||||
|
|
||||||
- 所有叶节点具有相同的深度,也就是说 B-Tree 是平衡的;
|
|
||||||
- 一个节点中的 key 从左到右非递减排列;
|
|
||||||
- 如果某个指针的左右相邻 key 分别是 key<sub>i</sub> 和 key<sub>i+1</sub>,且不为 null,则该指针指向节点的所有 key 大于等于 key<sub>i</sub> 且小于等于 key<sub>i+1</sub>。
|
|
||||||
|
|
||||||
在 B-Tree 中按 key 检索数据的算法非常直观:首先在根节点进行二分查找,如果找到则返回对应节点的 data,否则在相应区间的指针指向的节点递归进行查找。
|
|
||||||
|
|
||||||
由于插入删除新的数据记录会破坏 B-Tree 的性质,因此在插入删除时,需要对树进行一个分裂、合并、转移等操作以保持 B-Tree 性质。
|
|
||||||
|
|
||||||
### 2. B+Tree
|
|
||||||
|
|
||||||
<div align="center"> <img src="../pics//63cd5b50-d6d8-4df6-8912-ef4a1dd5ba13.jpg"/> </div><br>
|
|
||||||
|
|
||||||
与 B-Tree 相比,B+Tree 有以下不同点:
|
|
||||||
|
|
||||||
- 每个节点的指针上限为 2d 而不是 2d+1;
|
|
||||||
- 内节点不存储 data,只存储 key,叶子节点不存储指针。
|
|
||||||
|
|
||||||
### 3. 带有顺序访问指针的 B+Tree
|
|
||||||
|
|
||||||
<div align="center"> <img src="../pics//1ee5f0a5-b8df-43b9-95ab-c516c54ec797.jpg"/> </div><br>
|
|
||||||
|
|
||||||
一般在数据库系统或文件系统中使用的 B+Tree 结构都在经典 B+Tree 基础上进行了优化,在叶子节点增加了顺序访问指针,做这个优化的目的是为了提高区间访问的性能。
|
|
||||||
|
|
||||||
### 4. 为什么使用 B+Tree 和 B-Tree
|
|
||||||
|
|
||||||
红黑树等平衡树也可以用来实现索引,但是文件系统及数据库系统普遍采用 B+Tree B-Tree 作为索引结构,主要有以下两个原因:
|
|
||||||
|
|
||||||
**(一)更少的检索次数**
|
|
||||||
|
|
||||||
红黑树和 B+Tree B-Tree 检索数据的时间复杂度等于树高 h,而树高大致为 O(h)=O(log<sub>d</sub>N),其中 d 为每个节点的出度。
|
|
||||||
|
|
||||||
红黑树的出度为 2,而 B+Tree 与 B-Tree 的出度一般都非常大。红黑树的树高 h 很明显比 B+Tree B-Tree 大非常多,因此检索的次数也就更多。
|
|
||||||
|
|
||||||
B+Tree 相比于 B-Tree 更适合外存索引,因为 B+Tree 内节点去掉了 data 域,因此可以拥有更大的出度,检索效率会更高。
|
|
||||||
|
|
||||||
**(二)利用计算机预读特性**
|
|
||||||
|
|
||||||
为了减少磁盘 I/O,磁盘往往不是严格按需读取,而是每次都会预读。这样做的理论依据是计算机科学中著名的局部性原理:当一个数据被用到时,其附近的数据也通常会马上被使用。预读过程中,磁盘进行顺序读取,顺序读取不需要进行磁盘寻道,并且只需要很短的旋转时间,因此速度会非常快。
|
|
||||||
|
|
||||||
操作系统一般将内存和磁盘分割成固态大小的块,每一块称为一页,内存与磁盘以页为单位交换数据。数据库系统将索引的一个节点的大小设置为页的大小,使得一次 I/O 就能完全载入一个节点,并且可以利用预读特性,临近的节点也能够被预先载入。
|
|
||||||
|
|
||||||
更多内容请参考:[MySQL 索引背后的数据结构及算法原理](http://blog.codinglabs.org/articles/theory-of-mysql-index.html)
|
|
||||||
|
|
||||||
# 四、查询性能优化
|
# 四、查询性能优化
|
||||||
|
|
||||||
|
@ -355,14 +354,14 @@ do {
|
||||||
} while rows_affected > 0
|
} while rows_affected > 0
|
||||||
```
|
```
|
||||||
|
|
||||||
### 2. 分解关联查询
|
### 2. 分解大连接查询
|
||||||
|
|
||||||
将一个关联查询分解成对每一个表进行一次单表查询,然后将结果在应用程序中进行关联,这样做的好处有:
|
将一个大连接查询(JOIN)分解成对每一个表进行一次单表查询,然后将结果在应用程序中进行关联,这样做的好处有:
|
||||||
|
|
||||||
- 让缓存更高效。对于关联查询,如果其中一个表发生变化,那么整个查询缓存就无法使用。而分解后的多个查询,即使其中一个表发生变化,对其它表的查询缓存依然可以使用。
|
- 让缓存更高效。对于连接查询,如果其中一个表发生变化,那么整个查询缓存就无法使用。而分解后的多个查询,即使其中一个表发生变化,对其它表的查询缓存依然可以使用。
|
||||||
- 减少锁竞争;
|
- 减少锁竞争;
|
||||||
- 在应用层进行关联,可以更容易对数据库进行拆分,从而更容易做到高性能和可扩展。
|
- 在应用层进行连接,可以更容易对数据库进行拆分,从而更容易做到高性能和可扩展。
|
||||||
- 查询本身效率也可能会有所提升。例如下面的例子中,使用 IN() 代替关联查询,可以让 MySQL 按照 ID 顺序进行查询,这可能比随机的关联要更高效。
|
- 查询本身效率也可能会有所提升。例如下面的例子中,使用 IN() 代替连接查询,可以让 MySQL 按照 ID 顺序进行查询,这可能比随机的连接要更高效。
|
||||||
- 分解成多个单表查询,这些单表查询的缓存结果更可能被其它查询使用到,从而减少冗余记录的查询。
|
- 分解成多个单表查询,这些单表查询的缓存结果更可能被其它查询使用到,从而减少冗余记录的查询。
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
|
|
Loading…
Reference in New Issue
Block a user