mysql 索引

基本介绍

MySQL索引的建立对于MySQL的高效运行是很重要的,索引可以大大提高MySQL的检索速度
索引的价值,在于提高一个海量表的检索速度,索引的算法有btree 二叉树的算法,还有一种就是hash算法

索引的分类

主键索引 (primary key),唯一索引(unique),普通索引(index),全文索引(fulltext)

创建一个海量表

//部门表
mysql> CREATE TABLE dept( /*部门表*/
    -> deptno MEDIUMINT   UNSIGNED  NOT NULL  DEFAULT 0,
    -> dname VARCHAR(20)  NOT NULL  DEFAULT "",
    -> loc VARCHAR(13) NOT NULL DEFAULT ""
    -> ) ENGINE=MyISAM DEFAULT CHARSET=utf8 ;
Query OK, 0 rows affected (0.06 sec)

//员工表
mysql> CREATE TABLE emp
    -> (empno  MEDIUMINT UNSIGNED  NOT NULL  DEFAULT 0, /*编号*/
    -> ename VARCHAR(20) NOT NULL DEFAULT "", /*名字*/
    -> job VARCHAR(9) NOT NULL DEFAULT "",/*工作*/
    -> mgr MEDIUMINT UNSIGNED NOT NULL DEFAULT 0,/*上级编号*/
    -> hiredate DATE NOT NULL,/*入职时间*/
    -> sal DECIMAL(7,2)  NOT NULL,/*薪水*/
    -> comm DECIMAL(7,2) NOT NULL,/*红利*/
    -> deptno MEDIUMINT UNSIGNED NOT NULL DEFAULT 0 /*部门编号*/
    -> )ENGINE=MyISAM DEFAULT CHARSET=utf8 ;
Query OK, 0 rows affected (0.06 sec)

//工资级别表
mysql> CREATE TABLE salgrade(
    -> grade MEDIUMINT UNSIGNED NOT NULL DEFAULT 0,
    -> losal DECIMAL(17,2)  NOT NULL,
    -> hisal DECIMAL(17,2)  NOT NULL
    -> )ENGINE=MyISAM DEFAULT CHARSET=utf8;
Query OK, 0 rows affected (0.04 sec)

//插入数据
INSERT INTO salgrade VALUES (1,700,1200);
INSERT INTO salgrade VALUES (2,1201,1400);
INSERT INTO salgrade VALUES (3,1401,2000);
INSERT INTO salgrade VALUES (4,2001,3000);
INSERT INTO salgrade VALUES (5,3001,9999);

//定义一个新的命令结束符 防止存储过程冲突
delimiter $$

//创建一个函数,名字 rand_string,可以随机返回我指定的个数字符串
mysql> create function rand_string(n INT)
    -> returns varchar(255) #该函数会返回一个字符串
    -> begin
    -> #定义了一个变量 chars_str, 类型  varchar(100)
    -> #默认给 chars_str 初始值   'abcdefghijklmnopqrstuvwxyzABCDEFJHIJKLMNOPQRSTUVWXYZ'
    -> declare chars_str varchar(100) default 'abcdefghijklmnopqrstuvwxyzABCDEFJHIJKLMNOPQRSTUVWXYZ';
    -> declare return_str varchar(255) default '';
    -> declare i int default 0;
    -> while i < n do
    -> # concat 函数 : 连接函数mysql函数
    -> set return_str =concat(return_str,substring(chars_str,floor(1+rand()*52),1));
    -> set i = i + 1;
    -> end while;
    -> return return_str;
    -> end $$
Query OK, 0 rows affected (0.05 sec)

//这里我们又自定了一个函数,返回一个随机的部门号
mysql> create function rand_num( )
    -> returns int(5)
    -> begin
    -> declare i int default 0;
    -> set i = floor(10+rand()*500);
    -> return i;
    -> end $$
Query OK, 0 rows affected (0.00 sec)

