文章目录

🎉数据库的事务四大特性(ACID)以及隔离性

一、事务的四大特性✨

1、原子性(Atomicity)🎊

  原子性是指事务包含的所有操作要么全部成功,要么全部失败回滚。因此事务的操作如果成功就必须完全应用到数据库,如果操作失败不能对数据库有任何影响。
  例子:考虑银行转账操作,从一个账户扣除一定金额并存入另一个账户。如果扣除成功但存入失败,需要回滚,保持操作的原子性。

2、一致性(Consistency)🎊

  一致性要求事务执行前后数据库的状态保持一致。事务执行过程中可能涉及多个操作,这些操作的结果必须满足数据库的约束和规则。
  例子:在购物网站上进行支付操作,支付前后库存、账户余额等信息必须保持一致,否则支付过程可能导致数据不一致。
  🪄例如:那转账来说,假设用户A和用户B两者的钱加起来是5000,那么不管A和B之间如何转账,转几次账,事务结束后两个用户的钱加起来应该还得是5000,这就是事务的一致性。

3、隔离性(Isolation)🎊

  隔离性是指当多个用户并发访问数据库时,比如操作同一张表时,数据库为每个用户开启的事务,不能被其他事务的操作干扰,多个并发事务要互相隔离。

  即要达到一种效果:对于任意两个并发的事务T1和T2,在事务T1看来,T2要么在T1开始之前就已经结束,要么在T1结束之后开始,这样每个事务都感觉不到有其他的事务在并发执行。

  关于事务的隔离性又提供了多种隔离级别,如读已提交、读未提交、可重复读、串行化,稍后会讲到。

4、持久性(Durability)🎊

  持久性是指一个事务一旦被提交了,那么对数据库中的数据的改变是永久的,即便是在数据库系统中遇到故障的情况下也不会丢失提交事务的操作。

  例如我们在使用JSDC操作数据库时,在提交事务后,提示用户事务操作完成,当我们程序执行完成直到看到提示后,就可以认定事务已经正确提交,即使数据库出现了问题,也必须要将我们的事务完全执行完成,否则会造成我们看到提示事务处理完毕,但是数据库因为故障而没有执行事务的重大错误。
  例子:用户在博客平台上发表文章,一旦用户点击发布并事务提交,该文章的修改应该是永久性的,即使系统在发布过程中发生了故障。

二、详解事务的隔离性✨

  以上介绍完事务的四大特性(简称ACID),现在重点来说明一下事务的隔离性,当多个线程都开启事务的操作数据库中的数据,数据库系统要能进行隔离操作,以保证各个线程获取数据的准确性,在介绍数据库提供的各种隔离级别之前,我们先看看如果不考虑事务的隔离性,会发生哪些问题:

1、脏读🔮

  脏读是指在一个事务处理过程里读取了另一个未提交事务中的数据。

  当一个事务正在多次修改某个数据,而在这个事务中这多次的修改都还未提交,这时一个并发的事务来访问该数据,就会造成两个事务得到的数据不一致。例如:用户A向用户B转账100元,对应SQL命令如下

update account set money=money+100 where name=’B’;  (此时A通知B)

update account set money=money - 100 where name=’A’;

  当只执行第一条SQL时,A通知B查看账户,B发现确实钱已到账(此时即发生了脏读),而之后无论第二条SQL是否执行,只要该事务不提交,则所有操作都将回滚,那么当B以后再次查看账户时就会发现钱其实并没有转。

2、不可重复读🔮

不可重复读是指在对于数据库中的某个数据,一个事务范围内多次查询却返回了不同的数据值,这是由于在查询间隔,被另一个事务修改并提交了。

例如事务T1在读取某一数据,而事务T2立马修改了这个数据并且提交事务给数据库,事务T1再次读取该数据就得到了不同的结果,发送了不可重复读。

不可重复读和脏读的区别是,脏读是某一事务读取了另一个事务未提交的脏数据,而不可重复读则是读取了前一事务提交的数据。

在某些情况下,不可重复读并不是问题,比如我们多次查询某个数据当然以最后查询得到的结果为主。但在另一些情况下就有可能发生问题,例如对于同一个数据A和B依次查询就可能不同,A和B就可能打起来了……

3、虚读(幻读)🔮

幻读是事务非独立执行时发生的一种现象。例如事务T1对一个表中所有的行的某个数据项做了从“1”修改为“2”的操作,这时事务T2又对这个表中插入了一行数据项,而这个数据项的数值还是为“1”并且提交给数据库。而操作事务T1的用户如果再查看刚刚修改的数据,会发现还有一行没有修改,其实这行是从事务T2中添加的,就好像产生幻觉一样,这就是发生了幻读。

幻读和不可重复读都是读取了另一条已经提交的事务(这点就脏读不同),所不同的是不可重复读查询的都是同一个数据项,而幻读针对的是一批数据整体(比如数据的个数)。

4、隔离级别🔮

现在来看看MySQL数据库为我们提供的四种隔离级别(从低到高排序):

1️⃣ Read uncommitted (读未提交):最低级别,任何情况都无法保证。

  第一隔离级别怕回滚,因为它能读取到别的事务未提交的数据。如果当前事务将这个数据当成真正的数据,那么如果别的事务回滚,那么就会数据错误,导致脏读。

