事务及其特性

什么是事务?

事务是一组原子性的SQL语句或者说是一个独立的工作单元,如果数据库引擎能够成功对数据库应用这组SQL语句,那么就执行,如果其中有任何一条语句因为崩溃或其它原因无法执行,那么所有的语句都不会执行。也就是说,事务内的语句,要么全部执行成功,要么全部执行失败。 举个银行应用的典型例子:

假设银行的数据库有两张表:支票表和储蓄表,现在某个客户A要从其支票账户转移2000元到其储蓄账户,那么至少需求三个步骤:

a.检查A的支票账户余额高于2000元;

b.从A的支票账户余额中减去2000元;

c.在A的储蓄账户余额中增加2000元。

这三个步骤必须要打包在一个事务中,任何一个步骤失败,则必须要回滚所有的步骤,否则A作为银行的客户就可能要莫名损失2000元,就出问题了。这就是一个典型的事务,这个事务是不可分割的最小工作单元,整个事务中的所有操作要么全部提交成功,要么全部失败回滚,不可能只执行其中一部分,这也是事务的原子性特征。

  1. START TRANSACTION; --这条语句用于开启一个事务。事务是一组SQL操作,要么全部执行成功并提交,要么发生错误回滚。
  2. UPDATE user SET name = 'user1' WHERE id = 1; -- 这条语句将数据库中id为1的用户的姓名更新为’user1’。
  3. INSERT INTO user (id, name) VALUES (1, 'admin'); - -这条语句向用户表中插入一条新的记录,id为1,姓名为"admin"。
  4. COMMIT; -- 这条语句用于提交事务,将之前的所有操作永久性地保存到数据库中。
  5. SET autocommit='NO'; --这条语句用于将数据库的自动提交模式设置为关闭,意味着在事务开始之后,要手动执行提交操作。

    MySQL中的事务特性以及隔离机制是为了保证数据的一致性和并发操作的正确性。

ACID特性:

  1. 原子性(Atomicity):事务中所有的操作要么全部成功,要么全部失败回滚,保证事务的完整性。
  2. 一致性(Consistency):事务开始前和结束后,数据库必须保持一致状态,即满足约束条件和触发器等数据完整性约束。
  3. 隔离性(Isolation):并发事务之间相互隔离,每个事务都感觉不到其他事务的存在。
  4. 持久性(Durability):一旦事务提交,对数据库的修改将永久保存,即使系统发生故障也不会丢失。

隔离级别:

  1. 读未提交(Read Uncommitted):事务中的修改即使未提交,对其他事务也是可见的。解决了脏读问题。
  2. 读已提交(Read Committed):事务中的修改只对其他事务可见,等待事务提交后才能读取。解决了脏读问题。
  3. 可重复读(Repeatable Read):事务启动后,其他事务对数据的修改对本事务不可见,保证了在同一事务中多次读取同一数据的结果一致性。解决了脏读和不可重复读问题。
  4. 串行化(Serializable):最高的隔离级别,通过对事务进行串行执行,避免了脏读、不可重复读和幻读的问题。适用于对并发性要求非常高的场景。

当隔离级别为读未提交(Read Uncommitted)时,事务可以读取其他事务尚未提交的数据。下面是一个简单的实际操作验证读未提交隔离级别的例子:

首先设置隔离级别:

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;

  

1. 创建测试表并插入一条数据:

