首页 > 代码库 > Mysql-索引

Mysql-索引

MySQL索引原理

索引的目的在于提高查询效率,如字典,查询一个单词,需要定位到首字母,然后定位第二个字母,依次来查询。如果没有索引,就需要把字典中的单词看一遍,才能找到想要的单词。

1.b+树

技术分享

浅蓝色的块我们称之为一个磁盘块,可以看到每个磁盘块包含几个数据项(深蓝色所示)和指针(黄色所示),如磁盘块1包含数据项17和35,包含指针P1、P2、P3,P1表示小于17的磁盘块,P2表示在17和35之间的磁盘块,P3表示大于35的磁盘块。真实的数据存在于叶子节点即3、5、9、10、13、15、28、29、36、60、75、79、90、99。非叶子节点不存储真实的数据,只存储指引搜索方向的数据项,如17、35并不真实存在于数据表中。

2.b+树查找过程

如图所示,如果要查找数据项29,那么首先会把磁盘块1由磁盘加载到内存,此时发生一次IO,在内存中用二分查找确定29在17和35之间,锁定磁盘块1的P2指针,内存时间因为非常短(相比磁盘的IO)可以忽略不计,通过磁盘块1的P2指针的磁盘地址把磁盘块3由磁盘加载到内存,发生第二次IO,29在26和30之间,锁定磁盘块3的P2指针,通过指针加载磁盘块8到内存,发生第三次IO,同时内存中做二分查找找到29,结束查询,总计三次IO。真实的情况是,3层的b+树可以表示上百万的数据,如果上百万的数据查找只需要三次IO,性能提高将是巨大的,如果没有索引,每个数据项都要发生一次IO,那么总共需要百万次的IO,显然成本非常非常高。

MySQL中常见的索引

  • 普通索引
  • 唯一索引
  • 主键索引
  • 组合索引
  • 全文索引
  • 索引合并
  • 覆盖索引

1.普通索引

功能:加速查找

技术分享
CREATE TABLE tb2(
    nid INT NOT NULL auto_increment PRIMARY KEY,
    name VARCHAR(32) NOT NULL,
    email VARCHAR(64) NOT NULL,
    extra text,
    INDEX ix_name(name)
)ENGINE=INNODB DEFAULT charset=utf8;
创建表时创建索引
技术分享
CREATE INDEX index_name ON table_name(colunm_name)
仅创建索引
技术分享
DROP INDEX index_name ON table_name
删除索引
技术分享
SHOW INDEX FROM table_name
查看索引

注意:创建索引时,如果字段为BLOB和text类型,必须指定length。

2.唯一索引

功能:加速查询、唯一约束(可以为null)

技术分享
CREATE TABLE tb2(
    nid INT NOT NULL auto_increment PRIMARY KEY,
    name VARCHAR(32) NOT NULL,
    email VARCHAR(64) NOT NULL,
    extra text,
    UNIQUE ix_name(name)
)ENGINE=INNODB DEFAULT charset=utf8;
创建表时创建唯一索引
技术分享
CREATE UNIQUE index_name ON table_name(colunm_name)
创建唯一索引
技术分享
DROP UNIQUE index_name ON table_name
删除唯一索引

3.主键索引

功能:加速查询、唯一约束(不可以为null)

技术分享
CREATE TABLE tb2(
    nid INT NOT NULL auto_increment PRIMARY KEY,
    name VARCHAR(32) NOT NULL,
    email VARCHAR(64) NOT NULL,
    extra text
)ENGINE=INNODB DEFAULT charset=utf8;
创建表时设置主键

or

技术分享
CREATE TABLE tb2(
    nid INT NOT NULL auto_increment,
    name VARCHAR(32) NOT NULL,
    email VARCHAR(64) NOT NULL,
    extra text,
    PRIMARY KEY(nid)
)ENGINE=INNODB DEFAULT charset=utf8;
View Code

注:主键时一种特殊的索引,设置主键即创建了该字段的索引。

