笔记来源:MySQL数据库教程天花板,mysql安装到mysql高级,强!硬!
一致性
,同时我们还能通过事务的机制恢复到某个时间点,这样可以保证已提交到数据库的修改不会因为系统崩溃而丢失。工作单元
来执行,即使出现了故障,都不能改变这种执行方式。当在一个事务中执行多个操作时,要么所有的事务都被提交
(commit),那么这些修改就永久地保存下来;要么数据库管理系统将放弃所作的所有修改,整个事务回滚
( rollback )到最初状态。原子性(atomicity):原子性是指事务是一个不可分割的工作单位,要么全部提交,要么全部失败回滚。
一致性(consistency):一致性是指事务执行前后,数据从一个合法性状态
变换到另外一个合法性状态。
语义
上的而不是语法上的,跟具体的业务有关。什么是合法的数据状态
呢?满足预定的约束
的状态就叫做合法的状态。通俗一点,这状态是由你自己来定义
的(比如满足现实世界中的约束)。满足这个状态,数据就是一致的,不满足这个状态,数据就是不一致的!如果事务中的某个操作失败了,系统就会自动撤销当前正在执行的事务,返回到事务操作之前的状态。隔离性(isolation):事务的隔离性是指一个事务的执行不能被其他事务干扰,即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。
持久性(durability):持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来的其他操作和数据库故障不应该对其有任何影响。
首先会将数据库的变化信息记录到重做日志中
,然后再对数据库中对应的行进行修改
。总结
ACID是事务的四大特性,在这四个特性中,原子性是基础
,隔离性是手段
,一致性是约束条件
,而持久性是我们的目的
。
数据库事务,其实就是数据库设计者为了方便起见,把需要保证原子性、隔离性、一致性和持久性的一个或多个数据库操作称为一个事务。
我们现在知道事务是一个抽象的概念,它其实对应着一个或多个数据库操作,MySQL根据这些操作所执行的不同阶段把事务大致划分成几个状态:
活动的
(active):事务对应的数据库操作正在执行过程中时,我们就说该事务处在活动的状态。部分提交的
(partially committed):当事务中的最后一个操作执行完成,但由于操作都在内存中执行,所造成的影响并没有刷新到磁盘时,我们就说该事务处在部分提交的状态。失败的
(failed):当事务处在活动的或者部分提交的状态时,可能遇到了某些错误(数据库自身的错误、操作系统错误或者直接断电等)而无法继续执行,或者人为的停止当前事务的执行,我们就说该事务处在失败的状念。中止的
(aborted):如果事务执行了一部分而变为失败的状态,那么就需要把已经修改的事务中的操作还原到事务执行前的状态。换句话说,就是要撤销失败事务对当前数据库造成的影响。我们把这个撤销的过程称之为回滚。当回滚操作执行完毕时,也就是数据库恢复到了执行事务之前的状态,我们就说该事务处在了中止的状态。提交的
(committed):当一个处在部骨提交的状态的事务将修改过的数据都同步到磁盘上之后,我们就可以说该事务处在了提交的状态。事务的状态转换图如下所示:
图中可见,只有当事务处于提交的或者中止的状态时,一个事务的生命周期才算是结束了。对于已经提交的事务来说,该事务对数据库所做的修改将永久生效,对于处于中止状态的事务,该事务对数据库所做的所有修改都会被回滚到没执行该事务之前的状态。
步骤一:START TRANSACTION或者BEGIN,作用是显式开启一个事务。
mysql>BEGIN;
#或者
mysql> START TRANSACTION;
START TRANSACTION相较于BEGIN的特别之处在于,后面能跟几个修饰符:
只读事务
,即属于该事务的数据库只能进行读取操作,不能进行修改操作。(不能对其他事务能访问到的表
进行修改,对临时表
可以进行修改)#开启一个只读事务
START TRANSACTION READ ONLY;
#开启只读事务和一致性读
START TRANSACTION READ ONLY,WITH CONSISTENT SNAPSHOT;
READ ONLY和READ WRITE是用来设置所谓的事务访问模式的。一个事务的访问模式不能同时既设置为只读的也设置为读写的。
如果我们不显式指定事务的访问模式,那么该事务的默认访问模式是读写模式
。
步骤二:一些列事务中的操作,主要是DML,不包含DDL
步骤三:提交事务 或者 终止事务(回滚事务)
# 提交事务。当提交事务后,对数据库的修改是永久性的。
COMMIT;
# 回滚事务。即撤销正在进行的所有没有提交的修改
ROLLBACK;
# 将事务回滚到某个保存点。
ROLLBACK TO [ SAVEPOINT]
# 其中关于SAVEPoINT相关操作有:
# 在事务中创建保存点,方便后续针对保存点进行回滚。一个事务中可以存在多个保存点。
SAVEPOINT保存点名称;
# 删除某个保存点。
RELEASE SAVEPOINT保存点名称:
MySQL中有一个系统变量autocommit:
默认情况下,如果我们不显式的使用START TRANSACTION或者BEGIN语句开启一个事务,那么每一条语句都算是一个独立的事务,这种特性称之为事务的自动提交
。
当然,如果我们想关闭这种自动提交的功能,可以使用下边两种方法之一:
显式的的使用START TRANSACTION或者BEGIN语句开启一个事务。这样在本次事务提交或者回滚前会暂时关闭掉自动提交的功能。
把系统变量autocommit的值设置为OFF
SET autocommit = OFF;
#或
SET autocommit = 0;
数据定义语言(Data definition language,缩写为:DDL)
提交前边语句
所属于的事务。即:BEGIN;
#事务中的一条语句
SELECT ...
#事务中的一条语句
UPDATE ...
#事务中的其它语句
...
#此语句会隐式的提交前边语句所属于的事务
CREATE TABLE ...
隐式使用或修改mysql数据库中的表
事务控制或关于锁定的语句
当我们在一个事务还没提交或者回滚时就又使用START TRANSACTION或者BEGIN语句开启了另一个事务时,会隐式的提交上一个事务
。即:
BEGIN;
#事务中的一条语句
SELECT ...
#事务中的一条语句...#事务中的其它语句
UPDATE ...
#此语句会隐式的提交前边语句所属于的事务
BEGIN;
当前的autocommit系统变量的值为OFF,我们手动把它调为ON时,也会隐式的提交前边语句所属的事务。
使用LOCK TABLES、UNLOCK TABLES等关于锁定的语句也会隐式的提交前边语句所属的事务。
加载数据的语句
LOAD DATA
语句来批量往数据库中导入数据时,也会隐式的提交前边语句所属的事务。关于MySQL复制的一些语句
START SLAVE
、STOP SLAVE
、RESET SLAVE
、CHANGE MASTER TO
等语句时会隐式的提交前边语句所属的事务。其它的一些语句
ANALYZE TABLE
、CACHE INDEX
、CHECK TABLE
、FLUSH
、LOAD INDEX INTO CACHE
、OPTIMIZE TABLE
、REPAIR TABLE
、RESET
等语句也会隐式的提交前边语句所属的事务。举例一:提交与回滚
#举例1: commit 和 rollback
USE atguigudb2;
#情况1:
CREATE TABLE user3(NAME VARCHAR(15) PRIMARY KEY);
SELECT * FROM user3;
BEGIN;
INSERT INTO user3 VALUES('张三'); #此时不会自动提交数据
COMMIT;
BEGIN; #开启一个新的事务
INSERT INTO user3 VALUES('李四'); #此时不会自动提交数据
INSERT INTO user3 VALUES('李四'); #受主键的影响,不能添加成功
ROLLBACK;
SELECT * FROM user3;
TRUNCATE TABLE user3; #DDL操作会自动提交数据,不受autocommit变量的影响。
#情况2:
SELECT * FROM user3;
BEGIN;
INSERT INTO user3 VALUES('张三'); #此时不会自动提交数据
COMMIT;
INSERT INTO user3 VALUES('李四');# 默认情况下(即autocommit为true),DML操作也会自动提交数据。
INSERT INTO user3 VALUES('李四'); #事务的失败的状态
ROLLBACK;
SELECT * FROM user3;
TRUNCATE TABLE user3;
#情况3:
SELECT * FROM user3;
SELECT @@completion_type;
SET @@completion_type = 1;
BEGIN;
INSERT INTO user3 VALUES('张三');
COMMIT;
SELECT * FROM user3;
INSERT INTO user3 VALUES('李四');
INSERT INTO user3 VALUES('李四');
ROLLBACK;
SELECT * FROM user3;
链式事务
,即当我们提交事务之后会开启一个相同隔离级别的事务
。自动与服务器断开连接
。举例二:SAVEPOINT
#举例3:体会savepoint
CREATE TABLE user3(NAME VARCHAR(15),balance DECIMAL(10,2));
BEGIN
INSERT INTO user3(NAME,balance) VALUES('张三',1000);
COMMIT;
SELECT * FROM user3;
BEGIN;
UPDATE user3 SET balance = balance - 100 WHERE NAME = '张三';
UPDATE user3 SET balance = balance - 100 WHERE NAME = '张三';
SAVEPOINT s1;#设置保存点
UPDATE user3 SET balance = balance + 1 WHERE NAME = '张三';
ROLLBACK TO s1; #回滚到保存点
SELECT * FROM user3;
ROLLBACK; #回滚操作
SELECT * FROM user3;
客户端/服务器
架构的软件,对于同一个服务器来说,可以有若干个客户端与之连接,每个客户端与服务器连接上之后,就可以称为一个会话
(Session )。每个客户端都可以在自己的会话中向服务器发出请求语句,一个请求语句可能是某个事务的一部分,也就是对于服务器来说可能同时处理多个事务
。其他事务应该进行排队
,当该事务提交之后,其他事务才可以继续访问这个数据。但是这样对性能影响太大
,我们既想保持事务的隔离性,又想让服务器在处理访问同一数据的多个事务时性能尽量高些,那就看二者如何权衡取舍了。对于两个事务Session A、Session B,如果务Session A修改了另一个未提交事务SessionB修改过的数据
,那就意味着发生了脏写,示意图如下:
Session A和session B各开启了一个事务,Session B中的事务先将studentno列为1的记录的name列更新为’李四’,然后Session A中的事务接着又把这条studentno列为1的记录的name列更新为’张三’。如果之后Session B中的事务进行了回滚,那么Session A中的更新也将不复存在,这种现象就称之为脏写。这时Session A中的事务就没有效果了,明明把数据更新了,最后也提交事务了,最后看到的数据什么变化也没有。这里大家对事务的隔离级比较了解的话,会发现默认隔离级别下
,上面SessionA中的更新语句会处于等待
状态。脏写这个问题太严重了,不论是哪种隔离级别,都不允许脏写的情况发生
。
对于两个事务Session A、Session B,Sessidn A读取了已经被SessionB更新但还没有被提交的字段。之后若Session B回滚,Session A读取的内容就是临时且无效的
。
Session A和Session B各开启了一个事务,Session B中的事务先将studentno列为1的记录的name列更新为’张三’然后Session A中的事务再去查询这条studentno为1的记录,如果读到列name的值为’张三’,而Session B中的事务稍后进行了回滚,那么Session A中的事务相当于读到了一个不存在的数据,这种现象就称之为脏读。
对于两个事务Session A、Session B,SessionA读取了一个字段,然后Session B更新了该字段。之后Session再次读取同一个字段,值就不同了
。那就意味着发生了不可重复读。
我们在Session B中提交了几个隐式事务(注意是隐式事务,意味着语句结束事务就提交了),这些事务都修改了studentno列为1的记录的列name的值,每次事务提交之后,如果Session A中的事务都可以查看到最新的值,这种现象也被称之为不可重复读。
对于两个事务Ssession A、Session B,Session A从一个表中读取了一个字段,然后Session B在该表中插入了一些新的行。之后,如果Session A 再次读取同一个表,就会多出几行
。那就意味着发生了幻读。
Session A中的事务先根据条件studentno > 0这个条件查询表student,得到了name列值为’张三’的记录;之后SessionB中提交了一个隐式事务,该事务向表student中插入了一条新记录;之后Session A中的事务再根据相同:条件studentno>o查询表student,得到的结果集中包含Session B中的事务新插入的那条记录,这种现象也被称为幻读。我们把新插入的那些记录称之为幻影记录
。
那如果Session B中删除了一些符合studentno > 0的记录而不是插入新记录,那Session A之后再根据studentno > 0的条件读取的记录变少了
,这种现象算不算幻读呢?这种现象不属于幻读
,幻读强调的是一个事务按照某个相同条件多次读取记录时,后读取时读到了之前没有读到的记录
。
上面介绍了几种并发事务执行过程中可能遇到的一些问题,这些问题有轻重缓急之分,我们给这些问题按照严重性来排一下序:
脏写 > 脏读 > 不可重复读 > 幻读
我们愿意舍弃一部分隔离性来换取一部分性能在这里就体现在:设立一些隔离级别,隔离级别越低,并发问题发生的就越多。SQL标准中设立了4个隔离级别
:
READ UNCOMMITTED
:读未提交,在该隔离级别,所有事务都可以看到其他未提交事务的执行结果。不能避免脏读、不可重复读、幻读
。READ CONMITTED
:读已提交,它满足了隔离的简单定义:一个事务只能看见已经提交事务所做的改变
。这是大多数数据库系统的默认隔离级别(但不是MySQL默认的)。可以避免脏读,但不可重复读、幻读问题仍然存在
。REPEATABLE READ
︰可重复读,事务A在读到一条数据之后,此时事务B对该数据进行了修改并提交,那么事务A再读该数据,读到的还是原来的内容。可以避免脏读、不可重复读,但幻读问题仍然存在
。这是MySQL的默认隔离级别
。SERIALIZABLE
︰可串行化,确保事务可以从一个表中读取相同的行。在这个事务持续期间,禁止其他事务对该表执行插入、更新和删除操作。所有的并发问题都可以避免
,但性能十分低下
。SQL标准中规定,针对不同的隔离级别,并发事务可以发生不同严重程度的问题,具体情况如下:
隔离级别 | 脏读可能性 | 不可重复读可能性 | 幻读可能性 | 加锁读 |
---|---|---|---|---|
READ UNCONMITED | Yes | Yes | Yes | No |
READ COMMITED | No | Yes | Yes | No |
REPEATABLE READ | No | No | Yes | No |
SERIALIZABLE | No | No | No | Yes |
不同的隔离级别有不同的现象,并有不同的锁和并发机制,隔离级别越高,数据库的并发性能就越差。
MySQL支持的四种隔离级别,Oracle只支持READ COMMITTED(默认隔离级别)和SERIALIZABLE隔离级别。
通过下面的语句修改事务的隔离级别:
SET [GLOBAL | SESSION]TRANSACTION ISOLATION LEVEL 隔离级别;
# 隔离级别格式
# READ UNCOMMITTED
# READ COMMITTED
# REPEATABLE READ
# SERIALIZABLE
或者:
SET [ GLOBAL | SESSION] TRANSACTIONlISOLATION ='隔离级别';
# 隔离级别格式(多了'-'连接符)
# READ-UNCOMMITTED
# READ-COMMITTED
# REPEATABLE-READ
# SERIALIZABLE
关于设置时使用GLOBAL或SESSION的影响:
如果在服务器启动时想改变事务的默认隔离级别,可以修改启动参数transaction_isolation的值。比如,在启动服务器时指定transaction_isolation=SERIALIZABLE,那么事务的默认隔离级别就从原来的REPEATABLE-READ变成了SERIALIZABLE。
从事务理论的角度来看,可以把事务分为以下几种类型:
扁平事务
(Flat Transactions):扁平事务是事务类型中最简单的一种,但是在实际生产环境中,这可能是使用最频繁的事务,在扁平事务中,所有操作都处于同一层次,其由BEGIN WORK开始,由COMMIT WORK或ROLLBACK WORK结束,其间的操作是原子的,要么都执行,要么都回滚。带有保存点的扁平事务
(Flat Transactions with Savepoints):带有保存点的扁平事务除了支持扁平事务支持的操作外,还允许在事务执行过程中回滚到同一事务中较早的一个状态。这是因为某些事务可能在执行过程中出现的错误并不会导致所有的操作都无效,放弃整个事务不合乎要求,开销太大。链事务
(Chained Transactions):链事务是指一个事务由多个子事务链式组成,它可以被视为保存点模式的一个变种。带有保存点的扁平事务,当发生系统崩溃时,所有的保存点都将消失,这意味着当进行恢复时,事务需要从开始处重新执行,而不能从最近的一个保存点继续执行。链事务的思想是:在提交一个事务时,释放不需要的数据对象,将必要的处理上下文隐式地传给下一个要开始的事务,前一个子事务的提交操作和下一个子事务的开始操作合并成一个原子操作
,这意味着下一个事务将看到上一个事务的结果,就好像在一个事务中进行一样。嵌套事务
(Nested Transactions):嵌套事务是一个层次结构框架,由一个顶层事务(Top-Level Transaction)控制着各个层次的事务,顶层事务之下嵌套的事务被称为子事务(Subtransaction),其控制着每一个局部的变换,子事务本身也可以是嵌套事务因此,嵌套事务的层次结构可以看成是一棵树。分布式事务
(Distributed Transactions):分布式事务通常是在一个分布式环境下运行的扁平事务,因此,需要根据数据所在位置访问网络中不同节点的数据库资源。例如,一个银行用户从招商银行的账户向工商银行的账户转账1000元,这里需要用到分布式事务,因为不能仅调用某一家银行的数据库就完成任务。