//创建一个存储过程, 可以添加雇员
mysql> create procedure insert_emp(in start int(10),in max_num int(10))
    -> begin
    -> declare i int default 0;
    -> #set autocommit =0 把autocommit设置成0
    -> #autocommit = 0 含义: 不要自动提交
    -> set autocommit = 0; #默认不提交sql语句
    -> repeat
    -> set i = i + 1;
    -> #通过前面写的函数随机产生字符串和部门编号,然后加入到emp表
    -> insert into emp values ((start+i) ,rand_string(6),'SALESMAN',0001,curdate(),2000,400,rand_num());
    -> until i = max_num
    -> end repeat;
    -> #commit整体提交所有sql语句,提高效率
    -> commit;
    -> end $$
Query OK, 0 rows affected (0.00 sec)

//添加8000000数据
mysql> call insert_emp(100001,8000000)$$
Query OK, 0 rows affected (5 min 19.60 sec)

mysql> delimiter ;

//从800w的数据中查询
//本地操作且一个人查询需要的1.15sec已经非常慢了,如果一个网站1w人同时操作 会挂的
mysql> select * from emp where empno=3189732;
+---------+--------+----------+-----+------------+---------+--------+--------+
| empno   | ename  | job      | mgr | hiredate   | sal     | comm   | deptno |
+---------+--------+----------+-----+------------+---------+--------+--------+
| 3189732 | OEFnxA | SALESMAN |   1 | 2020-11-21 | 2000.00 | 400.00 |    124 |
+---------+--------+----------+-----+------------+---------+--------+--------+
1 row in set (1.15 sec)

问题和解决方案

上表查询速度太慢,需要创建索引解决,但是应该创建主键索引,唯一索引 还是 普通索引呢,这个需要看情况,上表empno是随机创建的,可能重复,所以只能创建普通索引。

添加普通索引

//给emp表的empno添加索引
mysql> alter table emp add index(empno);
Query OK, 8000000 rows affected (8.45 sec)
Records: 8000000  Duplicates: 0  Warnings: 0

引起的文件变化

emp.MYI是索引文件,从1k变成了71m

在这里插入图片描述
测试效果

//效果显著
mysql> select * from emp where empno=7237892;
+---------+--------+----------+-----+------------+---------+--------+--------+
| empno   | ename  | job      | mgr | hiredate   | sal     | comm   | deptno |
+---------+--------+----------+-----+------------+---------+--------+--------+
| 7237892 | UDgWoJ | SALESMAN |   1 | 2020-11-21 | 2000.00 | 400.00 |    150 |
+---------+--------+----------+-----+------------+---------+--------+--------+
1 row in set (0.00 sec)

索引的原理

没有索引
在这里插入图片描述
添加索引 二叉树
在这里插入图片描述

索引的优势和代价

  • 二叉树9条数据 只要比对4次 2^4,数据量小看不出其强悍之处, 1073741824条数据只需要比对30次 2^30 = 1073741824
  • 执行dml操作时,同时也要自动维护二叉树,将记录放进对应的节点,速度会有一定影响,但是不大
  • 添加索引会占用磁盘空间,是用空间换取时间的经典案例
  • 以上操作仅给 empno字段创建了索引,但并没有给其他字段创建索引,所以查询其他字段依旧很慢
mysql> select * from emp where empno=7237892;
+---------+--------+----------+-----+------------+---------+--------+--------+
| empno   | ename  | job      | mgr | hiredate   | sal     | comm   | deptno |
+---------+--------+----------+-----+------------+---------+--------+--------+
| 7237892 | UDgWoJ | SALESMAN |   1 | 2020-11-21 | 2000.00 | 400.00 |    150 |
+---------+--------+----------+-----+------------+---------+--------+--------+
1 row in set (0.00 sec)

mysql> select * from emp where ename='eeeeee';
Empty set (1.22 sec)

创建主键索引

主键本身就自带索引,所以创建主键索引就是创建主键,主键索引是一种特殊的唯一索引,一个表只能有一个主键,所以也只能有一个主键索引,不允许有空值。

//方式一 在创建表时,直接在字段名后面指定 primary key
mysql> create table test1(id int primary key,name varchar(30));
Query OK, 0 rows affected (0.68 sec)

