Mysql事务隔离级别对select in update的影响(InnoDB)

本文探讨Mysql 5.6数据库事务隔离级别read committd/repeatable read对InnoDB引擎下select in update的影响。

首先,这两种事务隔离级别都不会阻塞普通的查询语句。

但是在update语句中用到select时,如果该会话的事务隔离级别为repeatable read,且select的目标行上有排它锁,就会让select产生等待。

在此种情况下,如果在本地开启事务先执行了update A,又调用远程服务执行select A in update B,即会产生死锁。

举例说明:

准备测试数据:

CREATETABLET_A (

A_IDINTPRIMARYKEY, A_VALUEVARCHAR(10)

) ENGINE=InnoDBDEFAULTCHARSET=utf8;

CREATETABLET_B (

B_IDINTPRIMARYKEY, B_VALUEVARCHAR(10)

) ENGINE=InnoDBDEFAULTCHARSET=utf8;

INSERTINTOT_ASELECT1,'VA1';

INSERTINTOT_BSELECT1,'VB1';

Step 1:

打开查询分析器,先检查一下事务隔离级别

SELECT@@global.tx_isolation;

SELECT@@session.tx_isolation;

设置自动提交为false

SETAUTOCOMMIT =0;

更新A表一条数据,不提交

UPDATET_ASETA_VALUE =1WHEREA_ID =1;

Step 2:

在另一个客户端中打开同一个数据库的连接

验证当前的事务隔离级别是否为REPEATABLE READ或READ COMMITTED

SELECT@@session.tx_isolation;

无论何种事务隔离级别,执行对A表的查询,都可以顺利查询出来

SELECT*FROMT_AWHEREA_ID =1;

更改当前事务隔离级别为READ COMMITTED

SETSESSION TRANSACTIONISOLATIONLEVELREADCOMMITTED;

执行一个select in update

UPDATET_BSETB_VALUE = (SELECTA_VALUEFROMT_AWHEREA_ID = 1)WHEREB_ID =1;

此时可以更新目标行

更改当前事务隔离级别为REPEATABLE READ

SETSESSION TRANSACTIONISOLATIONLEVEL REPEATABLE READ;

再次执行上面的select in update

UPDATET_BSETB_VALUE = (SELECTA_VALUEFROMT_AWHEREA_ID = 1)WHEREB_ID =1;

此时更新被阻塞,在连接1中提交事务,此更新语句即可继续执行

同样的,如果连接1中的事务未提交,在连接2中尝试在A表上对A_ID=1的行获取共享锁或排它锁也是不可行的

SELECT*FROMT_AWHEREA_ID =1LOCKINSHARE MODE;

SELECT*FROMT_AWHEREA_ID =1FORUPDATE;

都会等待该行数据的锁被释放

在REPEATABLE READ模式下产生阻塞时分析锁状态:

CREATETABLEinnodb_lock_monitor (aINT) ENGINE=INNODB;

然后让select in update产生等待,分析锁状态

SHOWENGINE INNODB STATUS;

--事务2获取T_A主键上的共享锁产生等待

---TRANSACTION 222643, ACTIVE 11 sec starting index read

mysql tables in use 2, locked 2

LOCK WAIT 4 lock struct(s), heap size 1184, 2 row lock(s)

MySQL thread id 49, OS thread handle 0x7f2b58644700, query id 1061 192.168.50.57 root statistics

UPDATE T_B SET B_VALUE = (SELECT A_VALUE FROM T_A WHERE A_ID = 1) WHERE B_ID = 1

Trx read view will not see trx with id >= 222644, sees < 222642

------- TRX HAS BEEN WAITING 11 SEC FOR THIS LOCK TO BE GRANTED:

RECORD LOCKS space id 1611 page no 3 n bits 72 index `PRIMARY` of table `lovol_shop`.`T_A` trx id 222643 lock mode S locks rec but not gapwaiting

Record lock, heap no 2 PHYSICAL RECORD: n_fields 4; compact format; info bits 0

0: len 4; hex 80000001; asc     ;;

1: len 6; hex 0000000365b2; asc     e ;;

