数据库的事务机制是什么

数据库的事务机制是什么,第1张

回答的有点多请耐心看完。

希望能帮助你还请及时采纳谢谢

1事务的原理

事务就是将一组SQL语句放在同一批次内去执行,如果一个SQL语句出错,则该批次内的所有SQL都将被取消执行。MySQL事务处理只支持InnoDB和BDB数据表类型。

1事务的ACID原则

** 1(Atomicity)原子性**: 事务是最小的执行单位,不允许分割。原子性确保动作要么全部完成,要么完全不起作用;

2(Consistency)一致性: 执行事务前后,数据保持一致;

3(Isolation)隔离性: 并发访问数据库时,一个事务不被其他事务所干扰。

4(Durability)持久性: 一个事务被提交之后。对数据库中数据的改变是持久的,即使数据库发生故障。

1缓冲池(Buffer Pool)

Buffer Pool中包含了磁盘中部分数据页的映射。当从数据库读取数据时,会先从Buffer Pool中读取数据,如果Buffer Pool中没有,则从磁盘读取后放入到Buffer Pool中。当向数据库写入数据时,会先写入到Buffer Pool中,Buffer Pool中更新的数据会定期刷新到磁盘中(此过程称为刷脏)。

2日志缓冲区(Log Buffer)

当在MySQL中对InnoDB表进行更改时,这些更改命令首先存储在InnoDB日志缓冲区(Log Buffer)的内存中,然后写入通常称为重做日志(redo logs)的InnoDB日志文件中。

3双写机制缓存(DoubleWrite Buffer)

Doublewrite Buffer是共享表空间的物理文件的 buffer,其大小是2MB.是一个一分为二的2MB空间。

刷脏 *** 作开始之时,先进行脏页**‘备份’** *** 作.将脏页数据写入 Doublewrite Buffer.

将Doublewrite Buffer(顺序IO)写入磁盘文件中(共享表空间) 进行刷脏 *** 作.

4回滚日志(Undo Log)

Undo Log记录的是逻辑日志.记录的是事务过程中每条数据的变化版本和情况.

在Innodb 磁盘架构中Undo Log 默认是共享表空间的物理文件的Buffer.

在事务异常中断,或者主动(Rollback)回滚的过程中 ,Innodb基于 Undo Log进行数据撤销回滚,保证数据回归至事务开始状态.

5重做日志(Redo Log)

Redo Log通常指的是物理日志,记录的是数据页的物理修改.并不记录行记录情况。(也就是只记录要做哪些修改,并不记录修改的完成情况) 当数据库宕机重启的时候,会将重做日志中的内容恢复到数据库中。

1原子性

Innodb事务的原子性保证,包含事务的提交机制和事务的回滚机制.在Innodb引擎中事务的回滚机制是依托 回滚日志(Undo Log) 进行回滚数据,保证数据回归至事务开始状态.

2那么不同的隔离级别,隔离性是如何实现的,为什么不同事物间能够互不干扰? 答案是 锁 和 MVCC。

3持久性

基于事务的提交机制流程有可能出现三种场景.

1 数据刷脏正常.一切正常提交,Redo Log 循环记录.数据成功落盘.持久性得以保证

2数据刷脏的过程中出现的系统意外导致页断裂现象 (部分刷脏成功),针对页断裂情况,采用Double write机制进行保证页断裂数据的恢复.

3数据未出现页断裂现象,也没有刷脏成功,MySQL通过Redo Log 进行数据的持久化即可

4一致性

从数据库层面,数据库通过原子性、隔离性、持久性来保证一致性

2事务的隔离级别

Mysql 默认采用的 REPEATABLE_READ隔离级别 Oracle 默认采用的 READ_COMMITTED隔离级别

脏读: 指一个事务读取了另外一个事务未提交的数据。

不可重复读: 在一个事务内读取表中的某一行数据,多次读取结果不同

虚读(幻读): 是指在一个事务内读取到了别的事务插入的数据,导致前后读取不一致。

2基本语法

