当前位置: 首页 > 图灵资讯 > java面试题> 金三银四精选java面试题-事务的基本特性和隔离级别

金三银四精选java面试题-事务的基本特性和隔离级别

来源:图灵教育
时间:2023-12-05 10:23:14
 

事务的基本特性和隔离级别

事务4大特性(ACID):原子性、一致性、隔离性、持久性

  • 原子性(Atomicity):事务是不可分割的工作单元,要么全部执行成功,要么全部失败。如果事务中的任何一个操作失败,整个事务会被回滚,以确保数据的一致性。原子性保证了事务的完整性。
  • 一致性(Consistency):事务在执行前和执行后,数据库必须保持一致状态。这意味着事务将数据库从一个一致状态转变为另一个一致状态。如果一个事务执行过程中发生错误,数据库将被回滚到事务开始前的状态。
  • 隔离性(Isolation):隔离性确保多个事务可以并发执行,而不会相互干扰。每个事务都应该感觉就像它是唯一运行的,即使在多个事务并发执行的情况下。隔离级别控制了不同事务之间的可见性。
  • 持久性(Durability):一旦事务提交,其对数据库所做的更改应该是永久性的,即使系统崩溃或重启更改也不应丢失。数据库系统通常将事务的更改记录到持久性存储介质,如磁盘,以确保持久性。

隔离级别是事务隔离性的四种级别,从低到高,依次为:

  • 读未提交(Read Uncommitted):允许一个事务可以读取另一个事务未提交的数据。这是最低级别的隔离,可能会导致脏读、不可重复读和幻读。
DROP TABLE test;
CREATE TABLE test (id INT PRIMARY KEY, name VARCHAR(20), balance INT);
INSERT INTO test VALUES (1, 'Alice', 1000);
  • 事务A修改balance并且不提交事务,事务B读取balance值为900;
  • 如果此时事务A回滚数据,事务B读取balance值为1000(脏读);
# 事务A
set tx_isolation = 'read-uncommitted';
BEGIN;
UPDATE test SET balance = balance - 100 WHERE id = 1;
SELECT balance FROM test WHERE id = 1;

# @1
rollback
COMMIT;

# 事务B
set tx_isolation = 'read-uncommitted';
BEGIN;
SELECT balance FROM test WHERE id = 1;

# @1:rollback后
SELECT balance FROM test WHERE id = 1;
commit;
  • 读已提交(Read Committed):一个事务只能读取已经提交的数据。这可以避免脏读,但仍然允许不可重复读和幻读。
  • 事务A修改balance并且不提交事务,事务B读取balance为1000;当事务A提交后,事务B读取balance值为900;
  • 再重新开启事务A修改balance并提交事务,事务B中在读取balance值为800(整个过程事务B都不提交)(不可重复读);
update test set balance = 1000 where id = 1;
# 事务A
set tx_isolation = 'read-committed';
BEGIN;
UPDATE test SET balance = balance - 100 WHERE id = 1;
SELECT balance FROM test WHERE id = 1;
COMMIT;

# @2:再次修改balance并提交事务
BEGIN;
UPDATE test SET balance = balance - 100 WHERE id = 1;
SELECT balance FROM test WHERE id = 1;
COMMIT;

# 事务B
set tx_isolation = 'read-committed';
BEGIN;
# 事务A提交前
SELECT balance FROM test WHERE id = 1;

# 事务A提交后
SELECT balance FROM test WHERE id = 1;

# @2:再次查询balance
SELECT balance FROM test WHERE id = 1;
commit;
  • 可重复读(Repeatable Read):一个事务在其生命周期内看到的数据保持一致。这可以避免脏读和不可重复读,但仍然允许幻读。
  • 事务A修改balance并且不提交事务,事务B读取balance为1000;当事务A提交后,事务B读取balance值为1000;
  • 开启事务A修改balance并提交事务,事务B中在读取balance值为1000(可重复读)(整个过程事务B都不提交);
  • 开启事务A插入为2的记录,事务B无法读取到2的记录,此时修改id为2balance+1000,可以修改成功,重新读取为2的记录balance为3000(幻读)(整个过程事务B都不提交)
update test set balance = 1000 where id = 1;
# 事务A
set tx_isolation = 'repeatable-read';
BEGIN;
UPDATE test SET balance = balance - 100 WHERE id = 1;
SELECT balance FROM test WHERE id = 1;
COMMIT;

# @1:再次修改balance
BEGIN;
UPDATE test SET balance = balance - 100 WHERE id = 1;
SELECT balance FROM test WHERE id = 1;
COMMIT;

# @2:插入id:2记录
BEGIN;
INSERT INTO test VALUES (2, 'Alice2', 2000);
COMMIT;

# 事务B
set tx_isolation = 'repeatable-read';
BEGIN;
# 事务A提交前
SELECT balance FROM test WHERE id = 1;

# 事务A提交后
SELECT balance FROM test WHERE id = 1;

# @1:再次查询balance
SELECT balance FROM test WHERE id = 1;

# @2:查询id:2的记录
SELECT balance FROM test WHERE id = 2;

# 修改id:2的balance,修改成功
update test set balance = balance + 1000 where id = 2;

# 查询id:2的记录
SELECT balance FROM test WHERE id = 2;
commit;
  • 串行化(Serializable):最高级别的隔离,要求事务按顺序运行,彼此不会相互干扰。这可以避免脏读、不可重复读和幻读,但可能会导致性能下降,因为事务必须按顺序执行。
update test set balance = 1000 where id = 1;
# 事务A
set tx_isolation = 'serializable';
BEGIN;
UPDATE test SET balance = balance - 100 WHERE id = 1;
COMMIT;

# 事务B
set tx_isolation = 'serializable';
BEGIN;
SELECT balance FROM test WHERE id = 1;
commit;