第九章 - 并发事务与锁机制

第九章 - 并发事务与锁机制

MySQL在对数据库进行操作时,通过事务来保证数据的完整性。事务是由一系列的数据操作命令序列组成,是数据库应用程序的基本逻辑操作单元。

在MySQL环境中,事务是由作为一个逻辑单元的一个或多个SQL语句组成。例如,前面介绍的每一条DDL语句,都可以看成是一个事务;但在实际的工作中,一个事务往往是需要多条语句共同组成,来完成较为复杂的数据操作。

多用户访问数据库时,并发的情况是常态,数据库系统的并发处理能力是衡量其性能的重要标志之一。数据库系统需要通过适当的并发控制机制协调并发操作,保证数据的一致性。在MySQL数据库中,事务是进行数据管理的基本操作单元,锁机制是用于实现并发控制的主要方法。

本章主要介绍事务与锁的基本概念和基本操作。

9.1 认识事务机制

在程序设计过程中,与一个事务相关的数据必须保证可靠性、精确性、一致性和完整性,以符合实际的企业生产过程的需要。现实生活中如火车购票、网上购物、股票交易、银行借贷等都是的采用事务方式来处理。

在MySQL中,通常由事务来完成相关操作,以确保多个数据的修改作为一个单元来处理。

数据的可靠性和完整性就无法保证。而此过程实际上就是对银行服务器中的数据表中进行了含有一组数据修改SQL语句的事务操作。

9.1.1 事务的特性

事务处理机制在程序开发过程中有着非常重要的作用,它可以使整个系统更加安全。 MySQL系统具有事务处理功能,能够保证数据库操作的一致性和完整性,使用事务可以确保同时发生的行为与数据的有效性不发生冲突。在MySQL中,并不是所有的存储引擎都支持事务,如InnoDB和BDB支持,但MyISAM和MEMORY则不支持。

事务中的每个SQL语句是互相依赖的,而且单元作为一个整体是不可分割的。如果单元中的一个语句不能完成,整个单元就会回滚全部数据操作,返回到事务开始以前的状态。因此,只有事务中的所有语句都执行完毕,才能说这个事务被成功地执行,才能将执行结果提交到数据库文件中,成为数据库永久的组成部分。

这就要求事务本身必须具有以下4个特性。

(1)原子性 (Atomicity ) 。原子性意味着每个事务都必须被看作是一个不可分割的单元。如果事务失败,系统将会返回到该事务开始执行前的状态。

(2)一致性(Consistency )。事务执行完成后,都将数据库从一个一致状态转变到另一个一致状态,事务不能违背定义在数据库中的任何完整性检查。

(3)隔离性 ( Isolation)。隔离性是指每个事务在其自己的会话空间发生,和其他发生在系统中的事务隔离,而且事务的结果只有在完全被执行后才能看到。

(4)持久性 ( Durability)。要求一旦事务提交,那么对数据库所做的修改将是持久的,无论发生何种机器和系统故障,都不应该对其有任何影响。大多数DBMS产品通过保存所有行为的日志来保证数据的持久性,这些行为是指在数据库中以任何方法更改数据。数据库日志记录了所有对于表的更新、查询、报表等。例如,自动柜员机(ATM)在向客户支付一笔钱时,只要操作提交,就不用担心丢失客户的取款记录。

9.1.2 事务的分类

任何对数据的修改都是在事务环境中进行的。按照事务定义的方式可以将事务分为系统定义事务和用户定义事务。MySQL支持4种事务模式。其中显式事务和隐式事务属于用户定义的事务。

1. 自动提交事务

默认情况下 ,MySQL采用autocommit模式运行。当执行一个用于修改表数据的语句之后,MySQL会立刻将结果存储到磁盘中。如果没有用户定义事务,MySQL会自己定义事务,称为自动提交事务。每个MySQL语句在完成时,都被提交或回滚。如果一个语句成功地完成,则提交该语句。如果遇到错误,则回滚该语句的操作。

