事务

事务简介

事务的产生,其实是为了当应用程序访问数据库的时候,事务能够简化我们的编程模型,不需要我们去考虑各种各样的潜在错误和并发问题。使用事务时,要么提交,要么回滚,我们不会去考虑网络异常了,服务器宕机等。事务本质上是为了给应用层服务的

在实际开发中,较为复杂的业务逻辑都需要执行一组SQL语句,且这一组语句执行的数据结果存在一定的关联。要么执行成功,要么什么都不,也就是说事务永远不会只完成一部分。为了控制语句组的执行过程,数据库提供事务的机制进行控制。

引擎

什么是引擎

数据库中的数据用各种不同的技术存储在文件(或内存)中。每一种技术都使用不同的存储机制、索引技巧、锁定水平并且最终提供广泛的、不同的功能。根据选择不同技术(引擎),能够获得额外的速度或者功能,从而改善应用的整体功能。

引擎

主要有: InnoDB、MyIsam 、Mrg_Myisam、Memory、Blackhole、 CSV、Performance_Schema、Archive、Federated 。

支持事务的引擎

MySQLinnoDB,默认为innoDB。

InnoDB

InnoDB是MySQL默认的事务型引擎,也是最重要、最广泛的存储引擎。它的设计是用来处理大量短期事务,短期事务大部分是正常提交的,很少回滚。

特点:

  1. 更新多的表,适合处理多重并发的更新请求。

  2. 支持事务。

  3. 可以恢复数据(通过bin-log日志等)。

  4. 外键约束,只有它支持外键。

  5. 支持自动增加列属性auto_increment。

MyISAM

MySQL在5.1之前,MyISAM是默认的引擎,MyISAM有大量的特心态,包括全文索引、压缩、空间函数。但是MyISAM不支持事务和行级锁,而且在崩溃后无法安全恢复。即使后续版本中MyISAM支持了事务,但是很多人的概念中依然是不支持事务的引擎。

特点:

​ 1、不支持事务

​ 2、不支持外键

​ 3、查询速度很快。

​ 4、对表进行加锁

查看支持的引擎

show engines;

查询某一个数据库中所有表的相关信息,包括引擎信息

show table status from 数据库名;

修改表的引擎

alter table 表名 engine = innodb;

innodb改为myisam错误信息(myisam不支持外键)

显示表结构包括引擎信息

show create table 表名;

ACID标准-事务的四大特性

原子性

原子性 (Atomicity) :数据库事务是不可分割的操作单位。

  • 事务中的所有操作, 要么全部完成, 要么什么也不做。
  • 事务在执行过程中发生错误, 会被回滚 (Rollback) 到事务开始前的状态, 就像这个事务从来没有执行过一样。

一致性

一致性 (Consistency):事务使系统从一个一致的状态转换到另一个一致状态。

事务的一致性决定了一个系统设计和实现的复杂度,也导致了事务的不同隔离级别。

在事务开始之前和事务结束以后, 数据库的完整性约束没有被破坏。

在MySQL中,一致性主要由MySQL的日志机制处理,它记录数据库的所有变化,为事务恢复提供了跟踪记录。如在事务处理中发生错误,Mysql恢复过程将使用这些日志来发现事务是否已经完全执行,是否需要返回。

财务系统例子.

A要向B支付100元,而A的账户中只有90元,并且我们给定账户余额这一列的约束是,不能小于0.那么很明显这条事务执行会失败,因为90-100=-10,小于我们给定的约束了.

这个例子里,支付之前我们数据库里的数据都是符合约束的,但是如果事务执行成功了,我们的数据库数据就破坏约束了,因此事务不能成功,这里我们说事务提供了一致性的保证.然后我们再看个例子

A要向B支付100元,而A的账户中只有90元,我们的账户余额列没有任何约束.但是我们业务上不允许账户余额小于0.因此支付完成后我们会检查A的账户余额,发现余额小于0了,于是我们进行了事务的回滚.

这个例子里,如果事务执行成功,虽然没有破坏数据库的约束,但是破坏了我们应用层的约束.而事务的回滚保证了我们的约束,因此也可以说事务提供了一致性保证(事实上,是我们应用层利用事务回滚保证了我们的约束不被破坏).

A要向B支付100元,而A的账户中只有90元,我们的账户余额列没有任何约束.然后支付成功了.

事务不是保证一致性么?直观上账户余额为什么能为负呢.但这里事务执行前和执行后,我们的系统没有任何的约束被破坏.一直都是保持正确的状态.

综上,可以理解一致性就是:应用系统从一个正确的状态到另一个正确的状态.