技术分享
ALTER TABLE table_name ADD PRIMARY KEY(colunm_name)
创建主键
技术分享
ALTER TABLE table_name DROP PRIMARY KEY
ALTER TABLE table_name MODIFY colunm_name type,DROP PRIMARY KEY
删除主键

4.组合索引

组合索引是指,将多个列组合成一个索引。

场景:频繁地同时使用多个列进行查询。

技术分享
CREATE INDEX ix_account_email ON user(account,email)
创建组合索引

组合索引最左匹配:

创建组合索引后,执行查询:

  • account和email     - 走索引
  • account       - 走索引
  • email                 - 不走索引
select * from user where account=oliver;     #走索引
select * from user where account=oliver and email=asdfaskl@163.com;   #走索引
select * from user where email=asdfaskl@163.com;   #不走索引

5.全文索引

 对文本的内容进行分词,进行搜索

6.索引合并

使用多个单列索引组合搜索。如下:

nid   name(单独索引)    email(单独索引)    pwd

 

select * from tb where name=alex
select * from tb where email=alex3714@163.com
        
select * from tb where name=alex or email=alex3714@163.com

 

 

7.覆盖索引

只需要从索引中查询就能得到结果,不必再去读取数据表。

例:

 

select * from tb where nid=1    # 先从索引表中找,再去数据表中找,非覆盖索引

 

select nid from tb where nid < 10   # 只需要从索引表中找,就能得到查询结果。 覆盖索引。

执行计划

用于显示sql执行信息,相对比较准确表达出当前SQL运行状况,用户可以参考执行信息对sql语句进行优化。

格式: explain SQL语句

mysql> explain select * from user where email=oliver@163.com;
+----+-------------+-------+------------+------+---------------+------+---------+------+--------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows   | filtered | Extra       |
+----+-------------+-------+------------+------+---------------+------+---------+------+--------+----------+-------------+
|  1 | SIMPLE      | user  | NULL       | ALL  | NULL          | NULL | NULL    | NULL | 261791 |    10.00 | Using where |
+----+-------------+-------+------------+------+---------------+------+---------+------+--------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

1.id:查询顺序标识

2.select_type:查询类型

  • SIMPLE        简单查询
  • PRIMARY          最外层查询
  • SUBQUERY      映射为子查询
  • DERIVED          子查询
  • UNION              联合
  • UNION RESULT       使用联合的结果
  • ...

3.table:查询的表

4.type:查询时的访问方式

查询时的访问方式,性能:all < index < range < index_merge < ref_or_null < ref < eq_ref < system/const

(1)ALL 全表扫描

遍历整个数据表,如果有limit时,找到结果后就不再往下找了。

(2)INDEX  全索引表扫描

(3)RANGE  对索引列进行范围查找

mysql> explain select uid from user where uid<10200;
+----+-------------+-------+------------+-------+---------------+---------+---------+------+-------+----------+--------------------------+
| id | select_type | table | partitions | type  | possible_keys | key     | key_len | ref  | rows  | filtered | Extra                    |
+----+-------------+-------+------------+-------+---------------+---------+---------+------+-------+----------+--------------------------+
|  1 | SIMPLE      | user  | NULL       | range | PRIMARY       | PRIMARY | 4       | NULL | 11622 |   100.00 | Using where; Using index |
+----+-------------+-------+------------+-------+---------------+---------+---------+------+-------+----------+--------------------------+
1 row in set, 1 warning (0.00 sec)

(4)INDEX_MERGE 合并索引,使用多个单列索引搜索

(5)REF   根据索引查找一个或多个值

(6)EQ_REF  连接表时,使用primary key 或 unique类型字段

(7)CONST   常量,表中最多有一个匹配行

mysql> explain select uid from user where uid=10200;
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------------+
| id | select_type | table | partitions | type  | possible_keys | key     | key_len | ref   | rows | filtered | Extra       |
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------------+
|  1 | SIMPLE      | user  | NULL       | const | PRIMARY       | PRIMARY | 4       | const |    1 |   100.00 | Using index |
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

