1.S锁和X锁
S锁(共享锁)
又称读锁,若事务T对数据对象A加上S锁,则事务T可以读A但不能修改A,其他事务只能再对A加S锁,而不能加X锁,直到T释放A上的S锁。这保证了其他事务可以读A,但在T释放A上的S锁之前不能对A做任何修改,除非先获取A的X锁。
X锁(排他锁)
又称写锁,若事务T对数据对象A加上X锁,事务T可以读A也可以修改A,其他事务不能再对A加任何锁,直到T释放A上的锁。这保证了其他事务在T释放A上的锁之前不能再读取和修改A。
2.MyISAM的锁机制和innoDB的锁机制
MyISAM只支持表锁,锁的是整张表,读取数据的时候会加S锁,增删改的时候会加X锁。
innoDB既支持表锁也支持行锁,可以锁行级别的数据,增删改的时候会对数据对象(可能是一行或者多行或者一张表)加X锁,读取的时候一般不加锁,因为innoDB实现MVCC(Multi-Version Concurrency Control 多版本并发控制)。后面我会出一篇博客详细讲解innoDB的锁机制以及它的MVCC。
3.实际测试MyISAM的表锁(MySQL5.5版本)
首先准备一张MyISAM的表。
为了模拟高并发场景我们可以手动加锁(如果不手动加锁,他也会隐式的自动加锁和自动释放锁)
lock table name read; 给name表加S锁
lock table name write; 给name表加X锁
也可以一次性给多张表加锁
lock table name1 read,name2 write;
释放所有获得的锁
unlock tables;
测试一:测试mb表的S锁和X锁
可以看到,两个会话都成功数据对象加了S锁。
但是会话1不能更新数据。除非让第二个会话放弃S锁,然后第一个会话再加X锁,获取锁成功后才能更新数据。如果锁被其他会话占用 而没有获取成功的话会话1会一直等待获取,直到达到设置的超时时间。
测试二:加了X锁真的就立马不能读取数据吗?
如果会话1对数据对象加了X锁,但是这个时候还没有修改数据,而会话2却查询了数据,还是有可能可以获取数据的,但是一但会话1修改了数据那么会话2就不能再获取数据了,必须等会话1释放X锁。这个其实很好理解,MySQL底层是有个查询缓存的,当我们会话1没有修改数据的时候如果查询缓存当中有数据,那么这个数据是有效的,一但会话1修改了数据这个时候查询缓存就无效了,这样设计可以提升一点效率,而又不违背X锁和S锁的设计的原则。
可以看到,我们先把mb表给锁了,然后查询数据,这个时候会话1没有修改数据,但是会话2也没有查询缓存所以仍然查询不了数据。
接下来我们再测试。先让会话2先查询一次数据,然后会话1在加X锁,然后会话2再发送同样的sql语句。
可以看到还是成功的获取了数据。那如果这个时候会话1修改了mb表的数据呢?
可以看到,会话2的查询语句一直在等待,直到会话1释放X锁。
当会话1释放了X锁会话2也就顺利执行了查询语句。
4.MyISAM发生死锁
当会话1获取了A表的X锁,会话2获取了B表的X锁,而会话1又在尝试获取B表的X锁,会话2在尝试获取A表的X锁,这个时候就发生死锁了。
但是一般这种情况不会存在的,因为MyISAM不支持事务,相当于每一条SQL就是一个事务,当某个会话执行完一条语句之后会立马把锁给释放,并不会把锁保留,也就是说不存在同时需要占用两个锁的情况。当然我们可以手动加锁模拟这个场景。
在MyISAM下,当发生死锁时,MySQL会自动把会话持有的锁给释放。
会话1的ma的x锁被释放给了会话2,会话2的mb的x锁释放给了会话1。
当然在实际做项目的时候也不会去这样做,一般不会手动加锁而是让会话执行sql语句的时候自动加锁和自动释放锁。
前面所有的测试在innoDB上有同样的效果,因为innoDB一样支持表锁。
参考文章