隔离性 (Isolation):要求每个读写事务的对象与其他事务的操作对象能相互分离,即一个事务在提交前对其他事务都不可见。(一个事务会不会读取到另一个未提交的事务修改的数据)

隔离性通常使用锁来实现,数据库中提供了一种粒度锁的策略,允许事务仅锁住一个实体对象的子集,以此提高事务之间的并发度。

持久性

持久性 (Durability):事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。

事务的隔离级别

数据库是多线程并发的,容易出现多个线程同时开启事务的情况,这样就会出现重复读、脏读或幻读情况,为了避免这些情况,Mysql中定义了四类隔离级别,用来限定事务内外的哪些改变是可见的,哪些是不可见的 。

低级别的隔离一般支持高级别的并发处理,并拥有更低的系统开销。

脏读:事务A修改了一个数据,但未提交,事务B读到了事务A未提交的更新结果,如果事务A提交失败,事务B读到的就是脏数据。

幻读: 用户读取某一范围的数据行时,另一个事务又在该范围内插入新行,当用户在读该范围的数据行时,就发现数据发生了改变。幻读是由于并发事务增加记录导致的,这不能通过给记录加锁解决,因为对于新增的记录根本无法加锁。需要将事务串行化,才能避免幻读。

不可重复读:在同一个事务中,对于同一份数据读取到的结果不一致。

比如,事务B在事务A提交前读到的结果,和提交后读到的结果可能不同。不可重复读出现的原因就是事务并发修改记录,要避免这种情况,最简单的方法就是对要修改的记录加锁,这会导致锁竞争加剧,影响性能。

未提交读

(READ UNCOMMITTED):

最低的隔离级别,什么都不需要做,一个事务可以读到另一个事务未提交的结果。所有的并发事务问题都会发生。该隔离级别很少用于实际应用,性能不如其他级别好。

已提交读

(READ COMMITTED ):

该隔离级别满足了隔离 的简单定义,即只能看见已经提交事务所做的改变,这可以避免脏读。这种隔离级别支持所有的不可重复读。原因在于同一个事务的其他实例在该实例提交期间可能会有新的事务提交,所以同一查询可能返回不同结果。

可重复读

(REPEATABLE READ ):

可重复读隔离级别,是Mysql的默认事务隔离级别。可确保同一事务的多个实例再并发读取数据时,会看到同样的数据行。此隔离级别只允许读取已经提交的记录,而且在一个事务两次读取一个记录期间保持一致,但是该事务不要求其他事务可串行化。类如,一个事务可以找到由一个已提交事务更新的记录,但是可能产生幻读。Innodb存储引擎通过多版本并发控制机制解决了该问题。

可序列化

(SERIALIZABLE):

又称串行化,该级别是最高的隔离级别。通过强制事务排序,使之不可能相互重读,从而解决幻读、脏读和重复读的问题。它是在每个读的数据行加上共享锁,如果一个事务来查询同一份数据就必须等待,直到前一个事务完成并解除锁定位置。这个级别可能导致大量的超时现象和锁竞争,对数据库查询性能影响较大、所以实际开发中也很少使用。

​ 四种隔离级别可能产生的问题

隔离级别 读数据一致性 脏读 不可重复读 幻读
未提交读 最低级别,只能保证不读取物理上损坏的数据 Y Y Y
已提交读 语句级 N Y Y
可重复读 事务级 N N Y
可序列化 最高级别,事务级 N N N

锁机制

为解决数据库并发控制问题,MySQL中使用了锁机制。如在同一时刻,客户端对于同一个表做更新或查询操作,为了保证数据的一致性,需要对并发操作进行控制。与此同时,为实现Mysql的各个隔离级别,锁机制为其提供安全保障

锁粒度: 尽量只锁定需要修改的部分数据,而不是所有的资源。只对修改的数据片进行精确的锁定。任何时候,在给定的资源上,锁定的数据量越少,则系统的并发程度越高,只要相互之间不发生冲突即可。

锁策略: 就是在锁的开销和数据的安全性之间寻求平衡。

两种重要的锁策略:

  • 表锁(table lock)

    • 表锁管理锁的开销最小,同时允许的并发量也是最小的,MyISAM存储引擎使用的该锁机制。
    • 表锁是Mysql中最基本的锁策略,是开销最小的策略。表锁会锁定整张表。一个用户在对表进行写操作(插入、删除、更新等),需要先获得写锁,这会阻塞其他用户对该表的所有读写操作。特点:开销小,加锁快;不会出现死锁;锁定力度大,发生锁冲突概率高,并发度最低
  • 行级锁(row lock)

    • 行级锁可以最大程度地支持并发处理(同时也带来了最大的锁开销)。
    • 如果要支持并发读写,建议采用InnoDB存储引擎
    • 特点:开销大,加锁慢;会出现死锁;锁定粒度小,发生锁冲突的概率低,并发度高