2️⃣ Read committed (读已提交):可避免脏读的发生。

第二个隔离级别怕提交,因为提交会导致不可重复读。

3️⃣ Repeatable read (可重复读):可避免脏读、不可重复读的发生。*MySQL默认隔离级别
4️⃣ Serializable (串行化):可避免脏读、不可重复读、幻读的发生。

三、怎样算是一个事务🪄

1、从第一次执行SQL语句开始到执行commit或者rollback结束。中间不管执行多少SQL语句都是同一个事务内。

2、事务的开始与结束

start transaction; # 开启事务
commit;或者rollback; # 事务结束

3、数据库如何实现事务的

  服务端有日志记录功能,新操作是记录到日志中的,commit会将日志中的记录刷到硬盘中,rollback就是将日志中的记录删除掉了

四、常用操作🌈-----记住:设置数据库的隔离级别一定要是在开启事务之前!

1、查看提交状态命令

show variables like '%auto%';

2、关闭自动提交

set autocommit = 0;

3、查看隔离级别

select @@transaction_isolation; # 8.x版本数据库系统
show variables like 'transaction_isolation'; # 8.x版本数据库系统
SELECT @@tx_isolatione 5.x

4、修改隔离级别

set session transaction isolation level ; # 8.x
set session transaction isolation ; # 5.x

5、开启事务-----只要执行一条SQL语句就是开启事务,不一定非要使用start transaction;

start transaction;

6、结束事务

commit;或者
rollback;

五、数据库事务怎么做✨

以上四种隔离级别最高的是Serializable级别,最低的是Read uncommitted级别,当然级别越高,执行效率就越低。像Serializable这样的级别,就是以锁表的方式(类似于Java多线程中的锁)使得其他的线程只能在锁外等待,所以平时选用何种隔离级别应该根据实际情况。在MySQL数据库中默认的隔离级别为Repeatable read (可重复读)。

在MySQL数据库中,支持上面四种隔离级别,默认的为Repeatable read (可重复读);而在Oracle数据库中,只支持Serializable (串行化)级别和Read committed (读已提交)这两种级别,其中默认的为Read committed级别。

1、如何进行事务🏆

1️⃣ 建立引擎支持InnoDB,InnoDB支持数据库事务操作
2️⃣ 设置autocommit为false
show variables like '%auto%'; # 展示事务提交信息
set autocommit=0; # 设置autocommit为false
set autocommit=1; # 设置autocommit为true
3️⃣ 修改隔离级别
select @@transaction_isolation; # 查看隔离级别
set session transaction isolation level read uncommitted; # 设置隔离级别,此时设置为读未提交
4️⃣ 开启事务
5️⃣ 执行SQL语句
select * from employee;
update employee set sal=100 where empno=1022;
6️⃣ commit或者rollback(回滚)
commit;
rollback;

2、对Read Uncommitted级别进行操作

1️⃣ 脏读:两次数据

在这里插入图片描述

3、对Read Committed级别进行操作

2️⃣ 不可重复读-----此时避免了脏读,但是还存在不可重复读的问题,看此标题下第二张图

在这里插入图片描述

在这里插入图片描述

因为两个事务对同一个数据进行操作违反了隔离性,死锁。

在这里插入图片描述

对不同数据操作不报错

在这里插入图片描述

4、repeatable read隔离级别

3️⃣ 解决了脏读、不可重复读

在这里插入图片描述

记住:设置数据库的隔离级别一定要是在开启事务之前!

如果是使用JDBC对数据库的事务设置隔离级别的话,也应该是在调用Connection对象的setAutoCommit(false)方法之前。调用Connection对象的setTransactionIsolation(level)即可设置当前链接的隔离级别,至于参数level,可以使用Connection对象的字段:

在这里插入图片描述

在JDBC中设置隔离级别的部分代码:

在这里插入图片描述

后记:隔离级别的设置只对当前链接有效。对于使用MySQL命令窗口而言,一个窗口就相当于一个链接,当前窗口设置的隔离级别只对当前窗口中的事务有效;对于JDBC操作数据库来说,一个Connection对象相当于一个链接,而对于Connection对象设置的隔离级别只对该Connection对象有效,与其他链接Connection对象无关。

六、锁🔒

1、代码层次的乐观锁、删除锁

在代码层次,乐观锁是一种在数据读取时不加锁的并发控制机制。它假设多个事务在同一时间对同一数据进行操作的可能性较小,因此只在更新数据时进行版本检查,以防止数据被其他事务同时修改。在MySQL中,可以使用版本号或时间戳等机制来实现乐观锁。例如,在更新数据时,可以检查该数据的版本号是否与最初读取时一致,如果不一致,则表示有其他事务修改了该数据,此时可以选择回滚或重试等操作。

2、数据库层次的悲观锁:

数据库层次,悲观锁是一种在数据读取时加锁的并发控制机制。它假设多个事务在同一时间对同一数据进行操作的可能性较大,因此每次读取数据时都会对相应的数据进行加锁,以防止其他事务同时修改该数据。
在MySQL中,可以使用SELECT … FOR UPDATESELECT … LOCK IN SHARE MODE等语句来实现悲观锁。
例如,使用SELECT … FOR UPDATE语句时,会对选定的行进行加锁,直到事务结束时才释放锁。在此期间,其他事务无法对该行进行修改。

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