MySQL数据库——事务

1 事务处理

事务处理在数据库开发过程中有着非常重要的作用,它可以保证在同一个事务中的操作具有同步性。本节将针对事务处理的基础知识进行讲解。

1.1 事务的概念

现实生活中,人们经常会进行转账操作,转账可以分为转人和转出两部分,只有这两个部分都完成才认为转账成功。在数据库中,这个过程是使用两条SQL语句来实现的,如果其中任意一条语句出现异常没有执行,则会导致两个账户的金额不同步,造成错误。为了防止上述情况的发生,就需要使用MySQL中的事务(Transaction)。

在MySQL中,事务就是针对数据库的一组操作,它可以由一条或多条SQL语句组成,且每个SQL语句是相互依赖的。m只要在程序执行过程中有一条SQL语句执行失败或发生,错误,则其他语句都不会执行。也就是说,事务的执行要么成功,要么就返回到事务开始前的状态,这就保证了同一事务操作的同步性和数据的完整性。MySQL中的事务必须满足A、C.I、D这4个基本特性,具体如下。

(1)原子性(Atomicity)。原子性是指一个事务必须被视为一个不可分割的最小工作单元,只有事务中所有的数据库操作都执行成功,才算整个事务执行成功。事务中如果有任何一个SQL语句执行失败,已经执行成功的SQL语句也必须撤销,数据库的状态退回到执行事务前的状态。

(2)一致性(Consistency)。一致性是指在事务处理时,无论执行成功还是失败,都要保证数据库系统处于一致的状态,保证数据库系统不会返回到一个未处理的事务中。MySQL中的一致性主要由日志机制实现,通过日志记录数据库的所有变化,为事务恢复提供了跟踪记录。

(3)隔离性(Isolation)。隔离性是指当一个事务在执行时,不会受到其他事务的影响。保证了未完成事务的所有操作与数据库系统的隔离,直到事务完成为止,才能看到事务的执行结果。隔离性相关的技术有并发控制、可串行化、锁等。当多个用户并发访问数据库时,数据库为每一个用户开启的事务,不能被其他事务的操作数据所干扰,多个并发事务之间要相互隔离。

(4)持久性(Durability)。持久性是指事务一旦提交,其对数据库的修改就是永久性的。需要注意的是,事务的持久性不能做到百分百的持久,只能从事务本身的角度来保证永久性,而一些外部原因导致数据库发生故障,如硬盘损坏,那么所有提交的数据可能都会丢失。

1.2 事务的基本操作

在默认情况下,用户执行的每一条SQL语句都会被当成单独的事务自动提交。如果要将一组SQL语句作为一个事务,则需要先执行以下语句显式地开启一个事务。

START TRANSACTION;

上述语句执行后,每一.条SQL语句不再自动提交,用户需要使用以下语句手动提交,只有事务提交后,其中的操作才会生效。

COMMIT;

如果不想提交当前事务,可以使用如下语句取消事务(即回滚)。

ROLLBACK;

需要注意的是,ROLLBACK只能针对未提交的事务回滚,已提交的事务无法回滚。当执行COMMIT或ROLLBACK后,当前事务就会自动结束。
为了让读者更好地学习事务,接下来通过一个转账的案例来演示如何使用事务。选择前面创建的shop数据库,查看sh_user表中的用户数据,具体操作如下。

# ① 选择数据库
mysql> USE shop;
# ② 查看用户数据
mysql> SELECT name, money FROM sh_user;
+------+---------+
| name | money   |
+------+---------+
| Alex | 1000.00 |
| Bill | 1000.00 |
+------+---------+
2 rows in set (0.00 sec)

接下来开启一个事务,通过UPDATE语句将Alex用户的1000元钱转给Bill用户,最后提交事务,具体操作如下。

# ① 开启事务
mysql> START TRANSACTION;
# ② Alex减少100元
mysql> UPDATE sh_user SET money = money - 100 WHERE name = 'Alex';
# ③ Bill增加100元
mysql> UPDATE sh_user SET money = money + 100 WHERE name = 'Bill';
# ④ 提交事务
mysql> COMMIT;

上述操作完成后,使用SELECT语句查询Alex和Bill的金额,结果如下。

mysql> SELECT name, money FROM sh_user;
+------+---------+
| name | money   |
+------+---------+
| Alex |  900.00 |
| Bill | 1100.00 |
+------+---------+
2 rows in set (0.00 sec)