2. 显式事务

显式事务是指显式定义了启动(start transaction | begin work)和结束(commit 或rollback work )的事务。在实际应用中,大多数的事务是由用户来定义的。事务结束分为提交(commit)和回滚(rollback)两种状态。事务以提交状态结束,全部事务操作完成后,将操作结果提交到数据库中。事务以回滚的状态结束,则将事务的操作被全部取消,事务操作失败。

3. 分布式事务

一个比较复杂的环境,可能有多台服务器,那么要保证在多服务器环境中事务的完整性和一致性,就必须定义一个分布式事务。

分布式事务使用两段式提交(two-phase commit)的方式。在第一个阶段,所有参与全局事务的节点都开始准备,告诉事务管理器它们准备好提交了。第二个阶段,事务管理器告诉资源管理器执行 rollback或者commit,如果任何一个节点显示不能commit,那么所有的节点就得全部rollback。

跨越两个或多个数据库的单个数据库引擎实例中的事务实际上也是分布式事务。该实例对分布式事务进行内部管理;对于用户而言,其操作就像本地事务一样。

9.2 事务的管理

一般来说,事务的基本操作包括关闭自动提交、启动、保存、提交或回滚等环节。在MySQL中,当一个会话开始时,系统变量@@autocommit值为1,即自动提交功能是打开的,当用户每执行一条SQL语句后,该语句对数据库的修改就立即被提交成为持久性修改保存到磁盘上,一个事务也就结束了。

因此,用户必须关闭自动提交,事务才能由多条SQL语句组成,可以使用如下语句来实现。

set @@autocommit=0; 

执行此语句后,必须明确地指示每个事务的终止,事务中的SQL语句对数据库所做的修改才能成为持久化修改。

9.2.1 启动事务

当一个应用程序的第一条SQL语句或者在commit或rollback语句后的第一条SQL执行后,一个新的事务也就开始了。另外还可以使用一条start transaction语句来显式地启动一个事务。

启动事务的语法格式如下:

start transaction|begin work 

利用begin work语句可以用来替代start transaction语句,但是start transaction更常用些。

9.2.2 结束事务

commit语句是提交语句,它使得自从事务开始以来所执行的所有数据修改成为数据库的永久部分,也标志一个事务的结束。

结束事务的语法格式如下:

commit [work][and[no]chain][[no] release] 

注意:MySQL使用的是平面事务模型,因此嵌套的事务是不允许的。在第一个事务里使用start transaction命令后,当第二个事务开始时,自动地提交第一个事务。

同样,下面的这些MySQL语句运行时都会隐式地执行一个commit命令。

drop database / drop table /create index/ drop index/alter table / rename table /lock tables / unlock tables /set @@autocommit=1

9.2.3 回滚事务

rollback语句是回滚语句,它回滚事务所做的修改,并结束当前这个事务。

回滚事务的语法格式如下:

rollback [work][and[no]chain][[no]release] 

在前面的举例中,若在最后加上以下这条语句:

rollback work; 

执行完这条语句后,前面的删除动作将被回滚,可以使用select语句查看该行数据是否还原。

9.2.4 设置事务检查点

除了回滚整个事务,用户还可以使用rollback to语句使事务回滚到某个点,实现事务的部分回滚。这需要使用 savepoint语句来设置一个保存点。

设置事务检查点的语法格式如下:

savepoint identifier 

利用rollback to savepoint语句会向已命名的保存点回滚一个事务。如果在保存点被设置后,当前事务对数据进行了更改,则这些更改会在回滚中被回滚,

语法格式为:

rollback [work] to  savepoint identifier 

当事务回滚到某个保存点后,在该保存点之后设置的保存点将被删除。

release savepoint语句会从当前事务的一组保存点中删除已命名的保存点。不出现提交或回滚。如果保存点不存在,会出现错误。

语法格式为:

release  savepoint identifier 