(8)SYSTEM    系统

表仅有一行(=系统表)。这是const联接类型的一个特例。

select * from (select nid from tb1 where nid = 1) as A;

5.possible_keys

可能使用的索引

6.key

真实使用的索引

7.key_len

MySQL中使用索引字节长度

8.rows

mysql估计为了找到所需的行而要读取的行数 ------ 只是预估值

9.Extra   

该列包含MySQL解决查询的详细信息

使用索引

要使用正确的方式命中索引。如果方式错误,即使建立了索引也无法命中。

- like %xx
    select * from tb1 where name like %cn;
- 使用函数
    select * from tb1 where reverse(name) = wupeiqi;
- or
    select * from tb1 where nid = 1 or email = seven@live.com;  # nid有索引,email未创建索引,则不能命中索引
    特别的:当or条件中有未建立索引的列才失效,以下会走索引
            select * from tb1 where nid = 1 or name = seven;   #nid和name都创建了索引,能命中索引
            select * from tb1 where nid = 1 or email = seven@live.com and name = alex
- 类型不一致
    如果列是字符串类型,传入条件是必须用引号引起来,不然...
    select * from tb1 where name = 999;
- !=
    select * from tb1 where name != alex
    特别的:如果是主键,则还是会走索引
        select * from tb1 where nid != 123
- >
    select * from tb1 where name > alex
    特别的:如果是主键或索引是整数类型,则还是会走索引
        select * from tb1 where nid > 123
        select * from tb1 where num > 123
- order by
    select email from tb1 order by name desc;
    当没有where条件时,order by主键则走索引,有where时,order by索引字段则走索引。
    特别的:如果对主键排序,则还是走索引:
        select * from tb1 order by nid desc;
 
- 组合索引最左前缀
    如果组合索引为:(name,email)
    name and email       -- 使用索引
    name                 -- 使用索引
    email                -- 不使用索引

注意事项

避免使用select *
- count(1)或count(列) 代替 count(*)
- 创建表时尽量时 char 代替 varchar
- 表的字段顺序固定长度的字段优先
- 组合索引代替多个单列索引(经常使用多个条件查询时)
- 尽量使用短索引
- 使用连接(JOIN)来代替子查询(Sub-Queries)
- 连表时注意条件类型需一致
- 索引散列值(重复少)不适合建索引,例:性别不适合

慢日志查询

1.配置MySQL自动配置慢日志

slow_query_log = OFF                            是否开启慢日志记录
long_query_time = 2                              时间限制,超过此时间,则记录
slow_query_log_file = /usr/slow.log        日志文件
log_queries_not_using_indexes = OFF     为使用索引的搜索是否记录

注:查看当前配置信息:
       show variables like ‘%query%‘
     修改当前配置:
    set global 变量名 = 值

 

2.查看MySQL慢日志

mysqldumpslow -s at -a  /usr/local/var/mysql/MacBook-Pro-3-slow.log
"""
--verbose    版本
--debug      调试
--help       帮助
 
-v           版本
-d           调试模式
-s ORDER     排序方式
             what to sort by (al, at, ar, c, l, r, t), at is default
              al: average lock time
              ar: average rows sent
              at: average query time
               c: count
               l: lock time
               r: rows sent
               t: query time
-r           反转顺序,默认文件倒序拍。reverse the sort order (largest last instead of first)
-t NUM       显示前N条just show the top n queries
-a           不要将SQL中数字转换成N,字符串转换成S。dont abstract all numbers to N and strings to S-n NUM       abstract numbers with at least n digits within names
-g PATTERN   正则匹配;grep: only consider stmts that include this string
-h HOSTNAME  mysql机器名或者IP;hostname of db server for *-slow.log filename (can be wildcard),
             default is *, i.e. match all
-i NAME      name of server instance (if using mysql.server startup script)
-l           总时间中不减去锁定时间;dont subtract lock time from total time
"""

 

 

 

 

 

 

 

 

 

 

 

 

   

 

Mysql-索引