-- 使用set语句来改变自动提交模式

SET autocommit = 0 /*关闭*/

SET autocommit = 1 /*开启*/

-- 注意:

--- 1.MySQL中默认是自动提交

--- 2.使用事务时应先关闭自动提交

-- 开始一个事务,标记事务的起始点

START TRANSACTION

-- 提交一个事务给数据库

COMMIT

-- 将事务回滚,数据回到本次事务的初始状态

ROLLBACK

-- 还原MySQL数据库的自动提交

SET autocommit =1

-- 保存点

SAVEPOINT 保存点名称 -- 设置一个事务保存点

ROLLBACK TO SAVEPOINT 保存点名称 -- 回滚到保存点

RELEASE SAVEPOINT 保存点名称 -- 删除保存点

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

/*

课堂测试题目

A在线买一款价格为500元商品,网上银行转账.

A的yhk余额为2000,然后给商家B支付500.

商家B一开始的yhk余额为10000

创建数据库shop和创建表account并插入2条数据

*/

CREATE DATABASE `shop`CHARACTER SET utf8 COLLATE utf8_general_ci

USE `shop`

CREATE TABLE `account` (

`id` INT(11) NOT NULL AUTO_INCREMENT,

`name` VARCHAR(32) NOT NULL,

`cash` DECIMAL(9,2) NOT NULL,

PRIMARY KEY (`id`)

) ENGINE=INNODB DEFAULT CHARSET=utf8

INSERT INTO account (`name`,`cash`)

VALUES('A',2000.00),('B',10000.00)

-- 转账实现

SET autocommit = 0-- 关闭自动提交

START TRANSACTION -- 开始一个事务,标记事务的起始点

UPDATE account SET cash=cash-500 WHERE `name`='A'

UPDATE account SET cash=cash+500 WHERE `name`='B'

COMMIT-- 提交事务

# rollback

SET autocommit = 1-- 恢复自动提交

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

3事务实现方式-MVCC

1什么是MVCC

MVCC是mysql的的多版本并发控制即multi-Version Concurrency Controller,mysql的innodb引擎支持MVVC。MVCC是为了实现事务的隔离性,通过版本号,避免同一数据在不同事务间的竞争,你可以把它当成基于多版本号的一种乐观锁。当然,这种乐观锁只在事务级别为RR(可重复读)和RC(读提交)生效。MVCC最大的好处,相信也是耳熟能详:读不加锁,读写不冲突,极大的增加了系统的并发性能。

2MVCC的实现机制

InnoDB在每行数据都增加两个隐藏字段,一个记录创建的版本号,一个记录删除的版本号。

在多版本并发控制中,为了保证数据 *** 作在多线程过程中,保证事务隔离的机制,降低锁竞争的压力,保证较高的并发量。在每开启一个事务时,会生成一个事务的版本号,被 *** 作的数据会生成一条新的数据行(临时),但是在提交前对其他事务是不可见的;对于数据的更新(包括增删改) *** 作成功,会将这个版本号更新到数据的行中;事务提交成功,新的版本号也就更新到了此数据行中。这样保证了每个事务 *** 作的数据,都是互不影响的,也不存在锁的问题。

3MVCC下的CRUD

SELECT:

当隔离级别是REPEATABLE READ时select *** 作,InnoDB每行数据来保证它符合两个条件:

** 1 事务的版本号 大于等于 创建行版本号**

  ** 2 行数据的删除版本 未定义 或者大于 事务版本号**

  【行创建版本号 事务版本号 行删除版本号】

 

INSERT:

InnoDB为这个新行 记录 当前的系统版本号。

DELETE:

InnoDB将当前的系统版本号 设置为 这一行的删除版本号。

UPDATE:

InnoDB会写一个这行数据的新拷贝,这个拷贝的版本为 当前的系统版本号。它同时也会将这个版本号 写到 旧行的删除版本里。

————————————————

版权声明:本文为CSDN博主「@Autowire」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

原文链接:https://blog.csdn.net/zs18753479279/article/details/113933252