9.2.5 改变MySQL的自动提交模式

关闭自动提交的方法有两种:一种是显式地关闭自动提交,一种是隐式地关闭自动提交。

1. 显式地关闭自动提交

使用MySQL命令“set @@autocommit=0;”,可以显式地关闭MySQL自动提交。

【例9.1】变量@@autocommit自动提交模式的修改示例。删除课程号为c05103的表记录,然后回滚。

mysql> use teaching;
mysql> delimiter //
mysql> set @@autocommit =0;
    -> create procedure  auto_cno()
    -> begin
    -> start transaction;
    -> delete from course where  courseno='c05103';
    -> select * from course where  courseno='c05103';
    -> rollback;
    -> select * from  course where courseno='c05103';
    -> end//

调用存储过程auto_cno(),查看事务:

mysql> call auto_cno();

若想恢复事务的自动提交功能,执行如下语句即可。

set @@autocommit=1; 

2. 隐式地关闭自动提交

使用MySQL命令“start transaction;”可以隐式地关闭自动提交。隐式地关闭自动提交,不会修改系统会话变量@@autocommitte的值。

【例9.2】将teaching数据库的course表中课程号为c05103的课程名称改为“高等数学”,并提交该事务。

mysql> use teaching;
mysql> delimiter //
mysql> create procedure  update_cno()
    -> begin
    -> start transaction;
    -> update course set cname='高等数学' 
    -> where courseno='c05103';
    -> commit;
    -> select * from  course where courseno='c05103';
    -> end//
mysql> delimiter ;

调用存储过程update_cno(),查看事务如下:

mysql> call update_cno();

【例9.3】使用显式事务向表course中插入两条记录。

mysql> delimiter //
mysql> create procedure insert_cno()
    -> begin
    -> start transaction;
    ->   insert into course
    ->      values('c05141','WIN设计','选修',48,8,8);
    ->    insert into course
    ->       values('c05142','WEB语言','选修',32,8,8);
    -> select * from  course where term =8;
    -> commit;
    -> end//
mysql> delimiter ;

调用存储过程insert_cno(),查看事务如下:

mysql> call insert_cno();

【例9.4】定义一个事务,向course表中添加一条记录,并设置保存点。然后再删除该记录,并回滚到事务的保存点,提交事务。

mysql> delimiter //
mysql> create procedure  sp_cno()
    -> begin
    -> start transaction;
    -> insert into course
    -> values('c05139','建模UML','选修',48,12,7);
    -> savepoint spcno1;
    -> delete from course
    -> where courseno='c05139';
    -> rollback work  to  savepoint  spcno1;
    -> select * from  course where courseno='c05139';
    -> commit ;
    -> end//
mysql> delimiter ;

调用存储过程sp_cno(),运行结果如下:

mysql> call sp_cno();

【例9.5】 编写转账业务的存储过程,要求bank表中的账户的当前金额cur_money值不能小于1。

mysql> use mysqltest;

-- 创建表bank,输入记录并显示
mysql> create table bank(
    -> cus_no  varchar(8),
    -> cus_name  varchar(10),
    -> cur_money  decimal(13,2));
mysql> insert into bank  values(‘bj101211’,‘张思睿’,1000);
mysql> insert into bank  values(‘sd101677’,‘李佛’,1);
mysql> select * from bank ;

--创建存储过程trans_bank()
mysql> delimiter //
mysql> Create procedure  trans_bank()
    -> begin
    -> declare  money  decimal(13,2);
    -> start transaction;
    -> update bank  set cur_money=cur_money-1000 where  cus_no='bj101211';
    -> update bank  set cur_money=cur_money+1000 where  cus_no='sd101677';
    -> select cur_money into money from bank  where  cus_no='bj101211';

    -> if  money <1 then
    -> begin
    -> select ' The transaction fails, the rollback transaction ';
    -> rollback;
    -> end;
    -> else
    -> begin
    -> select ' A successful transaction,commits the transaction';
    -> commit;
    -> end;
    -> end if;
    -> end//
