MySQL索引与事务详解
一、前言
上一章我们讲解了MySQL的手工编译安装流程以及相关的数据库操作命令(sql语句),本文将要详细介绍MySQL索引与事务的概念及原理,并初步了解MySQL数据库视图概念,简述数据库的存储过程。
二、索引
2.1索引的概念——什么是索引?
一般来说,一篇论文,或者说一本书,都有其目录,而目录一般是所有章节的概述,或者说是要点核心,而索引的概念其实也与之类似。
索引,顾名思义,就是一个方便用户搜索所需资源的引导,只不过在数据库中,索引一般被认为是一种特殊的文件,尤其在Linux系统中("一切皆文件")。从专业术语上解释其含义就是“代表记录的引用指针”。
2.2索引的作用
- 加快查询速度,提高工作效率;
- 降低i/o成本,提供基础排序;
- 加快多表之间的连接;
- 支持唯一性索引(下面会介绍什么是唯一性索引),保证数据表中数据的唯一性性;
2.3索引的分类
2.3.1普通索引
就是一般的索引,只是为了区别于其他特殊索引的一个统称
2.3.2唯一性索引
与普通索引基本类同,区别在于,唯一性索引的列中的所有值都不相同,即“唯一”。
简单举例来说,学生数据表,年龄可以是普通索引,但不可以是唯一性索引,但是详细住址可以是。
2.3.3主键索引
本质上也是一种唯一性索引,但必须指定为“primary key”,该索引要求主键中的每个值都唯一。上篇文章中,我们提及到了主键的概念,其特点也是“非空唯一”。
2.3.4全文索引
索引类型为FULLTEXT,全文索引可以在char、vachar或者text类型的列上创建。
2.3.5单列索引与多列索引
可以在单列或多列上创建索引。多列索引一般用于区分其中一列可能有相同值的行。
2.4创建索引的原则:建立在表上的(索引依赖于表)
索引可以提升数据库的查询速度,但并非所有的数据表都需要创建索引。因为索引本身也是需要占用系统资源的,或许一般情况下这个问题不会很突出,因为服务器的资源在一定程度上还是能够正常支持的,但是如果索引文件过大,其大小可能达到操作系统允许的最大文件限制。
并且,如果说索引使用不当也会造成数据库的负担。因此,数据库创建索引也是有其原则的。
2.4.1创建索引的原则依据
- 表的主键、外键必须有索引;
- 达到一定量的表(300行记录)应该有索引;
- 表之间的连接字段上应该建立索引;
- 不能使用唯一性太差的字段作为索引;
- 更新频繁的字段也不适合作为索引;
- 小字段适合建立索引,长的字段则不适合建立索引;
2.5索引的优缺点
优点:快速查询所需资源
缺点:占用空间以及资源
2.6创建及查看索引
首先我们需要保证数据库中有表,且表内有数据;
mysql> select * from fruit_info;
+----+-------+---------+
| id | price | newtype |
+----+-------+---------+
| 1 | 2.50 | banana |
| 2 | 5.50 | apple |
| 3 | 6.00 | peach |
+----+-------+---------+
3 rows in set (0.00 sec)
2.6.1创建普通索引
命令格式:create index <索引名> on 数据表 (列名);
实例:
mysql> create index id_index on fruit_info(id);
Query OK, 0 rows affected (0.00 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> show index from fruit_info; #查看索引语句也可以将index换成“keys”
+------------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+------------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| fruit_info | 1 | id_index | 1 | id | A | 3 | NULL | NULL | | BTREE | | |
+------------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
1 row in set (0.00 sec)
其中Non_unique为1,表示不是唯一性索引;Key_name 对应的是索引名称,这里就是id_index;
2.6.2创建唯一性索引
命令格式:create unique index <索引名称> on 数据表 (列名);
实例:
mysql> create unique index type_index on fruit_info(newtype);
Query OK, 0 rows affected (0.01 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> show index from fruit_info;
+------------+------------+------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+------------+------------+------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| fruit_info | 0 | type_index | 1 | newtype | A | 3 | NULL | NULL | YES | BTREE | | |
| fruit_info | 1 | id_index | 1 | id | A | 3 | NULL | NULL | | BTREE | | |
+------------+------------+------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
2 rows in set (0.00 sec)
这里的索引名称为type_index,与之对应的Non_unique的值为0,表示其为唯一性索引。并且唯一性索引的值都不一样。
那么我们考虑一个问题:数据库中的表的字段是否既可以是普通索引,又可以是唯一性索引?
我们来实操验证一下:
mysql> create unique index id_index_new on fruit_info(id);
Query OK, 0 rows affected (0.03 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> show index from fruit_info;
+------------+------------+--------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+------------+------------+--------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| fruit_info | 0 | id_index_new | 1 | id | A | 3 | NULL | NULL | | BTREE | | |
| fruit_info | 0 | type_index | 1 | newtype | A | 3 | NULL | NULL | YES | BTREE | | |
| fruit_info | 1 | id_index | 1 | id | A | 3 | NULL | NULL | | BTREE | | |
+------------+------------+--------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
3 rows in set (0.00 sec)
事实证明是可以的,但是我们需要知道唯一性索引与普通索引的区别就在于“唯一性”上。如果创建了唯一性索引,那么在插入数据记录的时候就需要注意字段匹配时的唯一性。
2.6.3创建主键索引
命令格式:(1)创建表的时候创建主键:create table 表名 ([ ... ],primary key(列的列表));
(2)修改表结构加入主键:alter table 表名 add primary key;
实例:
mysql> create table student (id int not null,sex char(2),age int not null,hobby varchar(20),primary key(id,hobby));
Query OK, 0 rows affected (0.01 sec)
mysql> show index from student;
+---------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+---------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| student | 0 | PRIMARY | 1 | id | A | 0 | NULL | NULL | | BTREE | | |
| student | 0 | PRIMARY | 2 | hobby | A | 0 | NULL | NULL | | BTREE | | |
+---------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
2 rows in set (0.01 sec)
一个表里面只能有一个主键,但一个主键可以由多个字段组成。
mysql> alter table fruit_info add primary key(id);
Query OK, 0 rows affected (0.04 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> show index from fruit_info;
+------------+------------+--------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+------------+------------+--------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| fruit_info | 0 | PRIMARY | 1 | id | A | 4 | NULL | NULL | | BTREE | | |
| fruit_info | 0 | id_index_new | 1 | id | A | 4 | NULL | NULL | | BTREE | | |
| fruit_info | 0 | type_index | 1 | newtype | A | 4 | NULL | NULL | YES | BTREE | | |
| fruit_info | 1 | id_index | 1 | id | A | 4 | NULL | NULL | | BTREE | | |
+------------+------------+--------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
4 rows in set (0.00 sec)
主键索引是设置主键后自动创建的,无需指定名称,系统自动生成名字“primary”。主键索引与唯一性索引区别就在于唯一性索引可以为null,而主键索引为not null,所以可以简单用公式理解:primary index = not null + unique index;
2.6.4全文索引
全文索引可以建立的字段类型在前面已经提及了,命令格式如下:
1、create table 表名 (列名 text,FULLTEXT(列名))engine=MyISAM;
2、alter table 表名 add FULLTEXT(列名);
实例:
查看数据库的存储引擎类型:(存储引擎我们下一篇文章会讲解)
mysql> show table status from fruit where name='student'\G
*************************** 1. row ***************************
Name: student
Engine: InnoDB
Version: 10
Row_format: Dynamic
Rows: 0
Avg_row_length: 0
Data_length: 16384
Max_data_length: 0
Index_length: 16384
Data_free: 0
Auto_increment: NULL
Create_time: 2020-01-06 19:12:24
Update_time: NULL
Check_time: NULL
Collation: utf8_general_ci
Checksum: NULL
Create_options:
Comment:
1 row in set (0.00 sec)
mysql> alter table student add fulltext(hobby);
Query OK, 0 rows affected (0.02 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> mysql> show keys from student;
+---------+------------+-------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+---------+------------+-------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| student | 0 | PRIMARY | 1 | id | A | 0 | NULL | NULL | | BTREE | | |
| student | 0 | PRIMARY | 2 | hobby | A | 0 | NULL | NULL | | BTREE | | |
| student | 1 | hobby | 1 | hobby | NULL | 0 | NULL | NULL | | FULLTEXT | | |
+---------+------------+-------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
3 rows in set (0.00 sec)
2.6.5多列索引
在创建索引时指定多列即可
命令格式:create index 索引名 on 表名(字段1,字段2);
实例:
mysql> create index mo_index on student(id,hobby)
-> ;
Query OK, 0 rows affected (0.00 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> show index from student;
+---------+------------+-------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+---------+------------+-------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| student | 0 | PRIMARY | 1 | id | A | 0 | NULL | NULL | | BTREE | | |
| student | 0 | PRIMARY | 2 | hobby | A | 0 | NULL | NULL | | BTREE | | |
| student | 1 | mo_index | 1 | id | A | 0 | NULL | NULL | | BTREE | | |
| student | 1 | mo_index | 2 | hobby | A | 0 | NULL | NULL | | BTREE | | |
| student | 1 | hobby | 1 | hobby | NULL | 0 | NULL | NULL | | FULLTEXT | | |
+---------+------------+-------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
6 rows in set (0.00 sec)
2.7删除索引
命令格式:
2.7.1直接删除索引
格式:drop index 索引名 on 表名;
实例:
mysql> drop index mo_index on student;
Query OK, 0 rows affected (0.01 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> show index from student;
+---------+------------+-------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+---------+------------+-------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| student | 0 | PRIMARY | 1 | id | A | 0 | NULL | NULL | | BTREE | | |
| student | 0 | PRIMARY | 2 | hobby | A | 0 | NULL | NULL | | BTREE | | |
| student | 1 | hobby | 1 | hobby | NULL | 0 | NULL | NULL | | FULLTEXT | | |
+---------+------------+-------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
4 rows in set (0.00 sec)
2.7.2修改表是删除索引
格式:alter table 表名 drop index 索引名;
实例:
mysql> alter table student drop index hobby;
Query OK, 0 rows affected (0.01 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> show index from student;
+---------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+---------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| student | 0 | PRIMARY | 1 | id | A | 0 | NULL | NULL | | BTREE | | |
| student | 0 | PRIMARY | 2 | hobby | A | 0 | NULL | NULL | | BTREE | | |
+---------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
2 rows in set (0.00 sec)
2.7.3删除主键索引
格式:alter table 表名 drop primary key;
实例:
mysql> alter table student drop primary key;
Query OK, 0 rows affected (0.01 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> show index from student;
Empty set (0.00 sec)
当然如果在修改表结构的时候,删除了包含索引的列,那么对应的索引也会被删除。
三、事务
3.1事务的概念
将多个命令作为整体执行,要么都成功要么都不执行,如银行转账;
如果执行事务的时候,前面的一部分成功了,而最后的一部分失败了,那么就会实行“回滚“机制,将执行操作回到事务的起点位置,数据没有发生结果性变化。
3.2事务的ACID特点(4个)
3.2.1原子性
事务是不可分割的工作逻辑单元,事务可以包含多个sql语句,但整个事务是一个完整的操作,不可分割;(比如转账,转账过程中一但出现error,那么就会回滚到起初状态,二者资产不会有任何变化)
3.2.2一致性
事务执行前和执行后数据必须处于一致状态,但是执行过程中是动态变化的;(比如转账,转账前和转账后双方资产的总和是不变的(不考虑手续费等其他费用的情况))
3.2.3隔离性
并发事务是彼此隔离的,事务之间必须是独立(比如打电话,甲和乙打电话不会影响丙和丁打电话)
3.2.4持久性
事务结果都是永久的并且是不可逆的(比如转账的结果,如果发生纠纷再次处理就是另一个事务了。)
3.3事务的操作
3.3.1自动提交
默认的情况下是自动提交的,就是输入了sql语句就自动提交执行该命令,但一般来说这是不安全的;
3.3.2手动提交
在生产环境中,该模式使用比较多,这是因为手动提交可以通过缓存,内存中的数据显示的结果查看是否出错,错了即实行回滚操作(rollback)(一般会设置回滚点)。
3.3.3使用事务命令控制事务(4个)
begin:表示开始一个事务,后面接多个sql语句;0
commit:表示提交一个事务,对应前面的begin
rollback:表示回滚一个事务,在begin和rollback之间,错误的时候可以回滚。
savepoint:表示设置回滚点配合rollback命令使用。
实例:
首先我们有一个如下的数据表:
mysql> desc fruit_info; #表结构
+---------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+---------+--------------+------+-----+---------+-------+
| id | int(4) | NO | PRI | NULL | |
| price | decimal(3,2) | NO | | NULL | |
| newtype | varchar(6) | YES | UNI | NULL | |
+---------+--------------+------+-----+---------+-------+
3 rows in set (0.00 sec)
mysql> select * from fruit_info; #表数据
+----+-------+---------+
| id | price | newtype |
+----+-------+---------+
| 1 | 2.50 | banana |
| 2 | 5.50 | apple |
| 3 | 6.00 | peach |
| 4 | 6.00 | orange |
+----+-------+---------+
4 rows in set (0.00 sec)
mysql> begin; #开始一个事务的标志
Query OK, 0 rows affected (0.00 sec)
mysql> insert into fruit_info values(5,4,'pear'); #插入一个记录
Query OK, 1 row affected (0.00 sec)
mysql> select * from fruit_info; #此时只是放入缓存中使用rollback可以回到最初状态;
+----+-------+---------+
| id | price | newtype |
+----+-------+---------+
| 1 | 2.50 | banana |
| 2 | 5.50 | apple |
| 3 | 6.00 | peach |
| 4 | 6.00 | orange |
| 5 | 4.00 | pear |
+----+-------+---------+
5 rows in set (0.00 sec)
mysql> rollback;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from fruit_info;
+----+-------+---------+
| id | price | newtype |
+----+-------+---------+
| 1 | 2.50 | banana |
| 2 | 5.50 | apple |
| 3 | 6.00 | peach |
| 4 | 6.00 | orange |
+----+-------+---------+
4 rows in set (0.00 sec)
mysql> insert into fruit_info values(5,4,'pear');
Query OK, 1 row affected (0.00 sec)
mysql> commit; #提交之后无法使用rollback回到最初状态;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from fruit_info;
+----+-------+---------+
| id | price | newtype |
+----+-------+---------+
| 1 | 2.50 | banana |
| 2 | 5.50 | apple |
| 3 | 6.00 | peach |
| 4 | 6.00 | orange |
| 5 | 4.00 | pear |
+----+-------+---------+
5 rows in set (0.00 sec)
mysql> rollback;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from fruit_info;
+----+-------+---------+
| id | price | newtype |
+----+-------+---------+
| 1 | 2.50 | banana |
| 2 | 5.50 | apple |
| 3 | 6.00 | peach |
| 4 | 6.00 | orange |
| 5 | 4.00 | pear |
+----+-------+---------+
5 rows in set (0.01 sec)
设置断点——“回滚点”
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
mysql> insert into fruit_info values(5,4,'pear');
ERROR 1062 (23000): Duplicate entry '5' for key 'PRIMARY'
mysql> insert into fruit_info values(6,4,'grape');
Query OK, 1 row affected (0.00 sec)
mysql> savepoint s1;
Query OK, 0 rows affected (0.00 sec)
mysql> insert into fruit_info values(7,4,'cherry');
Query OK, 1 row affected (0.00 sec)
mysql> savepoint s2;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from fruit_info;
+----+-------+---------+
| id | price | newtype |
+----+-------+---------+
| 1 | 2.50 | banana |
| 2 | 5.50 | apple |
| 3 | 6.00 | peach |
| 4 | 6.00 | orange |
| 5 | 4.00 | pear |
| 6 | 4.00 | grape |
| 7 | 4.00 | cherry |
+----+-------+---------+
7 rows in set (0.00 sec)
mysql> rollback to savepoint s2;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from fruit_info;
+----+-------+---------+
| id | price | newtype |
+----+-------+---------+
| 1 | 2.50 | banana |
| 2 | 5.50 | apple |
| 3 | 6.00 | peach |
| 4 | 6.00 | orange |
| 5 | 4.00 | pear |
| 6 | 4.00 | grape |
| 7 | 4.00 | cherry |
+----+-------+---------+
7 rows in set (0.00 sec)
mysql> rollback to savepoint s1;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from fruit_info;
+----+-------+---------+
| id | price | newtype |
+----+-------+---------+
| 1 | 2.50 | banana |
| 2 | 5.50 | apple |
| 3 | 6.00 | peach |
| 4 | 6.00 | orange |
| 5 | 4.00 | pear |
| 6 | 4.00 | grape |
+----+-------+---------+
6 rows in set (0.00 sec)
mysql> rollback;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from fruit_info;
+----+-------+---------+
| id | price | newtype |
+----+-------+---------+
| 1 | 2.50 | banana |
| 2 | 5.50 | apple |
| 3 | 6.00 | peach |
| 4 | 6.00 | orange |
| 5 | 4.00 | pear |
+----+-------+---------+
5 rows in set (0.00 sec)
mysql> rollback to savepoint s1;
ERROR 1305 (42000): SAVEPOINT s1 does not exist
根据以上的结果我们可以得到如下结论:
使用事务命令控制事务是可以实现回滚机制的;
在使用savepoint结合rollback命令时,回滚的位置是根据你执行的命令的最终位置;
如果直接使用rollback命令是直接回到最初状态,且无法回到其他回滚节点。
3.3.4set命令
set autocommit=0:禁止自动提交 ——就相当于begin;
set autocommit-=1:开启自动提交
四、总结
本文主要是对MySQL数据库中的索引和事务的概念进行详细的介绍,这里的概念和原理以及对应的使用场景需要我们结合实例进行详细理解。索引的分类以及主键索引与唯一性索引的区别,事务的4大特点和事务的回滚机制。
本文的操作并不难,但是细节上的原理上的理解还是比较琐碎拗口的,需要真正的理解,面试的时候这块内容十分重要,谢谢您的阅读!
来源:51CTO
作者:wx5d8a17c45cb5b
链接:https://blog.51cto.com/14557673/2464798