从查询结果可以看出,通过事务成功地完成了转账功能。接下来测试事务的回滚,开启事务后,将Bill 的金额扣除100元,具体操作如下。

# ① 开启事务
mysql> START TRANSACTION;
# ② Bill扣除100元
mysql> UPDATE sh_user SET money = money - 100 WHERE name = 'Bill';
# ③ 查询Bill的金额
mysql> SELECT name, money FROM sh_user WHERE name = 'Bill';
+------+---------+
| name | money   |
+------+---------+
| Bill | 1000.00 |
+------+---------+
1 row in set (0.00 sec)

上述操作完成后,执行回滚操作,然后查询Bill的金额,结果如下。

# ① 回滚事务
mysql> ROLLBACK;
# ② 查看Bill的金额
mysql> SELECT name, money FROM sh_user WHERE name = 'Bill';
+------+---------+
| name | money   |
+------+---------+
| Bill | 1100.00 |
+------+---------+
1 row in set (0.00 sec)

从查询结果可以看出,Bill的金额又恢复成1100元,说明事务回滚成功。

注意
(1) MySQL中的事务不允许嵌套,若在执行START TRANSACTION语句前上一个事务还未提交,会隐式地执行提交操作。
(2)事务处理主要是针对数据表中数据的处理,不包括创建或删除数据库、数据表,修改表结构等操作,而且执行这类操作时会隐式地提交事务。 (3)
MySQL
5.7默认的存储引擎为InnoDB,该存储引擎支持事务,而另一个常见的存储引擎MyISAM不支持事务。对于MyISAM存储引擎的数据表,无论事务是否提交,对数据的操作都会立即生效,不能回滚。
(4)在MySQL中,还可以使用START TRANSACTION的别名BEGIN或BEGIN
WORK来显式地开启一个事务。但由于BEGIN与MySQL编程中的BEGIN… END冲突,因此不推荐使用BEGIN。

多学一招:事务的自动提交 MySQL默认是自动提交模式,如果没有显式开启事务(START
TRANSACTION),每一条SQL语句都会自动提交(COMMIT)。如果用户想要控制事务的自动提交方式,可以通过更改AUTOCOMMIT变量来实现,将其值设为1表示开启自动提交,设为0表示关闭自动提交。若要查看当前会话的AUTOCOMMIT值,使用如下语句。

+--------------+ | @@autocommit |
+--------------+ |            1 |
+--------------+ 1 row in set (0.00 sec) ```

从查询结果可以看出,当前会话开启了事务的自动提交。若要关闭当前会话的事务自动提交,可以使用如下语句。

```sql SET AUTOCOMMIT =0; ```

上述语句执行后,用户需要手动执行提交(COMMIT)操作,才会提交事务。否则,若直接终止MySQL会话,MySQL会自动进行回滚。

1.3 事务的保存点

在回滚事务时,事务内所有的操作都将撤销。而若希望只撤销一部分,可以用保存点来实现。使用以下语句可以在事务中设置一个保存点。

SAVEPOINT 保存点名;

在设置保存点后,使用以下语句可以将事务回滚到指定保存点。

ROLLBACK TO SAVEPOINT保仔点名;

使用时若不再需要一个保存点,可以使用如下语句删除保存点。

RELEASE SAVEPOINT保存点名:

值得一提的是,一个事务中可以创建多个保存点,在提交事务后,事务中的保存点就会被删除。另外,在回滚到某个保存点后,在该保存点之后创建过的保存点也会消失。

接下来通过案例演示事务保存点的使用。首先查询Alex的金额,如下所示。

mysql> SELECT name, money FROM sh_user WHERE name = 'Alex';
+------+--------+
| name | money  |
+------+--------+
| Alex | 900.00 |
+------+--------+
1 row in set (0.00 sec)

从查询结果可知,Alex当前金额为900元。然后开启事务,将Alex的金额扣除100元后,创建保存点s1,再将Alex的金额扣除50元,如下所示。

# ① 开启事务
mysql> START TRANSACTION;
# ② Alex扣除100元
mysql> UPDATE sh_user SET money = money - 100 WHERE name = 'Alex';
# ③ 创建保存点s1
mysql> SAVEPOINT s1;
# ④ Alex再扣除50元
mysql> UPDATE sh_user SET money = money - 50 WHERE name = 'Alex';

完成上述操作后,将事务回滚到保存点s1,然后查询Alex的金额,如下所示。

# ① 回滚到保存点s1
mysql> ROLLBACK TO SAVEPOINT s1;
# ② 查询Alex的金额
mysql> SELECT name, money FROM sh_user WHERE name = 'Alex';
+------+--------+
| name | money  |
+------+--------+
| Alex | 800.00 |
+------+--------+
1 row in set (0.00 sec)

在上述结果中,Alex的金额只减少了100元,说明当前恢复到了保存点s1时的数据状态。再次执行回滚操作,则恢复到事务开始时的状态,如下所示。

# ① 回滚事务
mysql> ROLLBACK;
# ② 查看Alex的金额
mysql> SELECT name, money FROM sh_user WHERE name = 'a';
+------+--------+
| name | money  |
+------+--------+
| Alex | 900.00 |
+------+--------+
1 row in set (0.00 sec)

上述结果显示的金额与事务开始时的金额相同,说明事务回滚成功。

多学一招:控制事务结束后的行为

事务的提交(COMMIT)和回滚(ROLLBACK)还有一些可选子句,如下所示。

CHAIN] [[NO] RELEASE] ```

