mysql innodb 锁_MySQL/InnoDB锁机制

显式加锁

select ... lock in share mode:加 S 锁

select ... for update:加 X 锁

MySQL快照读和当前读

在一个支持MVCC并发控制的系统中,哪些读操作是快照读?哪些操作又是当前读呢?以MySQL InnoDB为例:

快照读:简单的select操作,属于快照读,不加锁。(当然,也有例外,下面会分析)

select * from table where ?;

当前读:特殊的读操作,插入/更新/删除操作,属于当前读,需要加锁。

select * from table where ? lock in share mode;

select * from table where ? for update;

insert into table values (…);

update table set ? where ?;

delete from table where ?;

所有以上的语句,都属于当前读,读取记录的最新版本。并且,读取之后,还需要保证其他并发事务不能修改当前记录,对读取记录加锁。其中,除了第一条语句,对读取记录加S锁 (共享锁)外,其他的操作,都加的是X锁 (排它锁)。

为什么将 插入/更新/删除 操作,都归为当前读?可以看看下面这个 更新 操作,在数据库中的执行流程:

9447e14962c6404af6d2c6496a45c529.png

从图中,可以看到,一个Update操作的具体流程。当Update SQL被发给MySQL后,MySQL Server会根据where条件,读取第一条满足条件的记录,然后InnoDB引擎会将第一条记录返回,并加锁 (current read)。待MySQL Server收到这条加锁的记录之后,会再发起一个Update请求,更新这条记录。一条记录操作完成,再读取下一条记录,直至没有满足条件的记录为止。因此,Update操作内部,就包含了一个当前读。同理,Delete操作也一样。Insert操作会稍微有些不同,简单来说,就是Insert操作可能会触发Unique Key的冲突检查,也会进行一个当前读。

注:根据上图的交互,针对一条当前读的SQL语句,InnoDB与MySQL Server的交互,是一条一条进行的,因此,加锁也是一条一条进行的。先对一条满足条件的记录加锁,返回给MySQL Server,做一些DML操作;然后在读取下一条加锁,直至读取完毕。

select ... lock in share mode

官方文档解释

SELECT ... LOCK IN SHARE MODE sets a shared mode lock on the rows read. A shared mode lock enables other sessions to read the rows but not to modify them. The rows read are the latest available, so if they belong to another transaction that has not yet committed, the read blocks until that transaction ends.

在读取的行上设置一个共享模式的锁。这个共享锁允许其它session读取数据但不允许修改它。 行读取的是最新的数据,如果他被其它事务使用中而没有提交,读取锁将被阻塞直到那个事务结束。

lock in share mode示例一

session1

mysql> begin;

Query OK, 0 rows affected (0.00 sec)

mysql> select * from t1 where b = 4000 lock in share mode;

+----+------+

| a  | b    |

+----+------+

| 53 | 4000 |

| 54 | 4000 |

+----+------+

2 rows in set (0.00 sec)

session2

mysql> begin;

Query OK, 0 rows affected (0.00 sec)

mysql> select * from t1 where b= 4000;

+----+------+

| a  | b    |

+----+------+

| 53 | 4000 |

| 54 | 4000 |

+----+------+

2 rows in set (0.00 sec)

mysql> update t1 set b = 2999 where a = 53;

因为session1给查询结果加了读锁,当session2更新a=53这条数据时,session1里加的读锁拒绝修改,所以session2事务在这里阻塞,即发生死锁,当超过一定时间后,该事务执行失败,这是因为mysql的死锁检测起作用。

session1

mysql> commit;

Query OK, 0 rows affected (0.00 sec)

session1执行commit,提交事务,解除了对查询结果的读锁,也就是共享锁,这时,session2的update语句执行。

session2

Query OK, 1 row affected (20.83 sec)

Rows matched: 1  Changed: 1  Warnings: 0

mysql> select * from t1 where a = 53;

+----+------+

| a  | b    |

+----+------+

| 53 | 2999 |

+----+------+

1 row in set (0.00 sec)

mysql> commit;

Query OK, 0 rows affected (0.04 sec)

session1

mysql> begin;

Query OK, 0 rows affected (0.00 sec)

mysql> select * from t1 where a = 53;

+----+------+

| a  | b    |

+----+------+

| 53 | 2999 |

+----+------+

1 row in set (0.00 sec)

lock in share mode示例二

session1

mysql> begin;

Query OK, 0 rows affected (0.00 sec)

mysql> select * from t1 where b = 4000 lock in share mode;

+----+------+

| a  | b    |

+----+------+

| 54 | 4000 |

+----+------+

1 row in set (0.00 sec)

session2

mysql> begin;

Query OK, 0 rows affected (0.00 sec)

mysql> select * from t1 where b= 4000 lock in share mode;

+----+------+

| a  | b    |

+----+------+

| 54 | 4000 |

+----+------+

1 row in set (0.00 sec)

可以看出,当session1加了共享锁后,其他事务还可以给其加共享锁。。。。

select ... for update

官方解释

SELECT ... FOR UPDATE sets an exclusive lock on the rows read. An exclusive lock prevents other sessions from accessing the rows for reading or writing.

在读取行上设置一个排他锁。组织其他session读取或者写入行数据

select ... for update示例一

session1

mysql> begin;

Query OK, 0 rows affected (0.00 sec)

mysql> select * from t1 where b 

+----+------+

| a  | b    |

+----+------+

| 51 | 2000 |

| 52 | 2999 |

| 53 | 2999 |

| 57 | 2000 |

+----+------+

4 rows in set (0.06 sec)

mysql>

session2

mysql> begin;

Query OK, 0 rows affected (0.00 sec)

mysql> select * from t1 where b 

+----+------+

| a  | b    |

+----+------+

| 51 | 2000 |

| 52 | 2999 |

| 53 | 2999 |

| 57 | 2000 |

+----+------+

4 rows in set (0.00 sec)

mysql> select * from t1 where b 

ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

mysql>

在该事务中在加排它锁,过一段时间后,报错。但能读取数据。

最近写的文章的参考,希望对你有帮助。

========END========

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值