共享锁

共享锁的锁粒度是行或者多行。一个事务获取了共享锁以后,可以对锁定范围内的数据执行读操作。

排他锁

排他锁的粒度与共享锁相同,也是行或者多行。一个事务获取了排他锁之后,可以对锁定范围内的数据执行写操作。

意向锁

意向锁是一种表锁,锁定的粒度是整张表,分为意向共享锁和意向排他锁两类。

  • 意向表示一个事务有意对数据上共享锁或者排他锁。“有意”表示事务想执行操作但还没真正的执行。
  • 锁a和锁b是同一组数据时:
    • 意向共享锁:如果事务t1获取了锁a,另一个事务t2还可以获取锁b,相容。
    • 意向排他锁:如果事务t1获取了锁a,另一个事务t2在t1被释放之前无法获取锁b,相斥。

​ MySQL锁兼容情况

参数 排他锁 共享锁 意向排他锁 意向共享锁
排他锁 N N N Y
共享锁 N Y N Y
意向排他锁 N N Y Y
意向共享锁 N Y Y Y

死锁的处理

InnoDB存储引擎自动检测事务的死锁,并返回一个或几个事务来防止死锁。InnoDB存储引擎不能在Mysql设定表锁的范围或者涉及InnoDB之外的存储引擎所设置锁定的范围检查死锁。可以通过设置innodb_lock_wait_timeout系统变量的值来解决这些情况。如果要依靠锁等待超时来解决死锁,对于更新事务密集的应用,很可能导致大量的锁等待,导致系统异常。一般不推荐在一个事务中混合更新不同存储类型的表,也不推荐相同类型的表采用不同的锁定方式加锁

事务应用

事务的开始与结束可以有用户显示控制。在MySQL服务器中,显示操作事务的语句主要有:START TRANSACTION、COMMIT和ROLLBACK。

开启/关闭事务自动提交

-- 关闭
set autocommit=0;
-- 开启
set autocommit=1;

启动事务

start transaction;	

begin;

提交事务

Mysql中对象的创建、修改和删除等都会隐式地执行事务的提交

commit;
commit work;

回滚事务

rollback;
rollback work;

事务保存点

除了启动事务,提交事务和回滚事务外,在事务中还可以设置保存点savepoint,可以将处理的事务回滚至保存点。

创建保存点: 允许在事务回滚至保存点,一个事务中可以有多个保存点

savepoint identifier;

删除保存点: 当没有一个保存点时执行此语句会抛异常。

release savepoint identifier;

把事务回滚到保存点: 给出保存点,可以把事务回滚到指定的保存点。如果不给出保存点,则回滚到启动事务之前的状态。

rollback to identifier;

查询事务的隔离级别

show variables like '%isolation%';

image-20200825184617915

查看各种隔离级别

select @@global.tx_isolation,@@session.tx_isolation,@@tx_isolation;

image-20200826074427640

设置事务的隔离级别

SET [SESSION | GLOBAL] TRANSACTION ISOLATION LEVEL {READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ SERIALIZABLE}

修改隔离级别

set session transaction isolation level read uncommitted;

image-20200826074706275

事务日志

MySQL中,InnoDB存储引擎引入了与事务相关REDO和UNDO日志。

REDO日志

​ 事务执行时需要将执行的事务日志写到日志文件中,对应的文件为REDO日志。当每条SQL进行数据库更新操作时,首先将REDO日志写入到日志缓存区。当客户端执行COMMIT命令提交时,日志缓存区的内容将刷新到磁盘,日志缓冲区的刷新方式和时间间隔可以通过参数innodb_flush_log_at_trx_commit控制。

​ REDO日志对应磁盘上的ib_logfileN文件,该文件默认为5MB,建议设置为512MB,以便容纳较大的事务。在MySQL崩溃恢复时会重新执行REDO日志的记录。

UNDO日志

​ 与REDO日志相反,UNDO日志主要用于事务异常时的数据回滚,具体内容就是复制事务前的数据库内容到UNDO缓冲区,然后再合适的时间将内容刷新到磁盘。

​ 与REDO日志不同的是,磁盘不存在单独的UNDO日志文件,所有的UNDO日志均存放在表空间对应的.ibd数据文件中。

posted @ 2020-08-26 08:31  侬&码  阅读(128)  评论(0编辑  收藏  举报