读锁:又称共享锁(S锁)。若事务T对数据对象A(某一资源)加上S锁,则事务T可以读A但不能修改A。其它事务也只能读A但不能修改A。并且,其他事务只能再对A加S锁,不能加X锁,除非T释放A上的S 锁。简单的说,自己只能读,别人也只能读。
写锁:又称排他锁(X锁)、独占锁。若事务T对数据对象A(某一资源)加上X锁,事务T可以读A也可以修改A。其它事务不能读A也不能修改A。并且,其他事务不能再对A加任何锁(共享锁或排他锁),直到T释放A上的锁。简单的说,自己可读可写,别人不可读不可写。
行级锁操作
加排它锁:select ...for update
加共享锁:select ... lock in share mode
几个注意的点
- mysql InnoDB引擎的数据库,增删改操作默认都会加排他锁,而查询不会加任何锁。
- 自己对某一资源加共享锁,别人也可以再继续加共享锁,即多个共享锁可以共存。
- 数据库同一资源上,共享锁和排他锁不能共存,排他锁和排他锁不能共存。
- 同一资源上,要么不存在任何锁,要么存在单个或多个共享锁,要么存在单个排它锁。
准备测试数据
测试for update 排他锁
现在我们对id=1的数据行进行排他查询,这里会使用BEGIN开启事务,而不去COMMIT提交事务,这样做是为了保证锁不被释放,因为提交事务或回滚事务都会释放锁。
窗口1
此时会查询到一条数据,现在打开另一个查询窗口,对同一数据分别使用排他查询和共享查询。
排他查询:
结果:一直处于阻塞状态,没有返回查询结果集。
共享查询:
结果:也是一直处于阻塞状态,没有返回查询结果集。
分析:一个事务中的查询加了排它锁,且事务没有提交。另一个事务中的排他锁查询和共享锁查询都会处于阻塞状态。这是因为id=1的数据已经被加上了排他锁,并且该锁还未释放,阻塞表明是在等待排他锁释放。另外,说明一下,第二个窗口下的查询操作并没有开启一个事务,但属于其它事务中的查询。Why?因为在默认的自动事务提交设置下 ,select 同Update、Insert、Delete一样都会启动一个隐式的事务。窗口1中的事务是显示事务的使用。
窗口2中直接查询呢?
结果:直接查询可以查询到数据,不受影响。
窗口1中打开事务提交注释,窗口2中再排他锁查询或共享锁查询:
结果:窗口1中事务一旦提交,窗口2中阻塞状态立刻解除。
mysql事务管理
在 MySQL 中默认情况下每一条增、删、改 SQL 语句都是一个单独的事务,语句执行完后自动提交事务。如果业务中有多条 SQL 语句就需要使用事务相关的 SQL 语句来手动的开启、提交事务。
测试LOCK IN SHARE MODE 共享锁
我们再看一下,一个事务中加了共享锁,在其他事务查询中也加共享锁或不加锁的情况。
窗口1中共享锁的事务查询,且没有提交事务
窗口2中测试直接查询
结果:可以查出数据。
窗口2中测试共享锁查询
结果:可以查出数据。
窗口2中测试排他锁查询
结果:查询处于阻塞状态。
分析:一个事务中的查询加了共享锁,且没有提交事务,另一个事务中的不加锁查询和共享锁查询是可以查到数据的,但排他锁查询不可以,因为排他锁与共享锁不能共存于同一数据上。可以这么理解,排它锁是一种独占的锁,数据只能被自己独占,所以,也就不能存在其他锁了。
InnoDb 锁机制
最后我们验证一下上面所说的mysql InnoDb引擎中update、delete、insert语句会自动加排他锁的问题。
窗口1,事务中进行更新操作,不提交事务
结果:数据并没有更新。
窗口2中无锁查询
结果:查询到的依然是旧数据。
窗口2中共享锁查询
结果:查询处于阻塞。
窗口2中排他锁查询
结果:查询处于阻塞。
分析:一个事务中进行了修改操作(update、insert、delete),且没有提交事务。另一个事务中的直接查询(无锁查询)可以查到旧数据,排他锁查询和共享锁查询处于阻塞状态。这说明mysql数据库自动加上排他锁了。
窗口1提交事务,窗口2再排它锁查询
结果:事务2中的查询立刻解除阻塞状态,查到最新数据。
最后小结
1)一个事务中的查询加了排它锁,且没有提交事务。另一个事务中的排他锁查询和共享锁查询都会处于阻塞状态。
2)一个事务中的查询加了共享锁,且没有提交事务。另一个事务中的不加锁查询和共享锁查询可以查到数据,但排他锁查询不可以。
3)mysql InnoDB引擎的数据库,update、delete、insert 都会自动给涉及到的数据加上排他锁,select 语句默认不会加任何锁。