在上述选项中,AND
CHAIN用于在当前事务结束时,立即启动一个新事务,并且新事务与刚结束的事务有相同的隔离级;RELEASE用于在终止当前事务后,让服务器断开与客户端的连接。若添加NO,则表示抑制CHAINRELEASE完成。

2 事务隔离级别

由于数据库是一个多用户的共享资源,MySQL允许多线程并发访问,因此用户可以通过不同的线程执行不同的事务。为了保证这些事务之间不受影响,对事务设置隔离级是十分必要的。本节将针对事务的隔离级别进行详细讲解。

2.1 查看隔离级别

对于隔离级的查看,MySQL提供了以下几种不同的方式,具体使用哪种方式查询还需根据实际需求进行选择,具体如下。

# ① 查看全局隔离级
SELECT @@global.transaction_isolation;
# ② 查看当前会话中的隔离级
SELECT @@session.transaction_isolation;
# ③ 查看下一个事务的隔离级
SELECT @@transaction_isolation;

在以上语句中,全局的隔离级影响的是所有连接MySQL的用户,而当前会话的隔离级别只影响当前正在登录MySQL服务器的用户,不会影响其他用户。而下一个事务的隔离级别仅对当前用户的下一个事务操作有影响。

在默认情况下,上述3种方式返回的结果都是REPEATABLE-READ,表示隔离级别为可重复读。以第3种方式为例,查询结果如下所示。

mysql> SELECT @@transaction_isolation;
+-------------------------+
| @@transaction_isolation |
+-------------------------+
| REPEATABLE-READ         |
+-------------------------+
1 row in set (0.00 sec)

除了以上的REPEATABLE READ(可重复读)外,MySQL中事务的隔离级别还有READ UNCOMMITTED(读取未提交)、READ COMMITTED(读取提交)和SERIALIZABLE(可串行化)。具体内容会在后面的小节中详细讲解。

2.2 修改隔离级别

在MySQL中,事务的隔离级别可以通过SET语句进行设置,具体语法如下。
SET [SESSION | GLOBAL] TRANSACTION ISOLATION LEVEL 参数值
在上述语法中,SET后的SESSION表示当前会话,GLOBAL表示全局,若省略表示设置下一个事务的隔离级。TRANSACTION表示事务,ISOLATION表示隔离,LEVEL表示级别。参数值可以是READ UNCOMMITTED、READ COMMITTED、REPEATABLE READ或SERIALIZABLE中的一种。

下面演示将事务的隔离级别修改为READ UNCOMMITTED,具体如下。

# ① 修改事务隔离级别
mysql> SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
Query OK, 0 rows affected (0.00 sec)
# ② 查看是否修改成功
mysql> SELECT @@session.transaction_isolation;
+---------------------------------+
| @@session.transaction_isolation |
+---------------------------------+
| READ-UNCOMMITTED                |
+---------------------------------+
1 row in set (0.00 sec)

从上述结果可以看出,当前事务的隔离级别已经修改为READ UNCOMMITTED。
接下来将事务的隔离级别修改为默认的REPEATABLE READ,具体如下。