CREATE TABLE `test` (
  `id` int(11) NOT NULL,
  `value` int(11) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB;

INSERT INTO `test` (`id`, `value`) VALUES (1, 100);

 

 2. 打开两个不同的数据库连接或会话。

3. 在会话1中开始一个事务,并读取数据:

-- 会话1
START TRANSACTION;
SELECT * FROM `test`;

 此时,会话1中可以读取到已经存在的数据 `(1, 100)`。

4. 在会话2中开始另一个事务,修改数据但未提交:

-- 会话2
START TRANSACTION;
UPDATE `test` SET `value` = 200 WHERE `id` = 1;

 

5. 回到会话1,在未提交的事务中再次读取数据:

-- 会话1
SELECT * FROM `test`;

  

在读未提交隔离级别下,会话1可以读取到会话2修改但未提交的数据,即 `(1, 200)`。

6. 在会话1中提交事务:

-- 会话1
COMMIT;

  

数据修改成功提交后,结果 `(1, 200)` 成为了最终的数据。

这个例子验证了读未提交隔离级别下,一个事务可以读取另一个未提交事务的数据,可能导致脏读问题。实际上,在大多数情况下,读未提交隔离级别并不常用,因为它无法保证数据的一致性和可靠性。大多数情况下,数据库操作需要使用更高级别的隔离级别来解决并发访问的问题。

当隔离级别为读已提交(Read Committed)时,事务只能读取已经提交的数据,未提交的数据对其他事务是不可见的。下面是一个简单的实际操作验证读已提交隔离级别的例子:

设置隔离级别:

SET TRANSACTION ISOLATION LEVEL READ COMMITTED;

1. 创建测试表并插入一条数据:

CREATE TABLE `test` (
  `id` int(11) NOT NULL,
  `value` int(11) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB;

INSERT INTO `test` (`id`, `value`) VALUES (1, 100);


2. 打开两个不同的数据库连接或会话。


3. 在会话1中开始一个事务,并读取数据:

-- 会话1
START TRANSACTION;
SELECT * FROM `test`;

此时,会话1中可以读取到已经存在的数据 `(1, 100)`。

4. 在会话2中开始另一个事务,修改数据但未提交:

-- 会话2
START TRANSACTION;
UPDATE `test` SET `value` = 200 WHERE `id` = 1;

5. 回到会话1,在未提交的事务中再次尝试读取数据:

-- 会话1
SELECT * FROM `test`;

  

在读已提交隔离级别下,会话1无法读取到会话2中修改但未提交的数据。所以此时会话1仍然读取到的数据是 `(1, 100)`。

6. 在会话2中提交事务:

-- 会话2
COMMIT;

  

数据修改成功提交后,可以在会话1中重新读取数据:

-- 会话1
SELECT * FROM `test`;

现在会话1读取的数据是最新提交的数据 `(1, 200)`。

这个例子验证了读已提交隔离级别下,一个事务只能读取其他已经提交的事务的数据,未提交的数据对其他事务是不可见的。这样可以解决脏读的问题,确保数据的一致性和可靠性。

可重复读(Repeatable Read)隔离级别是保证在同一事务内多次读取同一数据时,结果始终保持一致,不受其他事务修改的影响。下面是一个简单的实际操作验证可重复读隔离级别的例子:

设置隔离级别

SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;

  

1. 创建测试表并插入一条数据:

CREATE TABLE `test` (
  `id` int(11) NOT NULL,
  `value` int(11) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB;

INSERT INTO `test` (`id`, `value`) VALUES (1, 100);

2. 打开两个不同的数据库连接或会话。

3. 在会话1中开始一个事务,并读取数据:

-- 会话1
START TRANSACTION;
SELECT * FROM `test`;

  

此时,会话1中可以读取到已经存在的数据 `(1, 100)`。

4. 在会话2中开始另一个事务,修改数据:

-- 会话2
START TRANSACTION;
UPDATE `test` SET `value` = 200 WHERE `id` = 1;

  

5. 回到会话1,在同一事务中再次读取数据:

-- 会话1
SELECT * FROM `test`;

  

在可重复读隔离级别下,会话1再次读取的数据仍然是 `(1, 100)`,不受会话2中的修改的影响。

6. 在会话1中再次读取数据:

-- 会话1
SELECT * FROM `test`;

  

再次读取数据时,会话1仍然读取到的是初始的数据 `(1, 100)`,并不受其他事务的修改影响。

7. 提交会话2中的事务:

-- 会话2
COMMIT;

  

8. 在会话1中再次读取数据:

-- 会话1
SELECT * FROM `test`;

  

此时,会话1读取到的数据仍然是最开始的数据 `(1, 100)`,并没有发生变化。

这个例子验证了可重复读隔离级别下,一个事务在多次读取同一数据时,结果始终保持一致,不受其他事务的修改的影响。这样可以解决不可重复读的问题,确保在事务内部数据的一致性和可靠性。

串行化(Serializable)隔离级别是最高的隔离级别,在该级别下,所有事务串行执行,确保数据的完全一致性。下面是一个简单的实际操作验证串行化隔离级别的例子:

设置隔离级别

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;

1. 创建测试表并插入一条数据:

CREATE TABLE `test` (
  `id` int(11) NOT NULL,
  `value` int(11) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB;

INSERT INTO `test` (`id`, `value`) VALUES (1, 100);

 

2. 打开两个不同的数据库连接或会话。

3. 在会话1中开始一个事务,并读取数据:

-- 会话1
START TRANSACTION;
SELECT * FROM `test`;

 

此时,会话1中可以读取到已经存在的数据 `(1, 100)`。

4. 在会话2中开始另一个事务,尝试修改数据:

-- 会话2
START TRANSACTION;
UPDATE `test` SET `value` = 200 WHERE `id` = 1;

 

5. 回到会话1,在事务内尝试读取数据:

-- 会话1
SELECT * FROM `test`;

 

在串行化隔离级别下,会话1无法读取到会话2中已经开始但未提交的数据。所以此时会话1依然读取到的数据是 `(1, 100)`。

6. 在会话2中提交事务:

-- 会话2
COMMIT;

 

7. 在会话1中再次尝试读取数据:

-- 会话1
SELECT * FROM `test`;

 

在串行化隔离级别下,会话1读取到的数据仍然是最开始的数据 `(1, 100)`,不受其他事务的修改影响。

这个例子验证了串行化隔离级别下,事务串行执行,确保数据的完全一致性。在这个隔离级别下,事务之间是完全隔离的,一次只执行一个事务,避免了任何并发导致的问题。然而,串行化隔离级别对系统性能的影响较大,因为它会限制并发操作。通常情况下,只有在极端需要确保数据完全一致性的场景下才使用串行化隔离级别。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

孤冢清风666

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值