MySQL数据库——事务

2023-12-27 20:23:56

1. 事务概述

事务是一个在数据库系统中执行的一系列操作的集合,这些操作被看作是一个不可分割的工作单位。事务的主要目的是确保数据的完整性和一致性。

在事务中,所有操作要么全部成功完成,要么全部不发生。也就是说,如果事务中的任何部分失败,那么整个事务就会被回滚到开始之前的状态,就像这些操作从未发生过一样。这确保了数据的一致性,防止了因部分操作成功而另一部分失败导致的数据不一致问题。

例如,在银行转账过程中,从一个账户扣除金额并添加到另一个账户的操作就应该在一个事务中进行。如果扣款成功但加款失败,事务会回滚,确保不会出现一个账户钱少了而另一个账户钱没多的情况。

从事务的角度来说,会有一下步骤:

  1. 开启事务:在执行转账操作之前,先开启一个新的事务。
  2. 执行操作:依次执行减少张三账户1000元和增加李四账户1000元的操作。
  3. 提交事务:如果上述两个操作都成功执行,那么就提交事务,使得这两个操作对数据库的更改永久生效。
  4. 回滚事务:如果在执行过程中遇到任何错误或异常,导致其中一个或两个操作未能成功完成,那么就会回滚事务,撤销已经执行的操作,恢复到事务开始前的数据状态。

默认情况下,MySQL的事务是自动提交的,这意味着每执行完一条DML语句(如INSERT、UPDATE、DELETE等),MySQL会立即隐式地提交事务。但在需要保证事务完整性的场景下,通常需要手动控制事务的开始和结束,以确保数据的一致性和完整性。


2. 事务操作

数据准备:

drop table if exists account;

create table account(
id int primary key AUTO_INCREMENT comment 'ID',
name varchar(10) comment '姓名',
money double(10,2) comment '余额'
) comment '账户表';

insert into account(name, money) VALUES ('张三',2000), ('李四',2000);


2.1. 未控制事务

  1. 正常情况:
-- 1. 查询张三余额
select * from account where name = '张三';

-- 2. 张三的余额减少1000
update account set money = money - 1000 where name = '张三';

-- 3. 李四的余额增加1000
update account set money = money + 1000 where name = '李四';

三条sql同时执行:

刷新数据库,表数据发生改变:


  1. 异常情况
-- 1. 查询张三余额
select * from account where name = '张三';

-- 2. 张三的余额减少1000
update account set money = money - 1000 where name = '张三';

我是异常sql语句...
-- 3. 李四的余额增加1000
update account set money = money + 1000 where name = '李四';


2.2. 控制事务方式一

方式一:通过设置autocommit的值来自动或者手动指定事务

  • 查看/设置事务提交方式

autocommit = 1 表示自动提交,autocommit = 0 表示手动提交

MySQL默认情况下是autocommit = 1 自动提交

SELECT @@autocommit ;
SET @@autocommit = 0 ;

  • 提交事务
COMMIT;

  • 回滚事务
ROLLBACK;

示例:

先把事务提交方式改为手动提交

此时执行语句后,刷新数据库,数据不会发生变化

只有手动提交事务才能更新数据

如果此时出现了异常,需要回滚事务,撤销之前的事务操作,则需要执行rollback操作

此时数据没更新是因为autocommit = 0 手动提交,现还处于一个事物步骤内

只是发生了事物操作异常,现在就需要对事物进行回滚

执行rollback语句后,事务操作完成,数据表里面的数据不会发生变化,保证数据的一致性和完整性


2.3. 事务控制方式二

方式二:通过设置 START TRANSACTION 或 BEGIN 来执行事务

  • 开启事务
START TRANSACTION 或 BEGIN ;

  • 提交事务
COMMIT;

  • 回滚事务
ROLLBACK;

示例:

因为设有start transaction,即使现在autocommit = 1 表示自动提交数据库的数据也不会发生变化

只有手动commit提交数据库才会发生变化,现在有事务异常需要rollback回滚事务

如果正常执行完毕, 则提交事务


3. *事务四大特性

事务的四大特性,简称ACID,是数据库系统中确保数据完整性和一致性的核心原则。

  1. 原子性(Atomicity)
    原子性意味着事务是一个不可分割的最小操作单位。在事务中的所有操作被视为一个单一的操作,要么全部成功执行,要么全部不执行。如果事务中的任何部分失败,那么整个事务将被回滚到开始之前的状态,就像这些操作从未发生过一样。这确保了数据的一致性,防止了因部分操作成功而另一部分失败导致的数据不一致问题。
  2. 一致性(Consistency)
    一致性保证了事务在执行前后,数据库都必须处于一致的状态。这意味着事务的执行不能破坏数据库的完整性约束,如主键唯一、外键引用等。事务完成后,所有的数据规则和业务规则都必须得到满足。例如,在银行转账的例子中,转账前后,两个账户的总金额应该是相同的。
  3. 隔离性(Isolation)
    隔离性是指在并发环境中,每个事务都应该与其他事务隔离执行,以防止数据之间的相互干扰。数据库系统通过不同的隔离级别来实现这一特性,确保在一个事务中看到的数据不会被其他未提交事务的影响。这样可以避免脏读(读取到未提交的数据)、不可重复读(在同一事务中多次读取同一数据返回不同结果)和幻读(在同一事务中多次执行相同查询返回不同数量的行)等问题。
  4. 持久性(Durability)
    持久性意味着一旦事务成功提交,其对数据库的改变必须是永久的,即使在系统发生故障的情况下,已经提交的事务结果也不会丢失。为了保证持久性,数据库系统通常会将事务的更改记录在非易失性存储(如磁盘)中,即使在电力中断或系统崩溃后也能恢复事务的更改。

