第8章 索引index
索引是在数据库表的字段上添加的,是为了提高查询效率存在的一种机制。
一张表的一个字段可以添加一个索引,当然,多个字段联合起来也可以添加索引。
索引相当于一本书的目录,是为了缩小扫描范围而存在的一种机制。
对于一本字典来说,查找某个汉字有两种方式:
第一种方式:一页一页挨着找,直到找到为止;即全字典扫描,效率低
第二种方式:先通过目录(索引)去定位一个大概的位置,然后直接定位到这个
位置,做局域性扫描,缩小扫描的范围,快速查找
t_user
id(idIndex) name(nameIndex) email(emailIndex) address (emailAddressIndex) ---------------------------------------------------------------------------------- 1 zhangsan... 2 lisi 3 wangwu 4 zhaoliu 5 hanmeimei 6 jack
select * from t_user where name='jack';
以上的这条SQL语句会去name字段上扫描,因为查询条件是:name=‘jack’
如果name字段上没有添加索引(目录),或者说没有给name字段创建索引,MySQL会进行全扫描,会将name字段上的每一个值都比对一遍。效率比较低。
8.1 查询两种方式
- 全表扫描
- 根据索引检索
注意:
在实际中,汉语字典前面的目录是排序的,按照a b c d e f…排序,
为什么排序呢?因为只有排序了才会有区间查找这一说!(缩小扫描范围其实就是扫描某个区间罢了!)
在mysql数据库当中索引也是需要排序的,并且这个索引的排序和TreeSet
数据结构相同。TreeSet(TreeMap)底层是一个自平衡的二叉树!在mysql当中索引是一个B-Tree数据结构。
遵循左小右大原则存放。采用中序遍历方式遍历取数据。
8.2 索引的实现原理
假设有一张用户表:t_user
id(PK) name 每一行记录在硬盘上都有物理存储编号 ---------------------------------------------------------------------------------- 100 zhangsan 0x1111 120 lisi 0x2222 99 wangwu 0x8888 88 zhaoliu 0x9999 101 jack 0x6666 55 lucy 0x5555 130 tom 0x7777
提醒1:任何数据库中主键上都会自动创建索引,所以id字段上自动有索引。 另外,一个字段如果有unique约束,也会自动创建索引。
提醒2:在任何数据库当中,任何一张表的任何一条记录在硬盘存储上都有一个硬盘的物理存储编号。
提醒3:在mysql当中,索引是一个单独的对象,不同的存储引擎以不同的形式存在,
在MyISAM存储引擎中,索引存储在一个.MYI文件中;
在InnoDB存储引擎中索引存储在一个逻辑名称叫做tablespace的当中;
在MEMORY存储引擎当中索引被存储在内存当中。
不管索引存储在哪里,索引在mysql当中都是一个树的形式存在。(自平衡二叉树:B-Tree)
什么时候考虑给字段添加索引?
- 数据量庞大
- 该字段经常出现在where的后面,以条件的形式存在;就是说该字段频繁被扫描。
- 该字段很少的DML(insert delete update)操作;就是说增删改少
8.3 索引的创建、删除
创建索引:create index emp_ename_index on emp(ename);
例:给emp表的ename字段添加索引,起名:emp_ename_index
mysql> create index emp_ename_index on emp(ename); Query OK, 0 rows affected (0.02 sec) Records: 0 Duplicates: 0 Warnings: 0
删除索引:drop index emp_ename_index on emp;
mysql> drop index emp_ename_index on emp; Query OK, 0 rows affected (0.02 sec) Records: 0 Duplicates: 0 Warnings: 0
8.4 查看是否使用索引检索:explain
mysql> explain select * from emp where ename='KING'; +----+-------------+-------+------+---------------+------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+------+---------------+------+---------+------+------+-------------+ | 1 | SIMPLE | emp | ALL | NULL | NULL | NULL | NULL | 14 | Using where | +----+-------------+-------+------+---------------+------+---------+------+------+-------------+ 1 row in set (0.00 sec)
扫描14条记录:说明没有使用索引。type=ALL
例:建立索引扫描:
mysql> create index emp_ename_index on emp(ename); Query OK, 0 rows affected (0.01 sec) Records: 0 Duplicates: 0 Warnings: 0 mysql> explain select * from emp where ename='KING'; +----+-------------+-------+------+-----------------+-----------------+---------+-------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+------+-----------------+-----------------+---------+-------+------+-------------+ | 1 | SIMPLE | emp | ref | emp_ename_index | emp_ename_index | 33 | const | 1 | Using where | +----+-------------+-------+------+-----------------+-----------------+---------+-------+------+-------------+ 1 row in set (0.01 sec)
8.5 索引失效情况
第1种:模糊匹配%开头情况
mysql> select * from emp where ename like '%T'; +-------+-------+---------+------+------------+---------+------+--------+ | EMPNO | ENAME | JOB | MGR | HIREDATE | SAL | COMM | DEPTNO | +-------+-------+---------+------+------------+---------+------+--------+ | 7788 | SCOTT | ANALYST | 7566 | 1987-04-19 | 3000.00 | NULL | 20 | +-------+-------+---------+------+------------+---------+------+--------+ 1 row in set (0.00 sec) mysql> explain select * from emp where ename like '%T'; +----+-------------+-------+------+---------------+------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+------+---------------+------+---------+------+------+-------------+ | 1 | SIMPLE | emp | ALL | NULL | NULL | NULL | NULL | 14 | Using where | +----+-------------+-------+------+---------------+------+---------+------+------+-------------+ 1 row in set (0.00 sec)
原因:模糊匹配以“%”开头了!
第2种:使用or情况
or两边的条件字段都要有索引,才会走索引;
mysql> explain select * from emp where ename = 'KING' or job = 'MANAGER'; //job字段没有建立索引 +----+-------------+-------+------+-----------------+------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+------+-----------------+------+---------+------+------+-------------+ | 1 | SIMPLE | emp | ALL | emp_ename_index | NULL | NULL | NULL | 14 | Using where | +----+-------------+-------+------+-----------------+------+---------+------+------+-------------+ 1 row in set (0.00 sec)
第3种:符合索引情况:
复合索引:两个字段,或者更多的字段联合起来添加一个索引。
使用复合索引时,若没有使用左侧的列查找,索引失效。最左原则
mysql> create index emp_job_sal_index on emp(job,sal); //建立复合索引 左边字段是job Query OK, 0 rows affected (0.02 sec) Records: 0 Duplicates: 0 Warnings: 0 mysql> explain select * from emp where job='MANAGER'; //最左原则 左边行 +----+-------------+-------+------+-------------------+-------------------+---------+-------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+------+-------------------+-------------------+---------+-------+------+-------------+ | 1 | SIMPLE | emp | ref | emp_job_sal_index | emp_job_sal_index | 30 | const | 3 | Using where | +----+-------------+-------+------+-------------------+-------------------+---------+-------+------+-------------+ 1 row in set (0.00 sec) mysql> explain select * from emp where sal=800; //右边不行 +----+-------------+-------+------+---------------+------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+------+---------------+------+---------+------+------+-------------+ | 1 | SIMPLE | emp | ALL | NULL | NULL | NULL | NULL | 14 | Using where | +----+-------------+-------+------+---------------+------+---------+------+------+-------------+ 1 row in set (0.01 sec)
第4种:where当中索引列参加运算的情况:
mysql> create index emp_sal_index on emp(sal); //建立索引 Query OK, 0 rows affected (0.02 sec) Records: 0 Duplicates: 0 Warnings: 0 mysql> explain select * from emp where sal=800; //正常情况没问题 +----+-------------+-------+------+---------------+---------------+---------+-------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+------+---------------+---------------+---------+-------+------+-------------+ | 1 | SIMPLE | emp | ref | emp_sal_index | emp_sal_index | 9 | const | 1 | Using where | +----+-------------+-------+------+---------------+---------------+---------+-------+------+-------------+ 1 row in set (0.00 sec) mysql> explain select * from emp where sal+1 = 800; //参加运算索引失效 +----+-------------+-------+------+---------------+------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+------+---------------+------+---------+------+------+-------------+ | 1 | SIMPLE | emp | ALL | NULL | NULL | NULL | NULL | 14 | Using where | +----+-------------+-------+------+---------------+------+---------+------+------+-------------+ 1 row in set (0.01 sec)
第5种:where当中索引列使用了函数的情况:
mysql> explain select * from emp where lower(ename) = 'smith'; +----+-------------+-------+------+---------------+------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+------+---------------+------+---------+------+------+-------------+ | 1 | SIMPLE | emp | ALL | NULL | NULL | NULL | NULL | 14 | Using where | +----+-------------+-------+------+---------------+------+---------+------+------+-------------+ 1 row in set (0.00 sec)
8.6 索引分类
- 单一索引:一个字段上添加索引
- 复合索引:两个字段或者更多的字段上添加索引
- 主键索引:主键上添加索引
- 唯一性索引:具有unique约束的字段上添加索引
…
注意:唯一性比较弱的字段上添加索引用处不大。