mysql> delimiter ;

调用存储过程trans_bank(),查看事务的执行结果如下:

mysql> call trans_bank();

9.3 事务的并发处理

用户创建会话访问服务器时,系统会为用户分配私有内存区域,保存当前用户的数据和控制信息,每个用户进程通过访问自己的私有内存区访问服务器,用户之间互不干扰,以此实现并发数据访问的控制。

当数据库引擎所支持的并发操作数较大时,数据库并发程序就会增多。控制多个用户如何同时访问和更改共享数据而不会彼此冲突称为并发控制。

在MySQL中,并发控制是通过用锁来实现的。如果事务与事务之间存在并发操作,事务的隔离性是通过事务的隔离级别来实现的,而事务的隔离级别则是由事务并发处理的锁机制来管理旳。以此保证同一时刻执行多个事务时,一个事务的执行不能被其他事务干扰。

9.3.1 并发问题及其影响

多个用户访问同一个数据资源时,如果数据存储系统没有并发控制,就会出现并发问题,比如修改数据的用户会影响同时读取或修改相同数据的其他用户。当同一数据库系统中有多个事务并发运行时,如果不加以适当控制,就可能产生数据的不一致性问题。

下面以并发取款操作为例,介绍并发操作过程中的常见问题。如果得到错误的结果往往是由于T1、T2两个事务并发操作引起的,数据库的并发操作导致的数据库的不一致性主要有4种:更新丢失、不可重复读、“脏读”和幻读数据。另外,数据库的并发操作还能够导致死锁问题发生。

1. 更新丢失(Lost Update)

当两个或多个事务选择同一行,然后根据最初选定的值更新该行时,就会出现更新丢失的问题。每个事务都不知道其他事务的存在。最后的更新将覆盖其他事务所做的更新,从而导致数据丢失。

假设某客户存款的金额M=2000元,事务T1取走存款500元,事务T2取走存款800元,如果正常操作,即甲事务T1执行完毕再执行乙事务T2,存款金额更新后应该是700元。但是如果按照如下顺序操作,则会有不同的结果。

(1) T1事务开始读取存款金额M=2000元。

(2) T2事务开始读取存款金额M=2000元。

(3) T1事务取走存款500元,修改存款金额M=M-500=1500,把M=1500写回到数据库。

(4) T2事务取走存款800元,修改存款金额M=M-800=1200,把M=1200写回到数据库。

结果两个事务共取走存款1300元,而数据库中的存款却只少了800元。

2. 脏读(Dirty Read)

脏读(Dirty Read)即读出的是不正确的临时数据。例如,T2事务选择T1事务正在更新的行时,就会出现一个事务可以读到另一个事务未提交的数据。T2事务正在读取的数据尚未被T1事务提交,并可能由更新此行T1事务更改。脏读问题违背了事务的隔离性原则。

T2事务读取的数据1500,是尚未被T1事务提交的数据,回滚操作后,金额M仍然是2000,而T2事务却读出1500。

3. 不可重复读(Non-repeatable Reads)

同一个事务内两条相同的查询语句,查询结果不一致。即当一个事务多次访问同一行且每次读取不同数据时,会出现不可重复读问题。因为其他事务可能正在更新该事务正在读取的数据。

事务T1多次查询M值,得到不同的结果,原因是事务T2修改了数据。

4. 幻读(Phantom Read)

当对某行执行插入或删除操作,而该行属于某事务正在读取的行的范围时,就会出现幻读问题。由于其他事务的删除操作,使事务第一次读取行范围时存在的行在后续读取时已不存在。

与此类似,由于其他事务的插入操作,后续读取显示原来读取时并不存在的行。

例如,T1事务第一次执行查询操作,查看M值为2000。T2事务删除本行记录后,T1事务第二次执行查询操作,查看M值,记录为NULL。T2事务插入改行记录后,T1事务第三次执行查询操作,有查看M值为2000。

