一、事务概述
1.1 事务的概念
- 事务是种机制、一个操作序列,包含了一组数据库操作命令,并且把所有的命令作为一个整体一起向系统提交或撤销操作请求,即这一组数据库命令要么都执行,要么都不执行。
- 事务是一个不可分割的工作逻辑单元,在数据库系统上执行并发操作时,事务是最小的控制单元。
- 事务适用于多用户同时操作的数据库系统的场景,如银行、保险公司及证券交易系统等等。
- 事务通过事务的整体性以保证数据的一致性。
- 事务能够提高在向表中更新和插入信息期间的可靠性。
1.2 事务的ACID特点
ACID:是指在可靠数据库管理系统(DBMS)中,事务(transaction)应该具有的四个特性:原子性(Atomicity) 、一致性(Consisteney) 、隔离性(Isolation)、持久性(Durability)。这是可靠数据库所应具备的几个特性。
-
原子性:指事务是一个不可再分割的工作单位,事务中的操作要么都发生,要么都不发生。
- 事务是一个完整的操作,事务的各元素是不可分的。
- 事务中的所有元素必须作为一个整体提交或回滚。
- 如果事务中的任何元素失败,则整个事务将失败。
-
一致性:指在事务开始之前和事务结束以后,数据库的完整性约束没有被破坏。
- 当事务完成时,数据必须处于一致状态。
- 在事务开始前,数据库中存储的数据处于一致状态。
- 在正在进行的事务中,数据可能处于不一致的状态。
- 当事务成功完成时,数据必须再次回到已知的一致状态。
-
隔离性:指在并发环境中,当不同的事务同时操纵相同的数据时,每个事务都有各自的完整数据空间。
- 对数据进行修改的所有并发事务是彼此隔离的,表明事务必须是独立的,它不应以任何方式依赖于或影响其他事务。
- 修改数据的事务可在另一个使用相同数据的事务开始之前访问这些数据,或者在另一个使用相同数据的事务结束之后访问这些数据。
-
持久性:在事务完成以后,该事务所对数据库所作的更改便持久的保存在数据库之中,并不会被回滚。
- 指不管系统是否发生故障,事务处理的结果都是永久的。
- 一旦事务被提交,事务的效果会被永久地保留在数据库中。
1.3 并发情况下共同访问表时,可能产生的问题
① 脏读
- 脏读:当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另一个事务也访问了此数据,看到了此数据已经被修改,因此称为脏读
脏读会导致数据读取的紊乱与不一致。
② 不可重复读
- 不可重复读:指在一个事务内,多次访问同一数据,在这个事务还没有结束时;此时,另外一个事务也访问了这个数据,然后使用了这个数据并进行经过事务的修改。另一个事务在访问并修改后,第一个事务两次读到数据可能会不一致,这样就导致了在一个事务内两次读到的数据不一致,因此称为不可重复读(即不能读到相同的数据内容)。
③ 幻读
- 幻读:一个事务对一个表中的数据进行了修改,这种修改涉及到表中的所有数据;同时,另一个事务也修改了这个表中的数据,而另一个事务只是向表中插入了一行新的数据,那么第一个事务的用户会发现表中仍有未修改的数据行,从而产生幻读。
④ 丢失更新
- 丢失更新:两个事务同时读取同一条记录,事务A修改了记录1,事务B也同样修改了记录1,B提交数据后,A 的修改内容被B的修改内容覆盖了。
1.4 事务的隔离级别
事务的隔离级别决定了事务之间可见的级别,MySQL事务支持如下四种隔离,可以用来控制事务所作的修改,并将修改告知给其他并发的事务。
① 未提交读(Read Uncommitted (RU))
允许脏读,即允许一个事务可以看到其他事务未提交的修改。
② 提交读(Read Committed (RC))
允许一个事务只能看到其他事务已经提交的修改,未提交的修改是不可见的,可以用于防止脏读。
③ 可重复读(Repeatable Read (RR))
可重复读是mysql默认的隔离级别,是确保如果在一个事务中执行两次相同的SELECT语句,都能得到相同的结果,不管其他事务是否提交这些修改。可以防止脏读和不可重复读。
④ 串行读(Serializable)——相当于锁表
完全串行化的读,将一个事务与其他事务完全地隔离。每次读都需要获得表级共享锁,读写相互都会阻塞。可以防止脏读,不可重复读取和幻读,但是事务的串行化会降低数据库的效率。
mysql默认的事务处理级别是repeatable read,而oracle 和SQL Server是read committed
二、事务的隔离级别的作用范围与配置
2.1 事务的隔离级别的作用范围
- 全局级:对所有的会话有效
- 会话级:只对当前的会话有效
2.2 查看全局事务隔离级别
show global variables like '%isolation%';
select @@global.tx_isolation;
#两种方式都能查看全局事务隔离级别
复制代码
2.3 查询会话事务隔离级别
show session variables like '%isolation%';
select @@session.tx_isolation;
select @@tx_isolation;
复制代码
2.4 设置全局事务隔离级别
set global transaction isolation level read committed;
set @@global.tx_isolation='read-committed'; # 重启服务后失效
# 将全局隔离级别修改为最小级别的提交读(read committed)
复制代码
2.5 设置会话事务隔离级别
set session transaction isolation level repeatable read;
set @@session.tx_isolation='repeatable-read';
复制代码
三、事务的控制语句
3.1 事务的控制语句分类
- begin 或 start transaction :显式地开启一个事务。
- commit 或 commit work:提交事务,并使已对数据库进行的所有修改变为永久性的。
- rollback 或 rollback work:回滚会结束用户的事务,并撤销正在进行的所有未提交的修改。
- savepoint S1:使用 savepoint 允许在事务中创建一个回滚点,一个事务中可以有多个 savepoint;“s1”代表回滚点名称。
- rollback to [savepoint] s1:把事务回滚到s1标记点。
3.2 事务示例演示
① 开启RC隔离级别演示
在同一台主机开启两个终端,一台终端开启事务操作,另一台进行查询:
此时,终端1提交之前更新的数据,结果如下:
Read Committed的隔离级别可以预防脏读,从而防止两个终端在共同操作一个数据时,查询数据为另一个终端未提交的修改的数据;但是RC隔离级别无法解决可重复读问题,在两个终端分别开启事务中,其中一个终端的修改数据操作提交后,另一个终端在事务中也能看见数据的改变,从而受到影响。
② 回滚操作演示
- 全部回滚操作
- 设置回滚点操作
- 事务的结束只能使用commit(提交事务)或者rollback(回滚所有)进行结束,设置回滚点并返回回滚点并不能将正在操作的事务终止。
③ 事务的自动提交演示
在MySQL数据库中,是默认开启了事务自动提交的功能,简单来说就是每一条SQL语句的操作,都相当于依次自动提交事务的操作。
show variables like 'autocommit';
#我们可以通过该命令查看自动提交功能是否开启
复制代码
set autocommit=0;
#在有需要时,该命令可以关闭事务自动提交的功能
set autocommit=1;
#开启自动提交
复制代码
接下来,我们使用两个终端演示,事务关闭自动提交功能的情况
从上面的操作可以看到,在关闭事务自动提交功能后,事务对数据进行增删改操作后,需要使用结束事务的SQL语句才可以进行数据的更新,否则数据并未上传;无论是否开启自动提交功能,只要手动开启事务(begin),只有使用SQL结束事务语句才能提交。
④ 开启RU隔离级别演示
一个终端在未提交读的隔离级别状态下,修改了数据但是并没有提交事务;此时,其他终端一样能查询到更改后的数据内容,此为脏读现象
⑤ 开启RR隔离级别演示
在RR隔离级别下,两个终端开启事务,不论第一个终端如何修改数据,提交事务;在事务中的终端2,查看的数据都是相同的结果,不会受终端1的事务操作影响,从而解决脏读和不可重复读的问题。
在终端1进行事务的数据更改提交后,终端2在被修改上的数据做增删改的操作,将基于终端1提交事务后的结果进行操作。
四、数据存储引擎介绍
-
MySQL中的数据用各种不同的技术存储在文件中,每一种技术都使用不同的存储机制、索引技巧、锁定水平并最终提供不同的功能和能力,这些不同的技术以及配套的功能在MySQL中称为存储引擎
-
存储引擎是MySQL将数据存储在文件系统中的存储方式或者存储格式
-
MySQL常用的存储引擎
- MyISAM
- InnoDB
-
MySQL数据库中的组件,负责执行实际的数据I/O操作
-
MySQL系统中,存储引擎处于文件系统之.上,在数据保存到数据文件之前会传输到存储引擎,之后按照各个存储引擎的存储格式进行存储
五、MyISAM数据引擎概述
5.1 MyISAM的特点介绍及数据引擎对应文件
-
MyISAM不支持事务,也不支持外键约束,只支持全文索引,数据文件和索引文件是分开保存的
-
访问速度快,对事务完整性没有要求
-
MyISAM适合查询、插入为主的应用
-
MyISAM在磁盘上存储成三个文件,文件名和表名都相同,但是扩展分别为:
- .frm文件存储表结构的定义
- 数据文件的扩展名为.MYD(MYData)
- 索引文件的扩展名为.MYI(MYIndex)
-
表级锁定形式,数据在更新时锁定整个表
-
数据库在读写过程中相互阻塞
- 会在数据写入的过程阻塞用户数据的读取
- 也会在数据读取的过程中阻塞用户的数据写入
-
数据单独写入或读取,速度过程较快且占用资源相对少
-
MyIAM支持的存储格式
- 静态表
- 动态表
- 压缩表
5.2 MyISAM的存储格式分类
① 静态(固定长度)表
静态表是默认的存储格式。静态表中的字段都是非可变字段,这样每个记录都是固定长度的,这种存储方式的优点是存储非常迅速,容易缓存,出现故障容易恢复;缺点是占用的空间通常比动态表多。
② 动态表
动态表包含可变字段,记录不是固定长度的,这样存储的优点是占用空间较少,但是频繁的更新、删除记录会产生碎片,需要定期数据库中执行SQL语句 OPTIMIZE TABLE 语句或命令行输入 myisamchk -r 命令来改善性能,并且出现故障的时候恢复相对比较困难。
③ 压缩表
压缩表由 myisamchk 工具创建,占据非常小的空间,因为每条记录都是被单独压缩的,所以只有非常小的访问开支。
5.3 MyISAM适用的生产场景举例
- 公司业务不需要事务的支持
- 单方面读取或写入数据比较多的业务
- MyISAM存储引擎数据读写都比较频繁场景不适合
- 使用读写并发访问相对较低的业务
- 数据修改相对较少的业务
- 对数据业务一致性要求不是非常高的业务
- 服务器硬件资源相对比较差
六、InnoDB数据引擎概述
6.1 InnoDB特点介绍及数据引擎对应文件
- 支持事务,支持4个事务隔离级别
- MySQL从5.5.5版本开始,默认的存储引擎为 InnoDB
- 读写阻塞与事务隔离级别相关
- 能非常高效的缓存索引和数据
- 表与主键以簇的方式存储
- 支持分区、表空间,类似oracle数据库
- 支持外键约束,5.5前不支持全文索引,5.5后支持全文索引
- 对硬件资源要求还是比较高的场合
- 行级锁定,但是全表扫描操作仍然会是表级锁定,如update table set a=1 where user like ‘%lic%’;
- InnoDB 中不保存表的行数,如 select count() from table;(统计表中所有字段的行数) 时,InnoDB 需要扫描一遍整个表来计算有多少行,但是 MyISAM 只要简单的读出保存好的行数即可。需要注意的是,当 count()语句包含 where 条件时 MyISAM 也需要扫描整个表
- 对于自增长的字段,InnoDB 中必须包含只有该字段的索引,但是在 MyISAM 表中可以和其他字段一起建立组合索引
- 清空整个表时,InnoDB 是一行一行的删除,效率非常慢。MyISAM 则会重建表
6.2 InnoDB适用生产场景分析
-
业务需要事务的支持
-
行级锁定对高并发有很好的适应能力,但需确保查询是通过索引来完成
-
业务数据更新较为频繁的场景
- 如:论坛,微博等
-
业务数据一致性要求较高
- 如:银行业务
-
硬件设备内存较大,利用InnoDB较好的缓存能力来提高内存利用率,减少磁盘IO的压力
6.3 企业选择存储引擎的依据
-
需要考虑每个存储引擎提供了哪些不同的核心功能及应用场景
-
支持的字段和数据类型
- 所有引擎都支持通用的数据类型
- 但不是所有的引擎都支持其它的字段类型,如二进制对象
-
锁定类型:不同的存储引擎支持不同级别的锁定
- 表锁定: MyISAM 支持
- 行锁定: InnoDB支持
七、如何配置存储引擎
7.1 查看系统支持的存储引擎
show engines;
#查看当前支持存储引擎
show create table <表名>\G;
#可以通过查看表结构中包含的存储引擎的信息
show table status from <库名> where name='<表名>'\G;
#可以通过查看库中某个表的状态,其中也包含了所使用的存储引擎
复制代码
7.2 查看表使用的存储引擎
① 方法一
alter table <表名> engine=MyISAM;
#指定表修改存储引擎为MyISAM
复制代码
② 方法二
#命令行进行操作修改配置文件
vim /etc/my.cnf
[mysqld]
default-storage-engine=MyISAM
#更改表的默认存储引擎为MyISAM
systemctl restart mysqld.service
#此方法只对修改了配置文件并重启mysql服务后新创建的表有效,已经存在的表不会受影响。
复制代码
③ 方法三
create table <表名> (字段 数据类型...) engine=MyISAM;
#创建新表时指定表的存储引擎为MyISAM
复制代码
八、补充
8.1 表锁与行锁
- 表锁会导致表中数据的读写相互阻塞
- 行锁如果使用不当会导致死锁(死锁一般是事务相互等待对方释放资源,最后形成环路导致死锁)
8.2 InnoDB使用表级锁定的场景
- 全表扫描的时候,比如where语句中使用like做模糊查询时会导致表级锁定
- select count(*)统计全表的记录行数的时候
- 使没有索引的字段操作的时候InnoDB行锁使通过给索引项实现的,如果没有索引那就会全表扫描
8.3 行锁的示例
示例条件:创建一张新表数据,设置存储引擎为InnoDB,创建一个独列索引(id),两个终端同时开启事务进行操作:
上方操作,在一个事务中已经做了指定数据的修改,相当于已经将该行进行了行锁操作,所以另一个终端无法修改该行,其他行均能被执行。
8.4 表锁的示例
由于第一个终端的事务操作的条件语句是不属于索引的字段,要进行全表的扫描才能执行,从而导致锁表;终端2无法在表级锁定的情况下执行修改表数据的操作。
8.5 死锁的示例
死锁就是两个事务之间互相锁表锁行,导致互相纠缠,从而形成死锁
8.6 如何避免死锁?
- 使用更合理的业务逻辑,以固定的顺序访问表和行数据
- 大事务拆小,大事务更容易出现死锁,如果业务允许,将大事务拆成多个小事务执行
- 在同一个事务中,尽可能做一次锁定所需的所有资源,减少死锁概率
- 降低隔离级别。如果业务允许,可以降低隔离级别,比如把RR调整成RC,这样可以避免很多造成死锁的因素
- 为表字段添加合理的索引。因为不使用会进行表级锁定,死锁的概率就会提高
总结
事务的ACID特性:
- 原子性(Atomicity)
- 一致性(Consistency )
- 隔离性(Isolation)
- 持久性(Durability)
这是可靠数据库所应具备的几个特性。
事务之间的相互影响
当多个客户端并发地访问同一个表时,可能出现下面的一致性问题:
- 脏读
- 不可重复读
- 幻读
- 丢失更新
事务的隔离级别:
(1)未提交读(Read Uncommitted(RU)) :允许脏读。
(2)提交读(Read Committed (RC)) :防止脏读。
(3)可重复读(Repeatable Read(RR)):—mysql默认的隔离级别,防止脏读和不可重复读。
(4)串行读(serializable):—相当于锁表,可以防止脏读、不可重复读和幻读,(事务串行化)会降低数据库的执行效率。
查询全局事务隔离级别:
- show global variables like '%isolation%';
- select @@global.tx_isolation;
查询会话事务隔离级别:
- show session variables like '%isolation%';
- select @@session.tx_isolation;
- select @@tx_isolation;
设置全局事务隔离级别:
- set global transaction isolation level 隔离级别; #永久生效
- 示例: set global transaction isolation level read committed;
设置会话事务隔离级别:
- set session transaction isolation level 隔离级别; #退出连接后失效
- 示例:set session transaction isolation level read committed;
事务控制语句:
- BEGIN 或 START TRANSACTION: 显式地开启一个事务。
- COMMIT 或 COMMITWORK: 提交事务,并使已对数据库进行的所有修改变为永久性的。
- ROLLBACK 或 ROLLBACK WORK: 回滚会结束用户的事务,并撤销正在进行的所有未提交的修改。
- SAVEPOINT S1: 使用SAVEPOINT 允许在事务中创建一个回滚点,一个事务中可以有多个SAVEPOINT;“S1”代表回滚点名称。
- ROLLBACK TO [SAVEPOINT] S1: 把事务回滚到标记点。
使用 set 设置控制事务:
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值