文章目录
- 1. 索引
- 1.1 索引的概念
- 1.2 索引的作用
- 1.3 如何实现索引
- 1.4 索引的缺点
- 1.5 建立索引的原则依据
- 1.6 索引的分类和创建
- 1.6.1 普通索引
- 1.6.2 唯一索引
- 1.6.3 主键索引
- 1.6.4 组合索引
- 1.6.5 全文索引
- 1.7 查看索引
- 1.8 删除索引
- 2. 事务
- 2.1 事务的概念
- 2.2 事务的ACID特性
- 2.2.1 原子性
- 2.2.2 一致性
- 2.2.3 隔离性
- 2.2.4 持久性
- 2.3 事务之间的相互影响
- 2.3.1 脏读
- 2.3.2 不可重复读
- 2.3.3 幻读
- 2.3.4 丢失更新
- 2.4 事务的隔离级别
- 2.4.1 未提交读(Read Uncommitted(RU))
- 2.4.2 提交读(Read Committed(RC))
- 2.4.3 可重复读(Repeatable Read(RR))
- 2.4.4 串行读(Serializable)
- 2.5 事务隔离级别的作用范围
- 2.6 事务隔离级别的设置和查询
- 2.6.1 查看当前隔离级别
- 2.6.1 设置全局事务隔离级别
- 2.6.2 设置会话事务隔离级别
- 2.7 事务控制语句
- 2.7.1 使用set设置控制事务 (拓展)
- 3. 存储引擎
- 3.1 存储引擎简介
- 3.2 Mysql常用的存储引擎
- 3.2.1 MyISAM
- 3.2.1.1 myisam表支持的3种不同的存储格式
- 3.2.2 InnoDB
- 3.3 查看和修改存储引擎
- 3.3.1 查看系统支持的存储引擎
- 3.3.2 查看表使用的存储引擎
- 3.3.3 修改存储引擎
- 3.4 InnoDB行锁与索引的关系
- 3.4.1 行级锁定与表级锁定
- 3.4.2 死锁
1. 索引
1.1 索引的概念
索引是一个排序的列表,包含索引字段的值和其相对于的行数据所在的物理地址
1.2 索引的作用
加快表的查询速度,还可以对字段排序
1.3 如何实现索引
-
没有索引的情况下,要查询某行数据时,需要先扫描全表,再来定位某行数据
-
有了索引后会先通过查找条件的字段值找到其索引对应的行数据的物理地址,然后根据物理地址访问相应的行数据
1.4 索引的缺点
-
索引需要占用额外的磁盘空间。
对于 MyISAM 引擎而言,索引文件和数据文件是分离的,索引文件用于保存数据记录的地址。
而 InnoDB 引擎的表数据文件本身就是索引文件。 -
更新一个包含索引的表需要比更新一个没有索引的表花费更多的时间,这是由于索引本身也需要更新。
因此,理想的做法是仅仅在常常被搜索的列(以及表)上面创建索引。
1.5 建立索引的原则依据
- 表中的记录行数较多时,一般超过300行的表建议要有索引
- 建议在表中的主键字段、外键字段、多表连接的公共关键字段、唯一性较好的字段、不经常更新的字段、经常出现在 where、group by、order by 子语句的字段、小字段上面创建索引
- 不建议在唯一较差的字段、更新太频繁的字段、大文本字段上面创建索引
1.6 索引的分类和创建
1.6.1 普通索引
最基本的索引类型,没有唯一性之类的限制。
#直接创建索引
CREATE INDEX 索引名 ON 表名 (列名[(length)]);
#(列名(length)):length是可选项,下同。如果忽略 length 的值,则使用整个列的值作为索引。如果指定,使用列的前 length 个字符来创建索引,这样有利于减小索引文件的大小。在不损失精确性的情况下,长度越短越好。
#索引名以“_index”结尾。
create index name_index on member (name);
#修改表方式创建
ALTER TABLE 表名 ADD INDEX 索引名 (列名);
#创建表的时候指定索引
CREATE TABLE 表名 ( 字段1 数据类型,字段2 数据类型[,...],INDEX 索引名 (列名));
#示例
#方式1
create table t1 (id int, name char(5), sex char(2), index id_index (id));
#创建表t1并指定id为普通索引
show create table t1;
#查看t1表结构
#方式2
create table member (id int(10),name varchar(10),cardid int(18),phone int(11),address varchar(50),remark text);
#创建一个新的表
create index name_index on member (name);
#指定member表name字段为索引
show create table member;
#查看member表结构
#方式3
create table member1 (id int(10),name varchar(10),cardid int(18),phone int(11),address varchar(50),remark text);
#创建一个新的表
alter table member1 add index name_index (name);
#指定member1表name字段为索引
show create table member1;
#查看member1表结构
1.6.2 唯一索引
- 与普通索引类似,但区别是唯一索引列的每个值都唯一。
- 唯一索引允许有空值(注意和主键不同)。
- 如果是用组合索引创建,则列值的组合必须唯一。
- 添加唯一键将自动创建唯一索引。
#直接创建唯一索引:
CREATE UNIQUE INDEX 索引名 ON 表名(列名);
create unique index cardid_index on member(cardid);
#修改表方式创建
ALTER TABLE 表名 ADD UNIQUE 索引名 (列名);
#创建表的时候指定
CREATE TABLE 表名 (字段1 数据类型,字段2 数据类型[,...],UNIQUE 索引名 (列名));
#示例
create table t2 (id int, name char(5), sex char(2), unique id_index (id));
#创建表t2并指定id为唯一索引
show create table t2;
#查看t2表结构
1.6.3 主键索引
- 是一种特殊的唯一索引,必须指定为“PRIMARY KEY”。
- 一个表只能有一个主键,不允许有空值。
- 添加主键将自动创建主键索引。
#创建表的时候指定
CREATE TABLE 表名 ([...],PRIMARY KEY (列名));
#修改表方式创建
ALTER TABLE 表名 ADD PRIMARY KEY (列名);
#示例
alter table member add primary key (id);
#修改member表id字段为主键索引
1.6.4 组合索引
- 可以是单列上创建的索引,也可以是在多列上创建的索引。
- 需要满足最左原则,因为 select 语句的 where 条件是依次从左往右执行的,所以在使用 select 语句查询时 where 条件使用的字段顺序必须和组合索引中的排序一致,否则索引将不会生效。
create index 索引名 on 表名 (字段1, 字段2, 字段3,....);
alter table 表名 add index 索引名 (字段1, 字段2, 字段3,....);
create unique index 索引名 on 表名 (字段1, 字段2, 字段3,....);
alter table 表名 add unique 索引名 (字段1, 字段2, 字段3,....);
select ... from 表名 where 字段1=XX and 字段2=XX and 字段3=XX #用 and 做条件逻辑运算符时,要创建组合索引且要满足最左原则
#示例
create index username_index on member (id, name);
#在member表中创建组合索引包括id、name
show create table member;
#查看表结构
1.6.5 全文索引
适合在进行模糊查询的时候使用,可用于在一篇文章中检索文本信息。
#直接创建索引
create fulltext index 索引名 on 表名 (字段);
#修改表方式创建
alter table 表名 add fulltext 索引名 (字段);
#创建表的时候指定索引
create table 表名 (字段1 数据类型[,...],fulltext 索引名 (列名));
#数据类型可以为 CHAR、VARCHAR 或者 TEXT
#使用全文索引查询
select ... from 表名 where match(字段) against('查询内容');
#示例
alter table t1 add fulltext name_index (name);
#将t1表name字段指定为全文索引
1.7 查看索引
show create table 表名;
show index from 表名;
show keys from 表名;
#示例
show keys from t1;
#查看t1表下所有所有信息
#各字段的含义如下:
Table:表的名称。
Non_unique:如果索引不能包括重复词,则为 0;如果可以,则为 1。
Key_name:索引的名称。
Seq_in_index:索引中的列序号,从 1 开始。
Column_name:列名称。
Collation:列以什么方式存储在索引中。在 MySQL 中,有值‘A’(升序)或 NULL(无分类)。
Cardinality:索引中唯一值数目的估计值。
Sub_part:如果列只是被部分地编入索引,则为被编入索引的字符的数目。如果整列被编入索引,则为 NULL。
Packed:指示关键字如何被压缩。如果没有被压缩,则为 NULL。
Null:如果列含有 NULL,则含有 YES。如果没有,则该列含有 NO。
Index_type:用过的索引方法(BTREE, FULLTEXT, HASH, RTREE)。
Comment:备注。
1.8 删除索引
#直接删除索引
drop index 索引名 on 表名;
#修改表方式删除索引
alter table 表名 drop index 索引名;
#删除主键索引
alter table 表名 drop primary key;
#示例
drop index name_index on t1;
#删除t1表名为name_index的索引
遇到 select 语句查询速度慢,怎么办?
先使用 explain 分析 select 查询语句,看 key 字段,确定 select 查询语句是否使用了索引或索引使用是否正确。
然后再根据 select 查询语句使用的条件创建相应的单列索引或者组合索引,组合索引要满足最左原则。
2. 事务
2.1 事务的概念
事务就是一组数据库操作序列(包含一个或者多个操作命令),事务会把所有操作看作一个不可分割的整体向系统提交或撤销操作,所有操作要么都执行,要么都不执行
2.2 事务的ACID特性
ACID,是指在可靠数据库管理系统(DBMS)中,事务(transaction)应该具有的四个特性:
- 原子性(Atomicity)
- 一致性(Consistency)
- 隔离性(Isolation)
- 持久性(Durability)
2.2.1 原子性
事务管理的基础。
把事务中的所有操作看作一个不可分割的工作单元,要么都执行,要么都不执行
案例:
A给B转帐100元钱的时候只执行了扣款语句,就提交了,此时如果突然断电,A账号已经发生了扣款,B账号却没收到加款,在生活中就会引起纠纷。
#这种情况就需要事务的原子性来保证事务要么都执行,要么就都不执行。
2.2.2 一致性
事务管理的目的。
保证事务开始前和事务结束后数据的完整和一致
案例:
对银行转帐事务,不管事务成功还是失败,应该保证事务结束后表中A和B的存款总额跟事务执行前一致。
2.2.3 隔离性
事务管理的手段。
- 使多个事务并发操作同一个表数据时,每个事务都有各自独立的数据空间,事务的执行不会受到其他事务的干扰。
- 可通过设置隔离级别解决不同的一致性问题
2.2.4 持久性
事务管理的结果。
当事务被提交以后,事务中的操作命令修改的结果会被持久保存,且不会被回滚
#总结:在事务管理中,原子性是基础,隔离性是手段,一致性是目的,持久性是结果。
2.3 事务之间的相互影响
当多个客户端并发地访问同一个表时,可能出现以下的一致性问题;
2.3.1 脏读
当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据然后使用了这个数据。
2.3.2 不可重复读
指在一个事务内,多次读同一数据。
在这个事务还没有结束时,另外一个事务也访问该同一数据。
那么,在第一个事务中的两次读数据之间,由于第二个事务的修改,那么第一个事务两次读到的的数据可能是不一样的。这样就发生了在一一个事务内两次读到的数据是不一样的,因此称为是不可重复读。( 即不能读到相同的数据内容)
2.3.3 幻读
一个事务对一个表中的数据进行了修改,这种修改涉及到表中的全部数据行。
同时,另一个事务也修改这个表中的数据,这种修改是向表中插入一行新数据。 那么,操作前一个 事务的用户会发现表中还有没有修改的数据行,就好像发生了幻觉一样 。
2.3.4 丢失更新
两个事务同时读取同一条记录,A先修改记录,B也修改记录(B不知道A修改过),B提交数据后B的修改结果覆盖了A的修改结果。
2.4 事务的隔离级别
事务的隔离级别决定了事务之间可见的级别。
MySQL事务支持如下四种隔离,用以控制事务所做的修改,并将修改通告至其它并发的事务
2.4.1 未提交读(Read Uncommitted(RU))
允许脏读,即允许一个事务可以看到其他事务未提交的修改。
2.4.2 提交读(Read Committed(RC))
允许一个事务只能看到其他事务已经提交的修改,未提交的修改是不可见的。防止脏读。
2.4.3 可重复读(Repeatable Read(RR))
确保如果在一个事务中执行两次相同的SELECT语句,都能得到相同的结果,不管其他事务是否提交这些修改。可以防止脏读和不可重复读。
2.4.4 串行读(Serializable)
相当于锁表 完全串行化的读,将一个事务与其他事务完全地隔离。每次读都需要获得表级共享锁,读写相互都会阻塞。可以防止脏读,不可重复读取和幻读,(事务串行化)会降低数据库的效率。
2.5 事务隔离级别的作用范围
事务隔离级别的作用范围分为两种:
- 全局级:对所有的会话有效
- 会话级:只对当前的会话有效(退出连接后即失效)
2.6 事务隔离级别的设置和查询
mysql默认的事务处理级别是 repeatable read
2.6.1 查看当前隔离级别
show session variables like '%isolation%';
#查看当前会话事务隔离级别
show global variables like '%isolation%';
#查看当前全局事务隔离级别
2.6.1 设置全局事务隔离级别
set global transaction isolation level 隔离级别名称;
#全局级的隔离级别,可在所有会话有效,需要重新登录才可生效
#示例
set global transaction isolation level read uncommitted;
#修改当前全局隔离级别为未提交读
2.6.2 设置会话事务隔离级别
set session transaction isolation level 隔离级别名称;
#会话级的隔离级别,在当前会话中立即生效
set session transaction isolation level Read Committed;
#将当前会话隔离级别改为提交读
2.7 事务控制语句
BEGIN 或 START TRANSACTION:显式地开启一个事务。
COMMIT 或 COMMIT WORK:提交事务,并使已对数据库进行的所有修改变为永久性的。
ROLLBACK 或 ROLLBACK WORK:回滚会结束用户的事务,并撤销正在进行的所有未提交的修改。
SAVEPOINT S1:使用 SAVEPOINT 允许在事务中创建一个回滚点,一个事务中可以有多个 SAVEPOINT;“S1”代表回滚点名称。
ROLLBACK TO [SAVEPOINT] S1:把事务回滚到标记点。
#示例
#设置回滚
savepoint s1;
#新建回滚点
update saliary set money=money-500 where id=1;
#修改表内容
rollback to s1;
#回到回滚点
2.7.1 使用set设置控制事务 (拓展)
在mysql中执行单独的命令会立即生效,是因为Mysql默认开启自动提交。
set AUTOCOMMIT=0; #禁止自动提交(仅针对当前会话)
set AUTOCOMMIT=1; #开启自动提交(仅针对当前会话),Mysql默认为1
set global AUTOCOMMIT=0; #禁止自动提交(针对全局事务)
set global AUTOCOMMIT=1; #开启自动提交(针对全局事务),Mysql默认为1
show variables like 'AUTOCOMMIT'; #查看当前会话的AUTOCOMMIT值
show global variables like 'AUTOCOMMIT'; #查看全局事务的AUTOCOMMIT值
- 如果没有开启自动提交,当前会话连接的mysql的所有操作都会当成一个事务直到你输入`rollback; 或 commit;`当前事务才算结束。当前事务结束前新的mysql连接时无法读取到任何当前会话的操作结果。
- 如果开起了自动提交,mysql 会把每个sql 语句当成一个事务,然后自动的commit。
- 当然无论开启与否,`begin; commit | rollback;` 都是独立的事务。
3. 存储引擎
3.1 存储引擎简介
储存引擎 是MySQL数据库中的组件,负责执行实际的数据I/O操作,工作在文件系统之上,数据库的数据会先传到存储引擎,再会按照存储引擎的存储格式保存到文件系统中
3.2 Mysql常用的存储引擎
MyISAM InnoDB
注意:一个表只能使用一个存储引擎,一个库中不同的表可以使用不同的存储引擎。
3.2.1 MyISAM
不支持事务、外键约束,只支持表级锁定,适合单独的查询或插入的操作,读写并发能力较弱,支持全文索引,硬件资源占用较小,数据文件和索引文件是分开存储的。
存储成三个文件:
- 表结构文件.frm
- 数据文件.MYD
- 索引文件.MYI
使用场景:适用于不需要事务处理,单独的查询或插入数据的业务场景
3.2.1.1 myisam表支持的3种不同的存储格式
-
静态(固定长度)表
静态表是默认的存储格式。静态表中的字段都是非可变字段,这样每个记录都是固定长度的,这种存储方式的优点是存储非常迅速,容易缓存,出现故障容易恢复;缺点是占用的空间通常比动态表多。 -
动态表
动态表包含可变字段,记录不是固定长度的,这样存储的优点是占用空间较少,但是频繁的更新、删除记录会产生碎片,需要定期执行 OPTIMIZE TABLE 语句或 myisamchk -r 命令来改善性能,并且出现故障的时候恢复相对比较困难。 -
压缩表
压缩表由 myisamchk 工具创建,占据非常小的空间,因为每条记录都是被单独压缩的,所以只有非常小的访问开支。
3.2.2 InnoDB
支持事务、外键约束,支持行级锁定(在全表扫描时仍然会表级锁定),读写并发能力较好,支持全文索引(5.5版本之后),缓存能力较好可以减少磁盘IO的压力,数据文件也是索引文件。
存储成两个文件
- 表结构文件.frm
- 数据文件.ibd
使用场景:适用于需要事务的支持,一致性要求高的,数据会频繁更新,读写并发高的业务场景
3.3 查看和修改存储引擎
3.3.1 查看系统支持的存储引擎
show engines;
#查看系统支持的存储引擎
#Engine:存储引擎的名称。
#Support:YES表示引擎受支持且处于活动状态,NO表示不支持,DEFAULT表示默认存储引擎。DISABLED表示支持引擎但已将其禁用。
#Comment:存储引擎的简要说明。
#Transactions:存储引擎是否支持事务。
#XA:存储引擎是否支持XA事务。
#Savepoints:存储引擎是否支持回滚点(标记点)。
3.3.2 查看表使用的存储引擎
#方式一:
show table status from 库名 where name='表名'\G
show table status from scj where name='saliary'\G;
#以mysql语句形式显示saliary表的所有信息
方法二:
use 库名;
show create table 表名\G;
3.3.3 修改存储引擎
方法一:alter table 修改
修改当前数据表使用的存储引擎
use 库名;
alter table 表名 engine=存储引擎名称;
注意:因为MyISAM不支持外键约束,如果数据表设置了外键,则无法修改为MyISAM。
#示例
use scj;
alter table saliary engine=myisam;
#将saliary表先存储引擎改为myisam
方法二:修改配置文件,指定默认存储引擎
注意:此方法只对修改配置文件并重启mysql服务之后新创建的表有效,已经存在的表不会有变更。
创建数据表时如果没有指定存储引擎,则会使用默认存储引擎。
vim /etc/my.cnf
......
[mysqld] default-storage-engine=InnoDB
#修改这一行,指定默认存储引擎为InnoDB
systemctl restart mysqld #重启服务
方法三:create table 创建表时指定存储引擎
use 库名;
create table 表名(字段1 数据类型,...) engine=存储引擎名称;
3.4 InnoDB行锁与索引的关系
InnoDB行锁是通过给索引项加锁来实现的,如果没有索引,InnoDB将通过隐藏的聚簇索引来对记录加锁。
#模拟测试
#准备测试环境
mysql> create table text1 (id int primary key,name char(10),age int(2));
mysql> alter table text1 add index name_index(name);
mysql> insert into text1 values (1,'aaa',22);
mysql> insert into text1 values (2,'bbb',23);
mysql> insert into text1 values (3,'ccc',24);
mysql> insert into text1 values (4,'ddd',22);
mysql> insert into text1 values (5,'eee',25);
mysql> insert into text1 values (6,'fff',21);
3.4.1 行级锁定与表级锁定
InnoDB行锁是通过给索引项加锁来实现的,如果没有索引,InnoDB将通过隐藏的聚簇索引来对记录加锁。
1)delete from text1 where id=1;
因为id字段是主键,Innodb对于主键使用了聚簇索引,删除过程中会直接锁住整行记录。行级锁定。
2)delete from text1 where name='aaa';
因为name字段是普通索引,会先锁住索引的行,接着会锁住相应主键对应的记录。行级锁定。
3)delete from text1 where age=23;
因为age字段没有索引,会使用全表扫描过滤,这时表上的各个记录都将加上锁。表级锁定。
for update
: 可以为数据库中的行上一个排它锁。当一个事务的操作未完成时,其他事务可以读取该行数据,但是不能写入、更新或删除。
3.4.2 死锁
死锁一般是事务相互等待对方资源,最后形成环路造成的。
#session1
begin;
#开启事务
delete from text1 where id=5;
#事务结束前,id=5的行会被锁定
#session2
begin;
#开启事务
select * from text1 where id=1 for update;
#加排他锁,模拟并发情况,锁定id=1的行
#session1
delete from text1 where id=1;
#死锁产生
#session2
update text1 set name='abc' where id=5;
#死锁产生。因为会话1中id=5的行还在删除过程中,该行已被锁定
rollback;
#回滚,结束事务。id=5的行被解锁
update text1 set name='abc' where id=5;
#成功更新数据
如何尽可能的避免死锁
1、使用更合理的业务逻辑,以固定的顺序访问表和行。
2、大事务拆小。大事务更倾向于死锁,如果业务允许,将大事务拆小。
3、在同一个事务中,尽可能做到一次锁定所需要的所有资源,减少死锁概率。
4、降低隔离级别。如果业务允许,将隔离级别调低也是较好的选择,比如将隔离级别从RR调整为RC,可以避免掉很多因为gap锁造成的死锁。
5、为表添加合理的索引。如果不使用索引将会为表的每一行记录添加上锁,死锁的概率大大增加。