5. 死锁(Deadlock)

如果很多用户并发访问数据库的话,还有一个常见的现象就是死锁。简单地说,如果两个用户相互等待对方的数据,就产生了一个死锁。

mysql检测到死锁之后,会选择一个事务进行回滚。而选择的依据:看哪个事务的权重最小,事务权重的计算方法:事务加的锁最少;事务写的日志最少;事务开启的时间最晚。

例如,事务T2写了日志,事务T1没有,回滚事务T1。事务T1、T2都没写日志,但是事务T1开始的早,回滚事务T2。

9.3.2 设置事务的隔离级别

为了防止数据库的并发操作导致的数据库不一致性的更新丢失、不可重复读、“脏读”和幻读数据等问题。

SQL标准定义了4种隔离级别:read uncommitted(读取未提交的数据)、read committed(读取提交的数据)、repeatable read(可重复读)以及serializable(串行化)。

4种隔离级别逐渐增强,其中read uncommitted的隔离级别最低,serializable的隔离级别最高。在InnoDB存储引擎中,可以使用以下命令设置事务的隔离级别。

set {global|session}transaction isolation level{
	read uncommitted|read committed|repeatable read|serializable}

说明:

(1) read uncommitted:在该隔离级别,所有事务都可以看到其他未提交事务的执行结果。该隔离级别很少用于实际应用,并且它的性能也不比其他隔离级别好多少。

(2) read committed:这是大多数数据库系统的默认隔离级别(但不是MySQL默认的)。它满足了隔离的简单定义,即一个事务只能看见已提交事务所做的改变。

(3) repeatable read:这是MySQL默认的事务隔离级别,它确保同一事务内相同的查询语句,执行结果一致。

(4) serializable:这是最高的隔离级别,它通过强制事务排序,使之不可能相互冲突。换言之,它会在每条select语句后自动加上lock in share mode,为每个查询操作施加一个共享锁。在这个级别,可能导致大量的锁等待现象。该隔离级别主要用于InnoDB存储引擎的分布式事务。

低级别的事务隔离可以提高事务的并发访问性能,却可能导致较多的并发问题(例如脏读、不可重复读、幻读等并发问题);高级别的事务隔离可以有效避免并发问题,但会降低事务的并发访问性能,可能导致出现大量的锁等待、甚至死锁现象。

系统变量@@tx_isolation中存储了事务的隔离级别,可以使用select随时获得当前隔离级的值,如下所示:

mysql> select @@tx_isolation

MySQL默认为repeatable read 隔离级,这个隔离级适用于大多数应用程序,只有在应用程序有具体的对于更高或更低隔离级的要求时才需要改动。没有一个标准公式来决定哪个隔离级适用于应用程序,一般是基于应用程序的容错能力和应用程序开发者对于潜在数据错误的影响的经验判断。隔离级的选择对于每个应用程序也是没有标准的。

9.4 管理锁

多用户同时并发访问同一数据表时,仅仅通过事务机制,是无法保证数据的一致性的,MySQL通过锁来防止数据并发操作过程中引起的问题。

锁就是防止其他事务访问指定资源的手段,它是实现并发控制的主要方法,是多个用户能够同时操作同一个数据库中的数据而不发生数据不一致性现象的重要保障。

9.4.1 认识锁机制

MySQL引入锁机制管理的并发访问,通过不同类型的锁来控管多用户并发访问,实现数据访问一致性。

1. 锁机制中的基本概念

(1) 锁的粒度

锁的粒度是指锁的作用范围。锁的粒度可以分为服务器级锁(server-level locking)和存储引擎级锁(storage-engine-level locking)。MyISAM存储引擎支持表锁。InnoDB存储引擎支持表锁以及行级锁。

(2) 隐式锁与显式锁