2: len 7; hex 770000025617ba; asc w   V  ;;

3: len 1; hex 31; asc 1;;

------------------

TABLE LOCK table `lovol_shop`.`T_B` trx id 222643 lock mode IX

RECORD LOCKS space id 1612 page no 3 n bits 72 index `PRIMARY` of table `lovol_shop`.`T_B` trx id 222643 lock_mode X locks rec but not gap--事务2获得了B表的排它锁

Record lock, heap no 2 PHYSICAL RECORD: n_fields 4; compact format; info bits 0

0: len 4; hex 80000001; asc     ;;

1: len 6; hex 0000000365b0; asc     e ;;

2: len 7; hex f5000001c0011d; asc        ;;

3: len 3; hex 564231; asc VB1;;

TABLE LOCK table `lovol_shop`.`T_A` trx id 222643 lock mode IS

RECORD LOCKS space id 1611 page no 3 n bits 72 index `PRIMARY` of table `lovol_shop`.`T_A` trx id 222643 lock mode S locks rec but not gapwaiting

Record lock, heap no 2 PHYSICAL RECORD: n_fields 4; compact format; info bits 0

0: len 4; hex 80000001; asc     ;;

1: len 6; hex 0000000365b2; asc     e ;;

2: len 7; hex 770000025617ba; asc w   V  ;;

3: len 1; hex 31; asc 1;;

--事务1获得了表A主键上的排它锁,但是未提交

---TRANSACTION 222642, ACTIVE 43 sec

2 lock struct(s), heap size 360, 1 row lock(s), undo log entries 1

MySQL thread id 35, OS thread handle 0x7f2b58603700, query id 1062 192.168.50.57 root init

/* ApplicationName=DBeaver Enterprise 3.8.0 - Main */ show engine innodb status

TABLE LOCK table `lovol_shop`.`T_A` trx id 222642 lock mode IX

RECORD LOCKS space id 1611 page no 3 n bits 72 index `PRIMARY` of table `lovol_shop`.`T_A` trx id 222642 lock_mode X locks rec but not gap

Record lock, heap no 2 PHYSICAL RECORD: n_fields 4; compact format; info bits 0

0: len 4; hex 80000001; asc     ;;

1: len 6; hex 0000000365b2; asc     e ;;

2: len 7; hex 770000025617ba; asc w   V  ;;

3: len 1; hex 31; asc 1;;

INNODB引擎对于普通的select来说不需要获取锁,所以任何语句都不会阻塞普通select的执行。在READ COMMITTED模式下select in update可以成功执行,说明该模式下的select in update语句与普通的select一样不会申请任何锁。

参考资料:

http://blog.csdn.net/hw_libo/article/details/39080809

http://www.cnblogs.com/zemliu/p/3502395.html

http://blog.csdn.net/xifeijian/article/details/20313977

注:

ANSI 99定义了4种事务隔离级别:

l未提交读READ UNCOMMITTED

在读数据时不会检查或使用任何锁。因此,在这种隔离级别中可能读取到没有提交的数据。

l已提交读READ COMMITTED

只读取提交的数据并等待其他事务释放排他锁。读数据的共享锁在读操作完成后立即释放。

l可重复读REPEATABLE READ

像已提交读级别那样读数据,但会保持共享锁直到事务结束。

l可序列化SERIALIZABLE

工作方式类似于可重复读。但它不仅会锁定受影响的数据,还会锁定这个范围。这就阻止了新数据插入查询所涉及的范围,这种情况可以导致幻像读。

Mysql支持全部4种隔离级别,默认级别为REPEATABLE READ,阿里云RDS默认为READ COMMITTED。

Oracle仅支持SQL标准中的READ COMMITTD及SERIALIZABLE,以及特有的READONLY模式。默认级别为READ COMMITTED。

SqlServer支持4种标准级别,以及两种使用行版本控制来读取数据的事务级别(已提交读快照READ_COMMITTED_SNAPSHOT/快照ALLOW_SNAPSHOT_ISOLATION)。默认级别为READ COMMITTED.

你可能感兴趣的:(Mysql事务隔离级别对select in update的影响(InnoDB))