本节主要探讨了数据库中事务与锁机制的必要性,讲解了如何在数据库中使用事务与锁机制实现数据库的一致性以及并发性,并结合“选课系统”讲解事务与锁机制在该系统中的应用。
9.4.1 事务机制
事务通常包含一系列更新操作,这些更新操作是一个不可分割的逻辑工作单元。如果事务成功执行,那么该事务中所有的更新操作都会成功执行、并将执行结果提交到数据库文件中,成为数据库永久的组成部分。如果事务中某条更新操作执行失败,那么事务中的所有操作均被撤销。简言之:事务中的更新操作要么都执行,要么都不执行,这个特征叫做事务的原子性。
9.4.2 事务的四大特性(ACID)
事务的任务是保证一系列更新语句的原子性,锁的任务是解决并发访问可能导致的数据不一致问题。如果事务与事务之间存在并发操作,此时可以通过隔离级别实现事务的隔离性,从而实现数据的并发访问。示意图如图9.6所示。
图9.6 事务的ACID特性
(1)原子性(atomicity):一个事务必须视为一个不可分割的最小工作单元,整个事务中的所有操作要么全部提交成功,要么全部失败回滚,对于一个事务来说,不可能只执行其中的一部分操作,这就是事务的原子性。
(2)一致性(consistency):数据库总数从一个一致性的状态转换到另一个一致性的状态。
(3)隔离性(isolation):一个事务所做的修改在最终提交以前,对其他事务是不可见的。
(4)持久性(durability):一旦事务提交,则其所做的修改就会永久保存到数据库中。此时即使系统崩溃,修改的数据也不会丢失。
【例9.2】事务操作实例。
(1)创建表test_1。
mysql> create table test_1(id int,username varchar(20))engine=innodb;
执行结果为图9.7所示。
图9.7 创建表test_1
(2)向表test_1插入记录。
mysql> insert into test_1 values(1,'petter'),(2,'aaa'),(3,'bob'),(4,'allen'),(5,'marno');
执行结果如图9.8所示。
图9.8 向表test_1插入记录
(3)查询表中的数据记录。
mysql>select * from test_1;
执行结果如图9.9所示。
图9.9 查询结果
(4)开启一个事务。
mysql>begin;
执行结果如图9.10所示。
图9.10 成功开启事务
(5)更新一条记录。
mysql>update test_1 set username=’sgq’where id=1;
执行结果如图9.11所示。
图9.11 更新记录数据
(6)提交事务。
mysql>commit;
执行结果如图9.12所示。
图9.12 成功提交事务
(7)发现记录已经更改生效。
mysql>select * from test_1;
执行结果如图9.13所示。
图9.13 事务生效后的数据(www.xing528.com)
(8)开启另一个事务。
mysql>begin;
执行结果如图9.14所示。
图9.14 成功开启另一个事务
(9)更新一条记录。
mysql>update test_1 set username=’qqq’where id=1;
执行结果如图9.15所示。
图9.15 成功更新数据
(10)发现记录已经更改生效。
mysql>select * from test_1;
查看结果如图9.16所示。
图9.16 已更新后的数据
(11)事务回滚。
mysql>rollback;
执行结果如图9.17所示。
图9.17 事务回滚成功
(12)再次查看发现数据已经回滚,如图9.18所示。
图9.18 事务回滚成功后,数据恢复到更新前状态
事务没有提交,回滚后就恢复到之前的状态。
9.4.3 事务的隔离级别与并发问题
SQL标准定义了四种隔离级别:read uncommitted(读取未提交的数据)、read committed(读取提交的数据)、repeatable read(可重复读)以及serializable(串行化)。四种隔离级别逐渐增强,其中read uncommitted的隔离级别最低,serializable的隔离级别最高。 MySQL支持4种事务隔离级别,在InnoDB存储引擎中,可以使用以下命令设置事务的隔离级别。
1.read uncommitted(读取未提交的数据)
在该隔离级别中,所有事务都可以看到其他未提交事务的执行结果。该隔离级别很少用于实际应用,并且它的性能也不比其他隔离级别好多少。合理地设置事务的隔离级别,可以有效避免脏读、不可重复读、幻读等并发问题。可以使用以下语句设置:
mysql> SET GLOBAL TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
2.read committed(读取提交的数据)
这是大多数数据库系统默认的隔离级别(但不是MySQL默认的)。它满足了隔离的简单定义:一个事务只能看见已提交事务所做的改变。可以使用以下语句设置:
mysql> SET GLOBAL TRANSACTION ISOLATION LEVEL READ COMMITTED;
3.repeatable read(可重复读)
这是MySQL默认的事务隔离级别,它确保同一事务内相同的查询语句,执行结果一致。可以使用以下语句设置:
mysql> SET GLOBAL TRANSACTION ISOLATION LEVEL REPEATABLE READ;
4.serializable(串行化)
这是最高的隔离级别,它通过强制事务排序,使之不可能相互冲突。换言之,它会在每条select语句后自动加上lock in share mode,为每个查询操作施加一个共享锁。在这个级别,可能导致大量的锁等待现象。该隔离级别主要用于InnoDB存储引擎的分布式事务。可以使用以下语句设置:
mysql> SET GLOBAL TRANSACTION ISOLATION LEVEL SERIALIZABLE;
低级别的事务隔离可以提高事务的并发访问性能,却可能导致较多的并发问题(例如脏读、不可重复读、幻读等并发问题);高级别的事务隔离可以有效避免并发问题,但会降低事务的并发访问性能,可能导致出现大量的锁等待、甚至死锁现象。
脏读(Dirty Read):一个事务可以读到另一个事务未提交的数据,脏读问题违背了事务的隔离性原则。
不可重复读(Non-repeatable read):同一个事务内两条相同的查询语句,查询结果不一致。
幻读(Phantom Read):同一个事务内,两条相同的查询语句,查询结果应该相同。但是,如果另一个事务同时提交了新数据,本事务再更新时,就会“惊奇的”发现了这些新数据,貌似之前读到的数据是“鬼影”一样的幻觉。
四种隔离级别的不同如表9.1所示。
表9.1 四种隔离级别的区别
免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。