MySQL锁分为隐式锁以及显式锁。MySQL自动加锁称为隐式锁。数据库开发人员手动加锁称为显式锁。

(3) 锁的类型

锁的类型包括读锁(read lock)和写锁(write lock),其中读锁也称为共享锁,写锁也称为排他锁或者独占锁。读锁允许其它MySQL客户机对数据同时“读”,但不允许其它MySQL客户机对数据任何“写”。写锁不允许其它MySQL客户机对数据同时读,也不允许其它MySQL客户机对数据同时写 。

(4) 锁的钥匙

多个MySQL客户机并发访问同一个数据时,如果MySQL客户机A对该数据成功地施加了锁,那么只有MySQL客户机A拥有这把锁的“钥匙”,也就是说:只有MySQL客户机A能够对该锁进行解锁操作。

(5) 锁的生命周期

锁的生命周期是指在同一个MySQL服务器连接内,对数据加锁到解锁之间的时间间隔。

2. 锁定与解锁

(1) 锁定表

MySQL提供了lock tables语句来锁定当前线程的表。锁定表的语法格式如下:

lock tables table_name[as alias]{read[local]|[low_priority]write} 

表锁定支持以下类型的锁定。read锁确保用户可以读取表,但是不能修改表。write锁只有锁定该表的用户可以修改表,其他用户无法访问该表。

在对一个事务表使用表锁定的时候需要注意是:在锁定表时会隐式地提交所有事务,在开始一个事务时,如start transaction,会隐式解开所有表锁定。

在事务表中,系统变量@@autocommit值必须设为0。否则,MySQL会在调用lock tables之后立刻释放表锁定,并且很容易形成死锁。

例如,在score表上设置一个只读锁定。

lock tables score read; 

在course表上设置一个写锁定。

lock tables course write; 

(2) 解锁表

在锁定表以后,可以使用unlock tables 命令解除锁定。该命令不需要指出解除锁定的表的名字。

解锁表的语法格式为如下:

unlock tables; 

9.4.2 锁的分类

MySQL支持很多不同的表类型,而且对于不同的类型,锁定机制也是不同的。在MySQL中有3种锁定机制。

1. 表锁

一个特殊类型的访问,整个表被客户锁定。根据锁定的类型,其他客户不能向表中插入记录,甚至从中读数据也受到限制。

表级锁定的类型包括两种锁,即读锁(read)和写锁(write)。

(1) 读锁

用于表级锁定的实现机制如下:如果表没有加写锁,那么就加一个读锁。否则的话,将请求放到读锁队列中。

(2) 写锁

用于表级锁定的实现机制如下:如果表没有加锁,那么就加一个写锁。否则的话,将请求放到写锁队列中。

2. 行锁

行级的锁定比表级锁定或页级锁定对锁定过程提供了更精细的控制。在这种情况下,只有线程使用的行是被锁定的。表中的其他行对于其他线程都是可用的。行级锁定并不是由MySQL提供的锁定机制,而是由存储引擎自己实现的,其中InnoDB的锁定机制就是行级锁定。

行级锁定的类型包括3种:排他锁、共享锁和意向锁。

(1) 排他锁(eXclusive Locks)

排他锁又称为X锁。如果事务T1获得了数据行D上的排他锁,则T1对数据行既可读又可写。事务T1对数据行D加上排他锁,则其他事务对数据行D的任务封锁请求都不会成功,直至事务T1释放数据行D上的排他锁。

(2) 共享锁(Share Locks)

共享锁又称为S锁。如果事务T1获得了数据行D上的共享锁,则T1对数据项D可以读但不可以写。事务T1对数据行D加上共享锁,则其他事务对数据行D的排他锁请求不会成功,而对数据行D的共享锁请求可以成功。

(3) 意向锁

意向锁是一种表锁,锁定的粒度是整张表,分为意向共享锁(IS)和意向排他锁(IX)两类。意向锁表示一个事务有意对数据上共享锁或排他锁。

