实验11 事务和锁
事务是一系列的数据库操作,是数据库应用程序的基本逻辑单元,是并发控制的基本单位。组成一个事务的所有操作要么都做,要么都不做,是一个不可分割的工作单元。在关系数据库中,一个事务可以是一条SQL语句、一组SQL语句或整个程序。
【实验目的】
①理解事务的概念。
②掌握事务的创建和运行方法。
③理解MySQL的隔离级别。
④理解MySQL锁机制。
⑤理解事务加锁的机制。
【知识要点】
(1)事务的特性
事务是作为单个逻辑工作单元执行的一系列操作。一个逻辑工作单元必须具有四个属性:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持续性(Durability),简称ACID属性。
1)原子性
事务必须是原子工作单元,事务中包含的诸操作要么都做,要么都不做。
2)一致性
事务执行的结果必须使数据库从一个一致性状态转变到另一个一致性的状态。
3)隔离性
一个事务的执行不能被其他事务干扰。即一个事务内部的操作及使用的数据对其他并发执行事务是隔离的,并发执行的各个事务之间不能互相干扰。
4)持续性
持续性也称永久性,指一个事务一旦提交,它对数据库中数据的改变就应该是永久性的,接下来的其他操作或故障不应该对其执行结果有任何影响。
(2)MySQL事务语句
在MySQL语言中,定义事务的语句有3条:
START TRANSACTION 或 BEGIN
COMMIT
ROLLBACK
事务通常是以START TRANSACTION或BEGIN开始,以COMMIT或ROLLBACK结束。
COMMIT表示提交,即提交事务的所有操作,它保证事务的所有修改在数据库中都永久有效,COMMIT 语句还释放事务使用的资源(例如锁)。
ROLLBACK表示回滚,即在事务运行的过程中出现错误,或用户决定取消事务,系统将事务中对数据库的所有已完成的操作全部撤销,数据返回到它在事务开始时所处的状态。ROLLBACK 还释放事务占用的资源。
(3)MySQL事务控制语句
MySQL有3种事务模式,即自动提交事务模式、显式事务模式和隐式事务模式。
①自动提交事务模式:每条单独的语句都是一个事务,是MySQL默认的事务管理模式。在此模式下,当一条语句成功执行后,它被自动提交隐式执行 COMMIT 操作,而当它在执行中产生错误时被自动回滚。执行“SET SESSION AUTOCOMMIT = 1”开启事务自动提交。
②显式事务模式:该模式允许用户手动开启和结束事务。事务以START TRANSACTION 或BEGIN语句作为开始,以COMMIT或 ROLLBACK语句结束。
③隐式事务模式:在隐式事务中,无须使用 START TRANASACTION 或 BEGIN 来开启事务,每个 SQL 语句第一次执行就会开启一个事务,直到用COMMIT或ROLLBACK 来提交或回滚结束事务。执行“SET SESSION AUTOCOMMIT = 0”,可使MySQL进入隐式事务模式。
(4)控制事务
应用程序主要通过指定事务启动和结束的时间来控制事务。可以使用SQL语句或数据库应用程序编程接口 (API) 函数来指定这些时间。系统还必须能够正确处理那些在事务完成之前便终止事务的错误。默认情况下,事务按连接级别进行管理。在一个连接上启动一个事务后,该事务结束之前,在该连接上执行的所有SQL语句都是该事务的一部分。(https://www.daowen.com)
(5)MySQL的锁
使用锁机制是防止其他用户修改另外一个未完成的事务中的数据。锁保证数据并发访问的一致性和有效性。MySQL的锁分为表级锁和行级锁。
1)表级锁
表级锁是以表为单位进行加锁,开销小,加锁快,不会出现死锁。锁粒度大,发生锁冲突的概率最高,并发度最低,表级锁适合做查询为主的场景,如小型的Web应用。
表级锁包括表共享读锁(Table Read Lock)和表独占写锁(Table Write Lock)。
对于读操作,可以增加读锁,一旦数据表被加上读锁,其他请求可以对该表再次增加读锁,但是不能增加写锁。对于写操作,可以增加写锁,一旦数据表被加上写锁,其他请求无法对该表增加读锁和写锁。
MySQL表级锁加入方式:
LOCK TABLES
tbl_name [[AS] alias] lock_type
[, tbl_name [[AS] alias] lock_type] ...
lock_type: {
READ [LOCAL]
| [LOW_PRIORITY] WRITE
}
MySQL解锁方式:
UNLOCK TABLES;
2)行级锁
行级锁是以记录为单位进行加锁。开销大,加锁慢,会出现死锁。锁粒度最小,发生锁冲突的概率最低,并发度也最高。行级锁适用于高并发环境下,对事务完整性要求较高的系统,如在线事务处理系统。
行级锁包括共享锁(S锁)和排他锁(X锁)。
共享锁:又称为读锁,就是多个事务对于同一数据可以共享一把锁,都能访问到数据,但是只能读不能修改。
MySQL行级共享锁加锁方式:
SELECT * FROM table_references WHERE where_condition LOCK IN SHARE MODE;
解锁方式:COMMIT/ROLLBACK。
排他锁:又称为写锁,排他锁不能与其他锁并存,如一个事务获取了一个数据行的排他锁,其他事务就不能再获取该行的锁(共享锁、排他锁),只有获取了排他锁的事务可以对数据行进行读取和修改。
MySQL行级排他锁加锁方式:
自动方式:在更新(INSERT、UPDATE、DELETE)语句中,MySQL将会对符合条件的记录默认自动加上排他锁。
手动加入方式:SELECT * FROM table_references WHERE where_condition FOR UPDATE;
3)表的意向锁
意向锁是隐式的表级锁,数据库开发人员在向表中的某些记录加行级锁时,MySQL首先会自动向该表施加意向锁,然后再施加行级锁。意向锁是数据引擎自己维护的,用户无法手动操作意向锁。MySQL提供两种意向锁:意向共享锁(IS)和意向排他锁(IX)。
意向共享锁(IS):表示事务准备给数据行加入共享锁,也就是说,一个数据行加共享锁前必须先取得该表的IS锁。例如,执行“SELECT * FROM table_references WHERE where_condition LOCK IN SHARE MODE;”后,MySQL在为表中符合条件的记录施加共享锁之前会自动地为该表施加意向共享锁(IS)。
意向排他锁(IX):表示事务准备给数据行加入排他锁,说明事务在一个数据行加排他锁前必须先取得该表的IX锁。例如,执行“SELECT * FROM table_references WHERE where_condition FOR UPDATE;”后,MySQL在为表中符合条件的记录施加排他锁之前会自动地为该表施加意向排他锁(IX)。
(6)事务隔离级别
事务隔离级别定义一个事务必须与由其他事务进行的资源或数据更改相隔离的程度。MySQL支持4种隔离级别,分别是读未提交(READ UNCOMMITTED)、读提交 (READ COMMITTED)、可重复读(REPEATABLE READ)、串行化(SERIALIZABLE)。定义事务的隔离级别可以使用SET TRANSACTION语句,其语法形式如下:
SET SESSION TRANSACTION ISOLATION LEVEL
SERIALIZABLE
| REPEATABLE READ
| READ COMMITTED
| READ UNCOMMITTED;
在系统变量@@TRANSACTION_ISOLATION中存储了事务的隔离级别,用户可以用SELECT @@TRANSACTION_ISOLATION语句查看当前设置的事务隔离级别。
READ UNCOMMITTED:在该隔离级别,所有事务都可以看到其他未提交事务的执行结果。本隔离级别很少用于实际应用,因为它的性能也不比其他级别好多少。读取未提交的数据,也被称为脏读(Dirty Read)。
READ COMMITTED:一个事务只能看见已经提交事务所做的改变。这种隔离级别可以避免脏读现象,但可能出现不可重复读和幻读,因为同一事务的其他实例在该实例处理期间可能有新COMMIT,所以同一查询可能返回不同的结果。
REPEATABLE READ:这是MySQL的默认事务隔离级别,它确保同一事务的多个实例在并发读取数据时,会看到同样的数据行。这种隔离级别可以避免脏读以及不可重复读的现象,但可能出现幻读现象。幻读指当用户读取某一范围的数据行时,另一个事务又在该范围内插入了新行,当用户再读取该范围的数据行时会发现有新的“幻影”行。
SERIALIZABLE:这是最高的隔离级别,它通过强制事务排序,使之不可能相互冲突,从而解决幻读问题。简言之,它是在每个读的数据行上加上共享锁。在这个级别中,可能导致大量的锁等待超时现象和锁竞争。
在SERIALIZABLE隔离级别下,所有事务按照次序依次执行,因此,脏读、不可重复读、幻读都不会出现。虽然SERIALIZABLE隔离级别下的事务具有最高的安全性,但是会降低事务并发访问性能,因此不建议将事务隔离级别设置为SERIALIZABLE。