总的来说,ACID特性是数据库系统提供的一种机制,用于管理和控制对数据库的修改,以确保数据的准确性和可靠性,尤其是在并发环境下。通过遵循这些特性,数据库系统能够保证事务的执行不会导致数据的不一致或错误。


4. *并发事务

4.1. 并发事务问题

在MySQL中,当多个事务同时执行时,可能会遇到以下几种并发事务问题:

  1. 脏读(Dirty Read)
    一个事物读取数据时读到了另外一个还没有及时提交的事务的数据。假设有两个事务T1和T2。T1修改了一行数据但尚未提交,而T2在这个时候读取了这行被修改但未提交的数据。如果随后T1决定回滚其更改,那么T2读取的数据就是无效的或“脏”的。
    例如,在银行转账场景中,T1开始一个事务,将A账户减去100元并添加到B账户,但此时事务还未提交。T2开始另一个事务,查询A账户的余额,看到已经被减去的100元。如果T1事务随后因为某种原因被回滚,那么T2读取的A账户余额就是错误的。

  1. 不可重复读(Non-Repeatable Read)
    在同一个事务中,两次执行相同的查询返回了不同的结果。因为在此期间另一个事务对数据进行了修改并提交。
    例如,假设T1开始一个事务,查询A账户的余额,然后T2开始另一个事务,将一笔钱从A账户转出并提交。当T1再次查询A账户的余额时,会发现余额与第一次查询的结果不同,这就是不可重复读。

  1. 幻读(Phantom Read)
    在同一个事务中,两次执行相同的范围查询返回了不同数量的行。因为在此期间另一个事务插入、删除或更新了满足查询条件的行并提交。
    例如,假设T1开始一个事务,查询所有余额大于1000元的账户,然后T2开始另一个事务,将一个新账户的余额设置为1500元并提交。当T1再次执行相同的查询时,会发现多了一个新的账户,这就是幻读。

  1. *丢失更新(Lost Update)
    两个或多个事务同时修改同一行数据,其中一个事务的更新可能会被另一个事务覆盖,导致数据的变更丢失。
    例如,假设T1和T2同时开始事务,并尝试将A账户的余额增加100元。T1首先读取A账户的余额,然后加上100元,准备写回数据库。与此同时,T2也读取A账户的余额,加上100元,并先于T1提交更改。当T1提交时,它的更改会覆盖T2的更改,导致T2的更新丢失。

为了解决这些问题,MySQL提供了不同的事务隔离级别和锁机制。在选择合适的事务隔离级别时,需要权衡并发性和数据一致性之间的关系。在某些情况下,可能需要使用显式锁定或者应用程序级别的逻辑来进一步控制并发访问和修改数据。


4.2. 事务隔离级别

  1. 事务隔离级别

为了解决并发事务所引发的问题,在数据库中引入了事务隔离级别。

隔离级别

脏读

不可重复读

幻读

Read uncommitted

Read committed

×

Repeatable Read(默认)

×

×

Serializable

×

×

×

  • 读未提交(Read Uncommitted): 这是最低的隔离级别。在读未提交隔离级别下,一个事务可以读取到另一个事务尚未提交的数据变更。因此,这种隔离级别允许脏读、不可重复读和幻读。
  • 读已提交(Read Committed): 在读已提交隔离级别下,一个事务只能读取到已经提交的数据变更。这意味着脏读被阻止了,但仍然可能出现不可重复读和幻读。
  • 可重复读(Repeatable Read): 这是MySQL的默认事务隔离级别。在可重复读隔离级别下,一个事务在开始时会获取一份数据的快照,然后在整个事务期间都基于这份快照进行读取操作。这样可以防止不可重复读,但仍然可能遇到幻读。
  • 串行化(Serializable): 这是最高的事务隔离级别。在串行化隔离级别下,数据库系统通过完全锁定数据或使用更严格的锁机制来确保事务之间的串行执行,从而避免所有并发问题,包括脏读、不可重复读和幻读。然而,这可能会导致性能下降,因为并发性大大降低。

注意:事务隔离级别越高,数据越安全,但是性能越低。


  1. 查看事务隔离级别
SELECT @@TRANSACTION_ISOLATION;

返回一个字符串值,表示当前会话的事务隔离级别。可能的返回值包括:

  • READ UNCOMMITTED
  • READ COMMITTED
  • REPEATABLE READ (这是MySQL的默认事务隔离级别)
  • SERIALIZABLE

  1. 设置事务隔离级别
SET [ SESSION | GLOBAL ] TRANSACTION ISOLATION LEVEL 
{ READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE }
  • SESSION:只影响当前会话的事务隔离级别。
  • GLOBAL:影响所有新建立的会话的事务隔离级别。注意,这不会改变已经存在的会话的事务隔离级别。

例如,要将当前会话的事务隔离级别设置为READ COMMITTED,可以使用以下命令:

sql
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;

或者,要全局地设置事务隔离级别为SERIALIZABLE,可以使用以下命令:

sql
SET GLOBAL TRANSACTION ISOLATION LEVEL SERIALIZABLE;

注意:但是请注意,更改全局事务隔离级别可能会影响整个系统的性能和并发性,因此在进行此类更改时需要谨慎考虑。通常情况下,建议根据具体的应用场景和需求来选择合适的事务隔离级别。

文章来源:https://blog.csdn.net/Sakurapaid/article/details/135252552
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。