//方式二 在创建表最后,指定某列,某几列为主键索引, 这里也可能是复合主键索引primary key(id,name);
mysql> create table test2(id int,name varchar(30),primary key(id));
Query OK, 0 rows affected (0.08 sec)

//方式三 在创建表后,再指定主键索引
mysql> create table test3(id int,name varchar(30));
Query OK, 0 rows affected (0.07 sec)

mysql> alter table test3 add primary key(id);
Query OK, 0 rows affected (0.17 sec)
Records: 0  Duplicates: 0  Warnings: 0

主键索引的特点

  • 一个表中,最多有一个主键索引,当然可以是复合主键
  • 主键索引和唯一索引的效率高,因为没有重复,二叉树纵向深
  • 创建主键索引的列,它的值不能为null,而且不能重复
  • 主键索引的列,基本上是int

创建唯一索引

索引列的值必须唯一,但允许有空值。如果是组合索引,则列值的组合必须唯一

//方式一 在表定义时,某列后直接指定 unique 即可.
mysql> create table test4(id int primary key,name varchar(30) unique);
Query OK, 0 rows affected (0.06 sec)

//方式二 我们可以在表的定义后面指定某列,或者某几列为uniqu
mysql> create table test5(id int primary key,name varchar(30),unique(name));
Query OK, 0 rows affected (0.06 sec)

//方式三 在创建表后,再指定唯一索引
mysql> create table test6(id int primary key,name varchar(30));
Query OK, 0 rows affected (0.06 sec)

mysql> alter table test6 add unique(name);
Query OK, 0 rows affected (0.09 sec)
Records: 0  Duplicates: 0  Warnings: 0

