无论数据有没有上锁都可以使用select ...from...查询数据,因为普通查询是快照读没有任何锁机制。
事物隔离级别(mysql默认可重复读):
脏读:读到其他事物未提交的数据,可能其他事物事物失败回滚。
不可重复读:同一个事务中第一次读取和第二次读取同一行数据,期间数据被修改了,两次读取到的数据内容不同。
幻读:同一个事务中两次查找的出的结果数量不同,两次查找期间插入(删除)了新的符合条件的数据。RR级别时MVCC的事物版本号解决了读数据的幻读(快照读)。但是在修改数据时,仍然会更新比当前事物版本号高的事物插入并提交的数据(当前读)。
共享锁:
读锁又称为共享锁,简称S锁,顾名思义,共享锁就是多个事务对于同一数据可以共享一把锁,都能访问到数据,但是只能读不能修改。
加共享锁可以使用select ... lock in share mode语句。
排他锁:
写锁又称为排他锁,简称X锁,顾名思义,排他锁就是不能与其他所并存,如一个事务获取了一个数据行的排他锁,其他事务就不能再获取该行的其他锁,包括共享锁和排他锁,但是获取排他锁的事务是可以对数据就行读取和修改。
加排他锁可以使用select ...for update 语句。
行锁:
行锁是排他锁的一种,它只锁住一行数据,是通过给主键加锁实现的。在用for update给查询结果加锁时,如果查询使用了二级索引,则在二级索引上找到主键索引,并把对应记录加锁。加锁的数据可能多于查询出来的数据,因为可能有些条件没有使用索引。如果查询没有使用索引,则使用表锁。
innodb有行锁,myisam则没有。
间隙锁:
间隙锁本质上是用于阻止其他事务在该间隙内(两边都是开区间)插入新记录,是不区分共享间隙锁或互斥间隙锁的,而且间隙锁是不互斥的,即两个事务可以同时持有包含共同间隙的间隙锁。这里的共同间隙包括两种场景:其一是两个间隙锁的间隙区间完全一样;其二是一个间隙锁包含的间隙区间是另一个间隙锁包含间隙区间的子集。也就是说间隙锁的应用场景包括并发读取、并发更新、并发删除和并发插入。
在RU和RC两种隔离级别下,即使你使用select ... in share mode或select ... for update,也无法防止幻读(读后写的场景)。因为这两种隔离级别下只会有行锁,而不会有间隙锁。
临键锁:
每个数据行上的非唯一索引列上都会存在一把临键锁,当某个事务持有该数据行的临键锁时,会锁住该行数据上下两个区间。如下图,当使用for update获取cnt=5的临间锁(cnt字段必须要有索引)时。无法插入1<=cnt<10的数据。cnt=10的数据可以插入,cnt=1则不行。我的innodb和MySQL版本都是5.6.42。临间锁=行锁+上下两个区间的间隙锁。
插入意向锁:
插入意向锁,是间隙锁的一种。RR级别是有间隙锁的。即使在同一个索引,同一个区间,但插入的记录并不冲突 ,事物之间不会相互堵塞。插入意向锁和间隙锁不兼容。
意向共享/排他锁:
意向锁致力于解决表锁和行锁冲突的问题。意向锁本身时表锁。意向共享锁和排他锁之间本身不会互相冲突。
如果没有意向锁,事物A获得了表中一条记录的写锁,此时事物B想要锁住整张表,必须要遍历所有数据确保所有数据都没有被加锁,而遍历一半发现事物A锁住了一条记录,于是事物B加表锁失败。这样效率低下。
有了意向锁,事物A对表先申请意向共享/排他锁,然后对相应记录添加记录锁。此时事物B想锁住整张表,发现表上已经有意向共享/排他锁,说明表中某些数据正在被加读/写锁。此时直接加锁失败。当然,如果事物B只想修改几条数据的话,可以申请意向排他锁(即使表上已经有意向排他锁也不妨碍其他事物申请意向排他锁),然后看自己要修改的记录是否有锁,没有锁则直接加锁修改,有锁则挂起等待。
自增锁:
自增锁是一种特殊的表级锁,主要用于事务中插入自增字段(AUTO_INCREMENT),也就是我们最常用的自增主键id。