当多个用户访问同一份数据时,一个用户在更改数据的过程中,可能有其他用户同时发起更改请求,为保证数据库记录的更新从一个一致性状态变为另外一个一致性状态,使用事务处理是非常必要的,事务具有以下四个特性:

MySQL 提供了多种事务型存储引擎,如 InnoDB 和 BDB 等,而 MyISAM 不支持事务。为了支持事务,InnoDB 存储引擎引入了与事务处理相关的 REDO 日志和 UNDO 日志,同时事务依赖于 MySQL 提供的锁机制

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

REDO 日志对应磁盘上的 ib_logifleN 文件,该文件默认为 5MB,建议设置为 512MB,以便容纳较大的事务。MySQL 崩溃恢复时会重新执行 REDO 日志的记录,恢复最新数据,保证已提交事务的持久性

与 REDO 日志相反,UNDO 日志主要用于事务异常时的数据回滚,具体内容就是记录数据被修改前的信息到 UNDO 缓冲区,然后在合适的时间将内容刷新到磁盘

假如由于系统错误或者 rollback *** 作而导致事务回滚,可以根据 undo 日志回滚到没修改前的状态,保证未提交事务的原子性

与 REDO 日志不同的是,磁盘上不存在单独的 UNDO 日志文件,所有的 UNDO 日志均存在表空间对应的 .ibd 数据文件中,即使 MySQL 服务启动了独立表空间

在 MySQL 中,可以使用 BEGIN 开始事务,使用 COMMIT 结束事务,中间可以使用 ROLLBACK 回滚事务。MySQL 通过 SET AUTOCOMMIT、START TRANSACTION、COMMIT 和 ROLLBACK 等语句支持本地事务

MySQL 定义了四种隔离级别,指定事务中哪些数据改变其他事务可见、哪些数据该表其他事务不可见。低级别的隔离级别可以支持更高的并发处理,同时占用的系统资源更少

InnoDB 系统级事务隔离级别可以使用以下语句设置:

查看系统级事务隔离级别:

InnoDB 会话级事务隔离级别可以使用以下语句设置:

查看会话级事务隔离级别:

在该隔离级别,所有事务都可以看到其他未提交事务的执行结果。读取未提交的数据称为脏读(Dirty Read),即是:首先开启 A 和 B 两个事务,在 B 事务更新但未提交之前,A 事务读取到了更新后的数据,但由于 B 事务回滚,导致 A 事务出现了脏读现象

所有事务只能看见已经提交事务所做的改变,此级别可以解决脏读,但也会导致不可重复读(Nonrepeatable Read):首先开启 A 和 B 两个事务,A事务读取了 B 事务的数据,在 B 事务更新并提交后,A 事务又读取到了更新后的数据,此时就出现了同一 A 事务中的查询出现了不同的查询结果

MySQL 默认的事务隔离级别,能确保同一事务的多个实例在并发读取数据时看到同样的数据行,理论上会导致一个问题,幻读(Phontom Read)。例如,第一个事务对一个表中的数据做了修改,这种修改会涉及表中的全部数据行,同时第二个事务也修改这个表中的数据,这次的修改是向表中插入一行新数据,此时就会发生 *** 作第一个事务的用户发现表中还有没有修改的数据行

InnoDB 通过多版本并发控制机制(MVCC)解决了该问题:InnoDB 通过为每个数据行增加两个隐含值的方式来实现,这两个隐含值记录了行的创建时间、过期时间以及每一行存储时间发生时的系统版本号,每个查询根据事务的版本号来查询结果

通过强制事务排序,使其不可能相互冲突,从而解决幻读问题。简而言之,就是在每个读的数据行上加上共享锁实现,这个级别会导致大量的超时现象和锁竞争,一般不推荐使用

为了解决数据库并发控制问题,如走到同一时刻客户端对同一张表做更新或者查询 *** 作,需要对并发 *** 作进行控制,因此产生了锁

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