# ① 修改事务隔离级别
mysql> SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
Query OK, 0 rows affected (0.00 sec)
# ② 查看是否修改成功
mysql> SELECT @@session.transaction_isolation;
+---------------------------------+
| @@session.transaction_isolation |
+---------------------------------+
| REPEATABLE-READ                 |
+---------------------------------+
1 row in set (0.00 sec)

从上述结果可以看出,当前事务的隔离级别已经修改为REPEATABLEREAD。

多学一招:只读事务
默认情况下,事务的访问模式为READ
WRITE(读/写模式),表示事务可以执行读(查询)或写(更改、插入、删除等)操作。若开发需要,可以将事务的访问模式设置为READ
ONLY(只读模式),禁止对表进行更改。具体SQL语句如下。

# ① 设置只读事务 
SET [SESSION | GLOBAL] TRANSACTION READ ONLY
# ② 恢复成读写事务 
SET [SESSION | GLOBAL] TRANSACTION READ WRITE ```

2.3 MySQL的4种隔离级别

MySQL中事务隔离级别有.READUNCOMMITTED(读取未提交)、READ COMMITTED(读取提交)、REPEATABLE READ(可重复读)和SERIALIZABLE(可串行化)。下面针对每种隔离级别的特点、带来的问题以及解决方案进行详细讲解。

1.READ UNCOMMITTED(读取未提交)
READUNCOMMITTED是事务中最低的级别,在该级别下的事务可以读取到其他事务中未提交的数据,这种读取的方式也被称为脏读(DirtyRead)。简而言之,脏读是指一个事务读取了另外一个事务未提交的数据。

例如,Alex要给Bill转账100元购买商品,Alex开启事务后转账,但不提交事务,通知Bill来查询,如果Bill的隔离级别较低,就会读取到Alex的事务中未提交的数据,发现Alex确实给自己转了100元,就给Alex发货。等Bill发货成功后,Alex将事务回滚,Bill就会受到损失,这就是脏读造成的。

为了演示和解决上述情况,首先需要开启两个命令行窗口,分别登录到MySQL数据库,执行USE shop切换到shop数据库。然后使用这两个窗口分别模拟Alex和Bill,以下称为客户端A和客户端B。准备完成后,按照如下步骤进行操作。

(1)设置客户端B的事务隔离级别。由于MySQL默认的隔离级别REPEATABLE READ可以避免脏读,为了演示脏读,需要将客户端B的隔离级别设为较低的READ UNCOMMITTED,具体如下。

# 客户端B
mysql> SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; 

(2)演示客户端B的脏读。在客户端B中查询Bill当前的金额,具体如下。

# 客户端B
mysql> SELECT name, money FROM sh_user WHERE name = 'Bill';
+------+---------+
| name | money   |
+------+---------+
| Bill | 1100.00 |
+------+---------+
1 rows in set (0.00 sec) 

此时客户端A未提交事务,客户端B查询金额,会看到金额已经增加,如下所示。

# 客户端A
mysql> START TRANSACTION;
mysql> UPDATE sh_user SET money = money - 100 WHERE name = 'Alex';
mysql> UPDATE sh_user SET money = money + 100 WHERE name = 'Bill';

(3)避免客户端B的脏读。将客户端B的事务隔离级设为READ COMMITTED(或更高级别)可以避免脏读。设置后再次查询Bill的金额,如下所示。

# 客户端B
mysql> SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
mysql> SELECT name, money FROM sh_user WHERE name = 'Bill';
+------+---------+
| name | money   |
+------+---------+
| Bill | 1100.00 |
+------+---------+
1 row in set (0.00 sec) 
# 客户端A
mysql> ROLLBACK;

从上述结果可以看出,由于客户端A没有提交事务,客户端B读取到了客户端A提交前的结果,说明READCOMMITTED级别可以避免脏读。客户端B操作完成后,回滚客户端A的事务,以免影响后面的案例演示。

值得一提的是,脏读在实际应用中会带来很多问题,除非用户有很好的理由,否则,为了保证数据的一致性,在实际应用中几乎不会使用这个隔离级别。

2.READ COMMITTED(读取提交)

READ COMMITTED是大多数DBMS(如SQL Server、Oracle)的默认隔离级,但不包括MySQL。在该隔离级下只能读取其他事务已经提交的数据,避免了脏读数据的现象。但是在该隔离级别下,会出现不可重复读(NON-REPEATABLE READ)的问题。

不可重复读是指在一个事务中多次查询的结果不一致,原因是查询的过程中数据发生了改变。例如,在网站后台统计所有用户的总金额,第1次查询Alex有900元,为了验证查询结果,第2次查询Alex有800元,两次查询结果不同,原因是第2次查询前Alex取出了100元。

为了读者更好地理解,接下来就通过案例演示和解决以上不可重复读的情况。假设客户端A是Alex用户,客户端B是网站后台,具体操作步骤如下。

(1)演示客户端B的不可重复读。当客户端B的事务隔离级为READ COMMITTED时,会出现不可重复读的情况。在客户端B中开启事务,查询Alex的金额,然后在客户端A中将Alex的金额扣除100元,最后客户端B再次查询Alex的金额,具体如下。

# 客户端B
mysql> SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
mysql> START TRANSACTION;
mysql> SELECT name, money FROM sh_user WHERE name = 'Alex';
+------+--------+
| name | money  |
+------+--------+
| Alex | 900.00 |
+------+--------+
1 row in set (0.00 sec)
# 客户端A
mysql> UPDATE sh_user SET money = money - 100 WHERE name = 'Alex';
# 客户端B
mysql> SELECT name, money FROM sh_user WHERE name = 'Alex';
+------+--------+
| name | money  |
+------+--------+
| Alex | 800.00 |
+------+--------+
1 row in set (0.00 sec)
# 客户端B
mysql> COMMIT;

从上述结果可以看出,客户端B在同一个事务中两次查询的结果不一致,这就是不可重复读的情况。操作完成后,将客户端B的事务提交,以免影响后面的演示。

(2)避免网站后台的不可重复读。将客户端B的事务隔离级别设为默认级别REPEATABLE READ,可以避免不可重复读的情况。在该级别下按照上一步的方式重新测试,结果如下。

# 客户端B
mysql> SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
mysql> START TRANSACTION;
mysql> SELECT name, money FROM sh_user WHERE name = 'Alex';
+------+--------+
| name | money  |
+------+--------+
| Alex | 800.00 |
+------+--------+
1 row in set (0.00 sec)

# 客户端A
mysql> UPDATE sh_user SET money = money + 100 WHERE name = 'Alex';
# 客户端B
mysql> SELECT name, money FROM sh_user WHERE name = 'Alex';
+------+--------+
| name | money  |
+------+--------+
| Alex | 800.00 |
+------+--------+
1 row in set (0.00 sec)
# 客户端B
mysql> COMMIT;

从上述结果可以看出,客户端B两次查询的结果是相同的,说明REPEATABLEREAD可以避免不可重复读的情况。最后将客户端B的事务提交,以免影响后面的案例演示。

3.REPEATABLE READ(可重复读)

REPEATABLE READ是MySQL的默认事务隔离级,它解决了脏读和不可重复读的问题,确保了同一事务的多个实例在并发读取数据时,会看到同样的结果。

但在理论上,该隔离级会出现幻读(PHANTOMREAD)的现象。幻读又被称为虚读,是指在一个事务内两次查询中数据条数不–致,幻读和不可重复读有些类似,同样发生在两次查询过程中。不同的是,幻读是由于其他事务做了插入记录的操作,导致记录数有所增加。不过,MySQL的InnoDB存储引擎通过多版本并发控制机制解决了幻读的问题。

例如,在网站后台统计所有用户的总金额时,当前只有两个用户,总金额为2000 元,若在此时新增一个用户,并且存人1000元,再次统计时发现总金额变为3000元,造成了幻读的情况。

为了读者更好地理解,接下来就通过案例演示和解决以上幻读的情况。假设客户端A用于新增用户,客户端B用于统计金额,具体操作步骤如下。

(1)演示客户端B的幻读。由于客户端B当前的隔离级别REPEATABLEREAD可以避免幻读,因此需要将级别降低为READ COMMITTED。降低后,开启事务,统计总金额,然后在客户端A中插入一条新记录Tom用户,再次统计总金额,具体如下。

# 客户端B
mysql> SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
mysql> START TRANSACTION;
mysql> SELECT SUM(money) FROM sh_user;
+------------+
| SUM(money) |
+------------+
|    2000.00 |
+------------+
1 row in set (0.00 sec)

# 客户端A
mysql> INSERT INTO sh_user (id, name, money) VALUES 
    -> (3, 'Tom', 1000);
# 客户端B
mysql> SELECT SUM(money) FROM sh_user;
+------------+
| SUM(money) |
+------------+
|    3000.00 |
+------------+
1 row in set (0.00 sec)

# 客户端B
mysql> COMMIT;

从上述结果可以看出,两次统计的结果不同,这就是幻读的情况。操作完成后,将客户端B的事务提交,以免影响后面的演示。

(2)避免客户端B的幻读。将客户端B的隔离级别设置为REPEATABLE READ,即可避免幻读,结果如下。

# 客户端B
mysql> SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
mysql> START TRANSACTION;
mysql> SELECT SUM(money) FROM sh_user;
+------------+
| SUM(money) |
+------------+
|    3000.00 |
+------------+
1 row in set (0.00 sec)

# 客户端A
mysql> INSERT INTO sh_user (id, name, money) VALUES 
    -> (4, 'd', 1000);
# 客户端B
mysql> SELECT SUM(money) FROM sh_user;
+------------+
| SUM(money) |
+------------+
|    3000.00 |
+------------+
1 row in set (0.00 sec)

# 客户端B
mysql> COMMIT;

从上述结果可以看出,客户端B两次统计的结果是相同的,说明REPEATABLEREAD级别可以避免幻读的情况。最后将客户端B的事务提交,以免影响后面的案例演示。

4.SERIALIZABLE(可串行化)

SERIALIZABLE是最高级别的隔离级,它在每个读的数据行上加锁,使之不会发生冲突,从而解决了脏读、不可重复读和幻读的问题。但是由于加锁可能导致超时(Timeout)和锁竞争(Lock Contention)现象,因此SERIALIZABLE也是性能最低的一种隔离级。除非为了数据的稳定性,需要强制减少并发的情况时,才会选择此种隔离级。

为了读者更好地理解,接下来就通过案例演示超时的情况。假设客户端B执行查询操作,客户端A执行更新操作,具体操作步骤如下。
(1)演示可串行化。将客户端B的事务隔离级别设置为SERIALIZABLE,然后开启事务,具体如下。

# 客户端B
mysql> SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;
mysql> START TRANSACTION;

在客户端B开启事务后,查看Alex的金额,具体如下。

# 客户端B
mysql> SELECT name, money FROM sh_user WHERE name = 'Alex';
+------+--------+
| name | money  |
+------+--------+
| Alex | 900.00 |
+------+--------+
1 row in set (0.00 sec)

在客户端A中将Alex的金额增加100元,会发现UPDATE操作一直在等待,而不是立即处理成功,如下所示。

# 客户端A
mysql> UPDATE sh_user SET money = money + 100 WHERE name = 'Alex';
(此时光标在不停闪烁,进入等待状态)

(2)提交客户端B的事务。在客户端A的UPDATE操作等待时,提交客户端B的事务,客户端A的操作才会执行,提示执行结果,具体如下。

# 客户端B
mysql> COMMIT;
# 客户端A
Query OK, 1 row affected (3.99 sec)
Rows matched: 1  Changed: 1  Warnings: 0

若客户端B一直未提交事务,客户端A的操作会一直等待,直到超时后,出现如下提示信息,表示锁等待超时,尝试重新启动事务。

# 客户端A
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting
transaction
在默认情况下,锁等待的超时时间为50秒,可以通过如下语句查询。
mysql> SELECT @@innodb_lock_wait_timeout;
+----------------------------+
| @@innodb_lock_wait_timeout |
+----------------------------+
|                         50 |
+----------------------------+
1 row in set (0.00 sec)

从上述情况可以看出,如果一个事务使用了SERIALIZABLE隔离级别,在这个事务没有被提交前,其他会话只能等到当前操作完成后,才能进行操作,这样会非常耗时,而且会影响数据库的并发性能,所以通常情况下不会使用这种隔离级别。


超全面的测试IT技术课程,0元立即加入学习!有需要的朋友戳:


腾讯课堂测试技术学习地址

欢迎转载,但未经作者同意请保留此段声明,并在文章页面明显位置给出原文链接。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值