意向共享锁(IS):事务打算给数据行加共享锁,事务在取得一个数据行的共享锁之前必须先取得该表的IS锁。

意向排他锁(IX):事务打算给数据行加排他锁,事务在取得一个数据行的排他锁之前必须先取得该表的IX锁。

3. 页锁

MySQL将锁定表中的某些行(称作页)。被锁定的行只对锁定最初的线程是可行的。

BDB表支持页级锁。页级锁开锁和加锁时间介于表级锁和行级锁之间,会出现死锁,锁定粒度介于表级锁和行级锁之间。

InnoDB表速度很快,比BDB更有性能优势,InnoDB表适合执行大量的insert或update数据操作。影响InnoDB类型的表速度的主要原因是autocommit默认设置是打开的,如果程序没有显式调用begin 开始事务,会导致每插入一条数据都会自动提交,严重影响了速度

9.4.3 死锁的管理

1. 死锁的原因

两个或两个以上的事务分别申请封锁对方已经封锁的数据对象,导致长期等待而无法继续运行下去的现象称为死锁。MySQL对并发事务的处理,使用任何方案都会导致死锁问题。

在下面两种情况会经常发生死锁现象。

第1种情况是,两个事务分别锁定了两个单独的对象,这时每一个事务都要求在另外一个事务锁定的对象上获得一个锁,结果是每一个事务都必须等待另外一个事务释放占有的锁,此时就发生了死锁。这种死锁是最典型的死锁形式。

第2种情况是,在一个数据库中,有若干长时间运行的事务并行的执行操作,查询分析器处理非常复杂的查询时,例如连接查询,由于不能控制处理的顺序,有可能发生死锁。

死锁是指事务永远不会释放它们所占用的锁,死锁中的两个事务都将无限期等待下去。MySQL的InnoDB Engine自动检测死锁循环,并选择一个会话作为死锁中放弃的一方,通过终止该事务来打断死锁。被终止的事务发生回滚,并返回给连接一个错误消息。

如果在交互式的MySQL语句中发生死锁错误,用户只要简单地重新输入该语句即可。

2. 死锁的处理

默认情况下:InnoDB存储引擎一旦出现锁等待超时异常,InnoDB存储引擎即不会提交事务,也不会回滚事务,而这是十分危险的。一旦发生锁等待超时异常,应用程序应该自定义错误处理程序,由程序开发人员选择进一步提交事务,还是回滚事务。

在InnoDB的事务管理和锁定机制中,有专门用于检测死锁的机制。当检测到死锁时,InnoDB会选择产生死锁的两个事务中较小的一个产生回滚,而让另外一个较大的事务成功完成。那么如何判断事务的大小呢?主要是通过计算两个事务各自插入、更新或者删除的数据量来判断,也就是说哪个事务改变的记录数越多,在死锁中越不会被回滚。

3. 事务与锁机制注意事项

(1)锁的粒度越小,应用系统的并发性能就越高,由于InnoDB存储引擎支持行锁,建议使用InnoDB存储引擎表以提高系统的可靠性。

(2)使用事务时,尽量避免一个事务中使用不同存储引擎的表。

(3)处理事务时尽量设置和使用较低的隔离级别。

(4)尽量使用基于行锁控制的隔离级别。必要时使用表锁,可以避免死锁现象。

(5)对于InnoDB存储引擎支持的行锁,设置合理的超时参数范围,编写锁等待超时异常处理程序,可以解决锁等待问题。

(6)为避免死锁,事务进行多记录修改时,尽量在获得所有记录的排它锁后,再进行修改操作。

(7)为避免死锁,尽量缩短锁的生命周期,保持事务简短并处于一个批处理中。

(8)为避免死锁,事务中尽量按照同一顺序访问数据库对象,避免在事务中存在用户交互访问数据的情况。

posted @ 2024-05-11 21:51  WNAG_zw  阅读(5)  评论(0编辑  收藏  举报