排他锁的粒度与共享锁相同,一个事务获取排他锁以后,可以对锁定范围内的数据执行写 *** 作

有两个事务 A 和 B,如果事务 A 获取了一个元组的共享锁,事务 B 还可以立即获取这个元组的共享锁,但不能获取这个元组的排他锁,必须等到事务 A 释放共享锁之后。如果事务 A 获取了一个元组的排他锁,事务 B 不能立即获取这个元组的共享锁,也不能立即获取这个元组的排他锁,必须等到 A 释放排他锁之后

意向锁是一种表锁,锁定的粒度是整张表,分为意向共享锁和意向排他锁。意向共享锁表示一个事务有意对数据上共享锁或者排他锁。有意表示事务想执行 *** 作但还没真正执行

锁的粒度主要分为表锁和行锁

表锁的开销最小,同时允许的并发量也是最小。MyISAM 存储引擎使用该锁机制。当要写入数据时,整个表记录被锁,此时其他读/写动作一律等待。一些特定的动作,如 ALTER TABLE 执行时使用的也是表锁

行锁可以支持最大的并发,InnoDB 存储引擎使用该锁机制。如果要支持并发读/写,建议采用 InnoDB 存储引擎

分别是原子性、一致性、隔离性、持久性。

原子性是指事务包含的所有 *** 作要么全部成功,要么全部失败回滚,因此事务的 *** 作如果成功就必须要完全应用到数据库,如果 *** 作失败则不能对数据库有任何影响。

一致性是指事务必须使数据库从一个一致性状态变换到另一个一致性状态,也就是说一个事务执行之前和执行之后都必须处于一致性状态。举例来说,假设用户A和用户B两者的钱加起来一共是1000,那么不管A和B之间如何转账、转几次账,事务结束后两个用户的钱相加起来应该还得是1000,这就是事务的一致性。

隔离性是当多个用户并发访问数据库时,比如同时 *** 作同一张表时,数据库为每一个用户开启的事务,不能被其他事务的 *** 作所干扰,多个并发事务之间要相互隔离。关于事务的隔离性数据库提供了多种隔离级别,稍后会介绍到。

持久性是指一个事务一旦被提交了,那么对数据库中的数据的改变就是永久性的,即便是在数据库系统遇到故障的情况下也不会丢失提交事务的 *** 作。例如我们在使用JDBC *** 作数据库时,在提交事务方法后,提示用户事务 *** 作完成,当我们程序执行完成直到看到提示后,就可以认定事务已经正确提交,即使这时候数据库出现了问题,也必须要将我们的事务完全执行完成。否则的话就会造成我们虽然看到提示事务处理完毕,但是数据库因为故障而没有执行事务的重大错误。这是不允许的。

在数据库 *** 作中,在并发的情况下可能出现如下问题:

正是为了解决以上情况,数据库提供了几种隔离级别。

数据库事务的隔离级别有4个,由低到高依次为Read uncommitted(未授权读取、读未提交)、Read committed(授权读取、读提交)、Repeatable read(可重复读取)、Serializable(序列化),这四个级别可以逐个解决脏读、不可重复读、幻象读这几类问题。

虽然数据库的隔离级别可以解决大多数问题,但是灵活度较差,为此又提出了悲观锁和乐观锁的概念。

悲观锁,它指的是对数据被外界(包括本系统当前的其他事务,以及来自外部系统的事务处理)修改持保守态度。因此,在整个数据处理过程中,将数据处于锁定状态。悲观锁的实现,往往依靠数据库提供的锁机制。也只有数据库层提供的锁机制才能真正保证数据访问的排他性,否则,即使在本系统的数据访问层中实现了加锁机制,也无法保证外部系统不会修改数据。

商品t_items表中有一个字段status,status为1代表商品未被下单,status为2代表商品已经被下单(此时该商品无法再次下单),那么我们对某个商品下单时必须确保该商品status为1。假设商品的id为1。

如果不采用锁,那么 *** 作方法如下:

但是上面这种场景在高并发访问的情况下很可能会出现问题。例如当第一步 *** 作中,查询出来的商品status为1。但是当我们执行第三步Update *** 作的时候,有可能出现其他人先一步对商品下单把t_items中的status修改为2了,但是我们并不知道数据已经被修改了,这样就可能造成同一个商品被下单2次,使得数据不一致。所以说这种方式是不安全的。

在上面的场景中,商品信息从查询出来到修改,中间有一个处理订单的过程,使用悲观锁的原理就是,当我们在查询出t_items信息后就把当前的数据锁定,直到我们修改完毕后再解锁。那么在这个过程中,因为t_items被锁定了,就不会出现有第三者来对其进行修改了。需要注意的是,要使用悲观锁,我们必须关闭mysql数据库的自动提交属性,因为MySQL默认使用autocommit模式,也就是说,当你执行一个更新 *** 作后,MySQL会立刻将结果进行提交。我们可以使用命令设置MySQL为非autocommit模式: set autocommit=0

设置完autocommit后,我们就可以执行我们的正常业务了。具体如下:

上面的begin/commit为事务的开始和结束,因为在前一步我们关闭了mysql的autocommit,所以需要手动控制事务的提交。

上面的第一步我们执行了一次查询 *** 作: select status from t_items where id=1 for update 与普通查询不一样的是,我们使用了 select…for update 的方式,这样就通过数据库实现了悲观锁。此时在t_items表中,id为1的那条数据就被我们锁定了,其它的事务必须等本次事务提交之后才能执行。这样我们可以保证当前的数据不会被其它事务修改。需要注意的是,在事务中,只有 SELECT ... FOR UPDATE 或 LOCK IN SHARE MODE *** 作同一个数据时才会等待其它事务结束后才执行,一般 SELECT ... 则不受此影响。拿上面的实例来说,当我执行 select status from t_items where id=1 for update 后。我在另外的事务中如果再次执行 select status from t_items where id=1 for update 则第二个事务会一直等待第一个事务的提交,此时第二个查询处于阻塞的状态,但是如果我是在第二个事务中执行 select status from t_items where id=1 则能正常查询出数据,不会受第一个事务的影响。

使用 select…for update 会把数据给锁住,不过我们需要注意一些锁的级别,MySQL InnoDB默认Row-Level Lock,所以只有「明确」地指定主键或者索引,MySQL 才会执行Row lock (只锁住被选取的数据) ,否则MySQL 将会执行Table Lock (将整个数据表单给锁住)。举例如下:

1、 select * from t_items where id=1 for update

这条语句明确指定主键(id=1),并且有此数据(id=1的数据存在),则采用row lock。只锁定当前这条数据。

2、 select * from t_items where id=3 for update

这条语句明确指定主键,但是却查无此数据,此时不会产生lock(没有元数据,又去lock谁呢?)。

3、 select * from t_items where name='手机' for update

这条语句没有指定数据的主键,那么此时产生table lock,即在当前事务提交前整张数据表的所有字段将无法被查询。

4、 select * from t_items where id>0 for update 或者 select * from t_items where id>1 for update (注:>在SQL中表示不等于)

上述两条语句的主键都不明确,也会产生table lock。

5、 select * from t_items where status=1 for update (假设为status字段添加了索引)

这条语句明确指定了索引,并且有此数据,则产生row lock。

6、 select * from t_items where status=3 for update (假设为status字段添加了索引)

这条语句明确指定索引,但是根据索引查无此数据,也就不会产生lock。

乐观锁( Optimistic Locking ) 相对悲观锁而言,乐观锁假设认为数据一般情况下不会造成冲突,所以只会在数据进行提交更新的时候,才会正式对数据的冲突与否进行检测,如果发现冲突了,则返回用户错误的信息,让用户决定如何去做。实现乐观锁一般来说有以下2种方式:


欢迎分享,转载请注明来源:内存溢出

原文地址:https://www.54852.com/zaji/8789152.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2023-04-21
下一篇2023-04-21

发表评论

登录后才能评论

评论列表(0条)

    保存