diff --git a/notes/MySQL_EXPLAIN.md b/notes/MySQL_EXPLAIN.md
index 8c470b7..8d0101e 100644
--- a/notes/MySQL_EXPLAIN.md
+++ b/notes/MySQL_EXPLAIN.md
@@ -3,93 +3,209 @@
EXPLAIN 关键字可以用于获取所修饰语句执行计划的相关信息,在 MySQL 8.0 中,EXPLAIN 支持大多数常见的语句,如 SELECT 、DELETE 、INSERT 、REPLACE、和 UPDATE 语句。示例如下:
```sql
--- 查询工资大于100000的雇员所在部门的编号
-EXPLAIN SELECT * FROM dept_emp WHERE emp_no IN (SELECT emp_no FROM salaries WHERE salary > 100000) LIMIT 100;
+mysql> EXPLAIN SELECT * FROM employees;
++----+-------------+-----------+------+---------------+------+---------+------+--------+-------+
+| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
++----+-------------+-----------+------+---------------+------+---------+------+--------+-------+
+| 1 | SIMPLE | employees | ALL | NULL | NULL | NULL | NULL | 299379 | NULL |
++----+-------------+-----------+------+---------------+------+---------+------+--------+-------+
```
-
+注:本篇文章所有的测试数据来均源于 MySQL 官方提供的 [Employees Sample Database](https://dev.mysql.com/doc/employee/en/),其数据库结构如下:
+
+
+以下对 EXPLAIN 输出结果中各个字段的含义及其可能的取值范围做详细的介绍:
+
+## 1. id
+
+ id 字段为行标识符,同时也表示该行语句执行的优先级,值越大则优先级越高。如果某行语句引用了其他行结果集的并集,则该值可以为 NULL。示例如下:
```sql
-
-mysql> EXPLAIN SELECT * FROM dept_emp WHERE emp_no IN (SELECT emp_no FROM salaries WHERE salary > 100000) LIMIT 100;
-+----+-------------+----------+------+---------------+---------+---------+---------------------------+--------+-----------------------------------+
-| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
-+----+-------------+----------+------+---------------+---------+---------+---------------------------+--------+-----------------------------------+
-| 1 | SIMPLE | dept_emp | ALL | PRIMARY | NULL | NULL | NULL | 331570 | NULL |
-| 1 | SIMPLE | salaries | ref | PRIMARY | PRIMARY | 4 | employees.dept_emp.emp_no | 4 | Using where; FirstMatch(dept_emp) |
-+----+-------------+----------+------+---------------+---------+---------+---------------------------+--------+-----------------------------------+
-```
-
-> 注:本篇文章测试数据来源于 MySQL 官方提供的 [Employees Sample Database](https://dev.mysql.com/doc/employee/en/)
-
-### 1. id
-
-每个 SELECT 语句都会对应输出结果中的一行数据,id 列是该行数据的编号,如果在语句当中没有子查询或者联合查询,则每行数据的编号都是 1 。如果涉及到 FROM 子查询 或者 UNION 查询,则编号通常会按照其在原始语句中的位置排序。示例如下:
-
-```sql
--- 该FROM字句只是用于演示
-mysql> EXPLAIN SELECT COUNT(1) FROM (SELECT emp_no FROM salaries) AS s;
-+----+-------------+------------+-------+
-| id | select_type | table | type |
-+----+-------------+------------+-------+
-| 1 | PRIMARY | | ALL |
-| 2 | DERIVED | salaries | index |
-+----+-------------+------------+-------+ ......
+-- 该FROM字句只是用于演示,并没有任何实际意义
+mysql> EXPLAIN SELECT COUNT(1) FROM (SELECT emp_no FROM salaries) AS t;
++----+-------------+------------+-------+---------------+---------+---------+------+---------+-------------+
+| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
++----+-------------+------------+-------+---------------+---------+---------+------+---------+-------------+
+| 1 | PRIMARY | | ALL | NULL | NULL | NULL | NULL | 2757635 | NULL |
+| 2 | DERIVED | salaries | index | NULL | PRIMARY | 7 | NULL | 2757635 | Using index |
++----+-------------+------------+-------+---------------+---------+---------+------+---------+-------------+
```
```sql
-- 查询工资大于500000或部门编号等于d007的所有雇员的编号
-mysql> EXPLAIN SELECT emp_no FROM salaries WHERE salary>500000 UNION ALL SELECT emp_no FROM dept_emp WHERE dept_no = "d007";
-+----+--------------+------------+------+
-| id | select_type | table | type |
-+----+--------------+------------+------+
-| 1 | PRIMARY | salaries | ALL |
-| 2 | UNION | dept_emp | ref |
-| NULL | UNION RESULT | | ALL |
-+----+--------------+------------+------+ ......
+mysql> EXPLAIN SELECT emp_no FROM salaries WHERE salary>500000 UNION ALL SELECT emp_no FROM dept_emp WHERE dept_no = "d007";
++----+--------------+------------+------+---------------+---------+---------+-------+---------+--------------------------+
+| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
++----+--------------+------------+------+---------------+---------+---------+-------+---------+--------------------------+
+| 1 | PRIMARY | salaries | ALL | NULL | NULL | NULL | NULL | 2837161 | Using where |
+| 2 | UNION | dept_emp | ref | dept_no | dept_no | 4 | const | 91566 | Using where; Using index |
+| NULL | UNION RESULT | | ALL | NULL | NULL | NULL | NULL | NULL | Using temporary |
++----+--------------+------------+------+---------------+---------+---------+-------+---------+--------------------------+
```
-### 2. select_type
+## 2. select_type
-### 3. table
++ **SIMPLE**:不包含子查询或者 UNION 操作的查询;
++ **PRIMARY**:查询中如果包含任何子查询,那么最外层的查询则被标记为 PRIMARY ;
++ **SUBQUERY**:子查询中第一个 SELECT ;
++ **DEPENDENT SUBQUERY**:子查询中的第一个 SELECT,取决于外部查询;
++ **UNION**:UNION 操作的第二个或者之后的查询;
++ **DEPENDENT UNION**:UNION 操作的第二个或者之后的查询,取决于外部查询;
++ **UNION RESULT**:UNION 产生的结果集;
++ **DERIVED**:出现在 FROM 字句中的子查询。
+
+这里常用的类型在上面的示例中都出现过,下面演示查询类型为 SUBQUERY 的情况,示例如下:
+
+```sql
+-- 根据员工编号查询员工姓名及其工资总和
+mysql> explain select first_name,(select sum(salary) from salaries where emp_no = 10001) from employees where emp_no = 10001;
++----+-------------+-----------+-------+---------------+---------+---------+-------+------+-------+
+| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
++----+-------------+-----------+-------+---------------+---------+---------+-------+------+-------+
+| 1 | PRIMARY | employees | const | PRIMARY | PRIMARY | 4 | const | 1 | NULL |
+| 2 | SUBQUERY | salaries | ref | PRIMARY | PRIMARY | 4 | const | 17 | NULL |
++----+-------------+-----------+-------+---------------+---------+---------+-------+------+-------+
+```
+
+## 3. table
- ****:输出结果中编号为 M 的行与编号为 N 的行的结果集的并集。
-- ****:输出结果中编号为 N 的行的结果集,使用 derived 修饰表示这是一个派生结果集,如来自于 FROM 子句中的子查询 。
+- ****:输出结果中编号为 N 的行的结果集,使用 derived 修饰表示这是一个派生结果集,如来自于 FROM 子句中的查询 。
- ****:输出结果中编号为 N 的行的结果集,使用 subquery 修饰表示其来源于一个物化子查询。
-### 4. type
+## 4. type
-这个字段表示 MySQL 如何查找表中的行。常见参数按照查找性能由低到高,排序如下:
+这个字段非常重要,它表示 MySQL 使用何种方式来查找目标数据,不同查找方式会导致不同的性能开销,常见查找方式及其性能表现按照由高到低的顺序排序如下:
-+ ALL:全表扫描;
-+ index:按照索引顺序而不是行顺序进行全表扫描;
-+ range:范围扫描;
-+ ref:
+**1. system**:这是 const 类型的一个特例,只会出现在待查询的表只有一行数据的情况下。
-### 5. possible_keys
+**2. const**:出现在主键或唯一索引与常量值进行比较的场景下,这种情况下查询性能是最优的。
+
+```sql
+mysql> explain select * from employees where emp_no = 10008;
++----+-------------+-----------+-------+---------------+---------+---------+-------+------+-------+
+| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
++----+-------------+-----------+-------+---------------+---------+---------+-------+------+-------+
+| 1 | SIMPLE | employees | const | PRIMARY | PRIMARY | 4 | const | 1 | NULL |
++----+-------------+-----------+-------+---------------+---------+---------+-------+------+-------+
+```
+
+**3. eq_ref**:在连接查询中,当连接使用索引的所有部分并且索引是 PRIMARY KEY 或 UNIQUE NOT NULL 索引时使用它。
+
+```sql
+-- 这里员工部门关系表 dept_no 的联合主键为 emp_no + dept_no ,即员工编号+部门标号
+mysql> explain select * from employees e,dept_emp d where e.emp_no = d.emp_no and dept_no = "d005";
++----+-------------+-------+--------+-----------------+---------+---------+--------------------+------+-----------------------+
+| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
++----+-------------+-------+--------+-----------------+---------+---------+--------------------+------+-----------------------+
+| 1 | SIMPLE | d | ref | PRIMARY,dept_no | dept_no | 4 | const | 1 | Using index condition |
+| 1 | SIMPLE | e | eq_ref | PRIMARY | PRIMARY | 4 | employees.d.emp_no | 1 | NULL |
++----+-------------+-------+--------+-----------------+---------+---------+--------------------+------+-----------------------+
+```
+
+**4. ref**:通常在连接查询中,如果连接仅使用了前缀索引或用于连接的条件不是 PRIMARY KEY 或 UNIQUE 索引时使用它。
+
+```sql
+-- 这里仅使用了前缀索引emp_no,所以其类型为 ref , 而不是 eq_ref
+mysql> explain select * from employees e,dept_emp d where e.emp_no = d.emp_no;
++----+-------------+-------+------+---------------+---------+---------+--------------------+--------+-------+
+| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
++----+-------------+-------+------+---------------+---------+---------+--------------------+--------+-------+
+| 1 | SIMPLE | e | ALL | PRIMARY | NULL | NULL | NULL | 299379 | NULL |
+| 1 | SIMPLE | d | ref | PRIMARY | PRIMARY | 4 | employees.e.emp_no | 1 | NULL |
++----+-------------+-------+------+---------------+---------+---------+--------------------+--------+-------+
+```
+
+**5. ref_or_null**:类似于 ref 类型的查询,但是附加了对 NULL 值列的查询。这里给出示例语句:
+
+```sql
+SELECT * FROM ref_table WHERE key_column=expr OR key_column IS NULL;
+```
+
+**6. index_merge**:该联接类型表示使用了索引进行合并优化,示例如下:
+
+```
+mysql> explain select * from dept_emp where dept_no = "d004" and emp_no < 10020;
++----+-------------+----------+-------------+-----------------+-----------------+---------+------+------+-----------------------------------------------+
+| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
++----+-------------+----------+-------------+-----------------+-----------------+---------+------+------+-----------------------------------------------+
+| 1 | SIMPLE | dept_emp | index_merge | PRIMARY,dept_no | dept_no,PRIMARY | 8,4 | NULL | 1 | Using intersect(dept_no,PRIMARY); Using where |
++----+-------------+----------+-------------+-----------------+-----------------+---------+------+------+-----------------------------------------------+
+```
+
+**7. range**:使用索引进行范围扫描,常见于 between、> 、< 这样的查询条件。
+
+```sql
+mysql> explain select * from employees where emp_no > 10000;
++----+-------------+-----------+-------+---------------+---------+---------+------+--------+-------------+
+| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
++----+-------------+-----------+-------+---------------+---------+---------+------+--------+-------------+
+| 1 | SIMPLE | employees | range | PRIMARY | PRIMARY | 4 | NULL | 149689 | Using where |
++----+-------------+-----------+-------+---------------+---------+---------+------+--------+-------------+
+```
+
+**8. index**:索引连接类型与 ALL 相同,只是扫描的是索引树,通常出现在索引是该查询的覆盖索引的情况:
+
+```sql
+mysql> explain select emp_no from employees;
++----+-------------+-----------+-------+---------------+---------+---------+------+--------+-------------+
+| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
++----+-------------+-----------+-------+---------------+---------+---------+------+--------+-------------+
+| 1 | SIMPLE | employees | index | NULL | PRIMARY | 4 | NULL | 299379 | Using index |
++----+-------------+-----------+-------+---------------+---------+---------+------+--------+-------------+
+```
+
+**9. ALL**:全表扫描,效率最差的查找方式。
+
+```sql
+mysql> explain select * from employees where first_name ="Bezalel";
++----+-------------+-----------+------+---------------+------+---------+------+--------+-------------+
+| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
++----+-------------+-----------+------+---------------+------+---------+------+--------+-------------+
+| 1 | SIMPLE | employees | ALL | NULL | NULL | NULL | NULL | 299379 | Using where |
++----+-------------+-----------+------+---------------+------+---------+------+--------+-------------+
+```
+
+## 5. possible_keys
表示在执行过程中可能会用到哪些索引来进行优化。
-### 6. key
+## 6. key
表示在执行过程中实际用到的用于优化的索引。
-### 7. key_len
+## 7. key_len
表示使用到的索引的字节数。
-### 8. ref
+## 8. ref
+显示哪些列或常量与 key 列中指定的索引进行比较。
-
-### 9. rows
+## 9. rows
MySQL 为了找到目标行而读取的所有行的数量,这是一个估算的值。
-### 10. Extra
+## 10. Extra
-用于显示额外的信息,常用
+用于显示额外的信息,常见参数如下:
-### 11. partitions
++ **Distinct**:优化 distinct 操作,再找到第一匹配的元组后即停止找同样值的动作;
++ **Not exists**:使用 not exists 来优化查询;
++ **Using filesort**:使用额外操作进行排序,通常会出现在 order by 或 group by 查询中;
++ **Using index**:使用了覆盖索引进行查询;
++ **Using temporary**:使用临时表来处理查询,常见于排序、子查询和分组查询;
++ **Using where**:使用 WHERE 条件来过滤数据;
++ **select tables optimized away**:直接通过索引来获取数据,不需要访问表。
-显示查询结果集中记录所在的分区。如果目标表不是分区表,则值为 null 。
\ No newline at end of file
+## 11. partitions
+
+显示查询结果集中记录所在的分区。如果目标表不是分区表,则值为 null 。
+
+
+
+
+
+## 参考资料
+
+MySQL官方文档:https://dev.mysql.com/doc/refman/8.0/en/explain-output.html
\ No newline at end of file
diff --git a/notes/MySQL_索引.md b/notes/MySQL_索引.md
new file mode 100644
index 0000000..e6c632d
--- /dev/null
+++ b/notes/MySQL_索引.md
@@ -0,0 +1,51 @@
+## B+ tree 索引
+
+B-Tree 适用于以下类型的查找:
+
++ **全值匹配**:以索引为条件进行精确查找。如 `emp_no` 字段为索引,查询条件为 `emp_no = 10008`;
++ **前缀匹配**:以联合索引的前缀为查找条件。如 `emp_no` 和 `dept_no` 为联合索引,查找条件为 `emp_no = 10008`;
++ **列前缀匹配**:匹配索引列的值的开头部分。如 `dept_no` 为索引,查询条件为 `dept_no like "d1%"`;
++ **匹配范围值**:按照索引列匹配一定范围内的值。如 `emp_no` 字段为索引,查询条件为 `emp_no > 10008`;
++ **只访问索引的查询**:如 `emp_no` 字段为索引,查询语句为 `select emp_no from employees`;
++ **精确匹配某一列并范围匹配某一列**:如 `emp_no` 和 `dept_no` 为联合索引,查找条件为 `dept_no = "d004" and emp_no < 10020`。
+
+## 哈希索引
+
+使用哈希索引时,存储引擎会对索引列的值进行哈希运算,并将计算出的哈希值和指向该行数据的指针存储在索引中,因此它更适用于等值比较查询,而不是范围查询。在建立哈希索引时,需要选取选择性比较高的列,即列上的数据不容易重复 (如身份证号),这样可以尽量避免哈希冲突。因为哈希索引并不需要存储索引列的数据,所以其结构比较紧凑,对应的查询速度也比较快。
+
+InnoDB 引擎有一个名为 “自适应哈希索引 (adaptive hash index)” 的功能,当某些索引值被频繁使用时,它会在内存中基于 B-Tree 索引在创建一个哈希索引,从而让 B-Tree 索引具备哈希索引的优点,如快速查找。
+
+## 索引的优点
+
++ 索引极大减少了服务器需要扫描的数据量;
++ 索引可以帮助服务器避免排序和临时表;
++ 索引可以将随机 IO;
+
+## 索引的创建与使用策略
+
+在查询时,应该避免在索引列上使用函数或者表达式;
+
+对于多列索引,应该按照使用频率由高到低的顺序建立联合索引;
+
+建立索引时,应该考虑查询时候的排序和分组的需求。只有当索引列的顺序和 ORDER BY 字句的顺序完全一致,并且遵循同样的升序或降序规则时候,MySQL 才会使用索引来对结果做排序。
+
+尽量避免创建冗余的索引。通常会出现以下两种情况的冗余:
+
+如已经存在索引 (A,B),接着又创建了索引 A,此时会出现冗余,因为索引 A 只是索引 (A,B) 的前缀索引;
+
+如为主键列再次创建索引,因为主键已经是唯一索引了,此时再创建就会出现冗余。
+
+
+
+## 参考资料
+
+1. [ InnoDB 数据页解析](http://mysql.taobao.org/monthly/2018/04/03/)
+
+2. [MySQL索引背后的数据结构及算法原理](https://blog.codinglabs.org/articles/theory-of-mysql-index.html)
+
+
+
+
+
+
+
diff --git a/pictures/employees-schema.png b/pictures/employees-schema.png
new file mode 100644
index 0000000..b7ea054
Binary files /dev/null and b/pictures/employees-schema.png differ