//方式四 uni_name是唯一索引的名字,命名规范是uni_后面带上该字段的名称
mysql> create unique index uni_name on test7(name);
Query OK, 0 rows affected (0.09 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> show create table test7;
+-------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table                                                                                                                                                                   |
+-------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| test7 | CREATE TABLE `test7` (
  `id` int(11) NOT NULL,
  `name` varchar(30) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `uni_name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 |
+-------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

唯一索引的特点

  • 一个表中,可以有多个唯一索引
  • 查询的效率很高
  • 如果你在某个字段上要创建唯一索引,必须保证这列的值不重复.
  • 如果我们在唯一索引上在指定了 not null 约束,等价于一个主键索引

创建普通索引

//方式一 在表的定义的最后指定某列为索引
mysql> create table test8(id int primary key,name varchar(30),index(name));
Query OK, 0 rows affected (0.06 sec)

mysql> create table test9(id int primary key,name varchar(30));
Query OK, 0 rows affected (0.05 sec)

//方式二 在创建表后,再指定普通索引
mysql> alter table test9 index(name);
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '(name)' at line 1
mysql> alter table test9 add index(name);
Query OK, 0 rows affected (0.08 sec)
Records: 0  Duplicates: 0  Warnings: 0

//方式三 inx_name是普通索引的名字,命名规范是inx_后面带上该字段的名称
mysql> create table test10(id int primary key,name varchar(30));
Query OK, 0 rows affected (0.05 sec)
mysql> create index inx_name on test10(name);
Query OK, 0 rows affected (0.08 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> show create table test10;
+--------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table  | Create Table                                                                                                                                                             |
+--------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| test10 | CREATE TABLE `test10` (
  `id` int(11) NOT NULL,
  `name` varchar(30) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `inx_name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 |
+--------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

普通索引的特点

  • 一个表中可以有多个普通索引,普通索引在实际开发中,使用的很多
  • 如果某列需要创建索引,但是该列有重复的值,那么我们就应该使用普通索引来搞定

创建全文索引(了解)

当我们对文章字段或者有大量文字的字段进行检索时,会使用到全文索引,mysql 提供全文索引的机制,但是要求表的存储引擎是 MyISAM,而且默认全文索引支持英文,不支持中文,如果对中文进行全文检索,可以使用 mysql的插件 mysqlcft,或是使用sphinx 的中文版(coreseek)

创建全文索引

mysql> create table articles(
    -> id int unsigned primary key auto_increment,
    -> title varchar(200),
    -> body text,
    -> fulltext(title,body)
    -> )engine=myisam;
Query OK, 0 rows affected (0.04 sec)


mysql> INSERT INTO articles (title,body) VALUES
    -> ('MySQL Tutorial','DBMS stands for DataBase ...'),
    -> ('How To Use MySQL Well','After you went through a ...'),
    -> ('Optimizing MySQL','In this tutorial we will show ...'),
    -> ('1001 MySQL Tricks','1. Never run mysqld as root. 2. ...'),
    -> ('MySQL vs. YourSQL','In the following database comparison ...'),
    -> ('MySQL Security','When configured properly, MySQL ...');
Query OK, 6 rows affected (0.01 sec)
Records: 6  Duplicates: 0  Warnings: 0


mysql> select * from articles;
+----+-----------------------+------------------------------------------+
| id | title                 | body                                     |
+----+-----------------------+------------------------------------------+
|  1 | MySQL Tutorial        | DBMS stands for DataBase ...             |
|  2 | How To Use MySQL Well | After you went through a ...             |
|  3 | Optimizing MySQL      | In this tutorial we will show ...        |
|  4 | 1001 MySQL Tricks     | 1. Never run mysqld as root. 2. ...      |
|  5 | MySQL vs. YourSQL     | In the following database comparison ... |
|  6 | MySQL Security        | When configured properly, MySQL ...      |
+----+-----------------------+------------------------------------------+
6 rows in set (0.00 sec)

//这种方式根本没有用到索引
mysql> select * from articles where body like '%database%';
+----+-------------------+------------------------------------------+
| id | title             | body                                     |
+----+-------------------+------------------------------------------+
|  1 | MySQL Tutorial    | DBMS stands for DataBase ...             |
|  5 | MySQL vs. YourSQL | In the following database comparison ... |
+----+-------------------+------------------------------------------+
2 rows in set (0.00 sec)

//explain + 语句,可以检索该语句是否用到了索引  key=null 没有到
mysql> explain select * from articles where body like '%database%';
+----+-------------+----------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table    | type | possible_keys | key  | key_len | ref  | rows | Extra       |
+----+-------------+----------+------+---------------+------+---------+------+------+-------------+
|  1 | SIMPLE      | articles | ALL  | NULL          | NULL | NULL    | NULL |    6 | Using where |
+----+-------------+----------+------+---------------+------+---------+------+------+-------------+
1 row in set (0.00 sec)

mysql> explain select * from articles where body like '%database%'\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: articles
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 6
        Extra: Using where
1 row in set (0.00 sec)

使用全文索引

mysql> select * from articles where match(title,body) against('database');
+----+-------------------+------------------------------------------+
| id | title             | body                                     |
+----+-------------------+------------------------------------------+
|  5 | MySQL vs. YourSQL | In the following database comparison ... |
|  1 | MySQL Tutorial    | DBMS stands for DataBase ...             |
+----+-------------------+------------------------------------------+
2 rows in set (0.00 sec)

mysql> explain select * from articles where match(title,body) against('database')\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: articles
         type: fulltext
possible_keys: title
          key: title
      key_len: 0
          ref:
         rows: 1
        Extra: Using where
1 row in set (0.00 sec)

全文索引的几个概念

  • 停止词: 全文索引不会对一般意义的单词建索引
  • 匹配度: mysql提供的全文索引机制,是以匹配度来返回结果
//全文索引不会对一般意义的单词建索引
mysql> select * from articles where match(title,body) against('a');
Empty set (0.00 sec)

//mysql提供的全文索引机制,是以匹配度来返回结果
mysql> select match(title,body) against('database') from articles;
+---------------------------------------+
| match(title,body) against('database') |
+---------------------------------------+
|                    0.6554583311080933 |
|                                     0 |
|                                     0 |
|                                     0 |
|                    0.6626645922660828 |
|                                     0 |
+---------------------------------------+
6 rows in set (0.00 sec)

显示索引信息

你可以使用 show index/keys 命令来列出表中的相关的索引信息,可以通过添加 \G 来格式化输出信息

创建测试表

//test11表包含 主键索引 唯一索引 普通索引
mysql> create table test11(id int primary key,name varchar(30),email varchar(30) unique,index(name));
Query OK, 0 rows affected (0.67 sec)

//这种方式其实也可以查看索引
mysql> show create table test11;
+--------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table  | Create Table                                                                                                                                                                                                                             |
+--------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| test11 | CREATE TABLE `test11` (
  `id` int(11) NOT NULL,
  `name` varchar(30) DEFAULT NULL,
  `email` varchar(30) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `email` (`email`),
  KEY `name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 |
+--------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

显示索引信息

  • show keys from test11
  • show index from test11
  • show indexes from test11
  • 三种方式完全一样,一般用第二种
//show keys from test11 和 show index from test11 完全一样
mysql> show keys from test11\G
*************************** 1. row ***************************
        Table: test11		# 表名:test11
   Non_unique: 0			# 不是唯一索引: 否
     Key_name: PRIMARY		# 索引名称:主键
 Seq_in_index: 1			# 
  Column_name: id			# 索引位置:id
    Collation: A
  Cardinality: 0
     Sub_part: NULL
       Packed: NULL
         Null:
   Index_type: BTREE		# 索引算法:二叉树
      Comment:
Index_comment:
*************************** 2. row ***************************
        Table: test11
   Non_unique: 0
     Key_name: email
 Seq_in_index: 1
  Column_name: email
    Collation: A
  Cardinality: 0
     Sub_part: NULL
       Packed: NULL
         Null: YES
   Index_type: BTREE
      Comment:
Index_comment:
*************************** 3. row ***************************
        Table: test11
   Non_unique: 1
     Key_name: name
 Seq_in_index: 1
  Column_name: name
    Collation: A
  Cardinality: 0
     Sub_part: NULL
       Packed: NULL
         Null: YES
   Index_type: BTREE
      Comment:
Index_comment:
3 rows in set (0.00 sec)

删除索引

在删除索引前,先使用查看索引的指令,看key_name 是什么,然后再删除

删除主键索引语法

alter table 表名 drop primary key

删除其他索引语法

alter table 表名 drop index 索引名

删除索引的第三种语法

drop index 索引名 on 表名

//查看索引
mysql> show index from test11\G
*************************** 1. row ***************************
        Table: test11
   Non_unique: 0
     Key_name: PRIMARY
 Seq_in_index: 1
  Column_name: id
    Collation: A
  Cardinality: 0
     Sub_part: NULL
       Packed: NULL
         Null:
   Index_type: BTREE
      Comment:
Index_comment:
*************************** 2. row ***************************
        Table: test11
   Non_unique: 0
     Key_name: email
 Seq_in_index: 1
  Column_name: email
    Collation: A
  Cardinality: 0
     Sub_part: NULL
       Packed: NULL
         Null: YES
   Index_type: BTREE
      Comment:
Index_comment:
*************************** 3. row ***************************
        Table: test11
   Non_unique: 1
     Key_name: name
 Seq_in_index: 1
  Column_name: name
    Collation: A
  Cardinality: 0
     Sub_part: NULL
       Packed: NULL
         Null: YES
   Index_type: BTREE
      Comment:
Index_comment:
3 rows in set (0.00 sec)

//删除主键索引
mysql> alter table test11 drop primary key;
Query OK, 0 rows affected (0.14 sec)
Records: 0  Duplicates: 0  Warnings: 0

//删除其他索引
mysql> alter table test11 drop index email;
Query OK, 0 rows affected (0.10 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> alter table test11 drop index name;
Query OK, 0 rows affected (0.11 sec)
Records: 0  Duplicates: 0  Warnings: 0

//查看索引
mysql> show index from test11\G
Empty set (0.00 sec)

修改索引

先删除,再创建吧,其实删除索引也可以用修改字段实现

创建索引的原则

较频繁的作为查询条件的字段应该创建索引

select * from emp where empno=1

唯一性太差(重复太多)的字段不适合单独创建索引,即使频繁作为查询条件

select * from emp where sec=‘男’

更新非常频繁的字段不适合创建索引

select * from emp where logincount=1

不会出现在where子句中的字段不该创建索引

第一句的反写

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值