据我所知,读和读之间,不存在锁。
1.程序中非数据库交互操作导致事务挂起
将接口调用或者文件操作等这一类非数据库交互操作嵌入在sql事务代码之中,那么整个事务很有可能因此挂起(接口不通等待超时或是上传下载大附件)。
2.事务中包含性能较差的查询sql
事务中存在慢查询,导致同一个事务中的其他dml无法及时释放占用的行锁,引起行锁等待。
3.单个事务中包含大量sql
通常是由于在事务代码中加入for循环导致,虽然单个sql运行很快,但是sql数量一大,事务就会很慢。
4.级联更新sql执行时间较久
这类sql容易让人产生错觉,例如:updateaset...where...in(selectb)这类级联更新,不仅会占用a表上的行锁,也会占用b表上的行锁,当sql执行较久时,很容易引起b表上的行锁等待。
5.磁盘问题导致的事务挂起
极少出现的情形,比如存储突然离线,sql执行会卡在内核调用磁盘的步骤上,一直等待,事务无法提交。
综上可以看出,如果事务长时间未提交,且事务中包含了dml操作,那么就有可能产生行锁等待,引起报错。
myisam不支持事物,所以这些隔离级别是没有意义的。然后再说一下这些隔离级别和锁之间的关系(如在innodb中,支持行级锁):首先一个事物由begin开始,由commit(成功执行)或rollback(执行失败,需要回滚)终止,所以考虑事物问题的时候要考虑到事物的整个生命周期,对同一行数据的加锁解锁操作是有可能多次执行的,因此传统的2phaselocking(2pl,两阶段锁)才会有很多变种,最简单的是只要先统一上锁,后统一释放锁就可以了,不用考虑释放锁的时间,最严格的是所有的锁一旦加上之后,必须要等到事物commit或rollback后才可以释放。举个例子,有一行数据a,有两个事物t1,t21.readuncommittedt1对a进行写操作,写之前加了写锁,写之后但是commit前释放了写锁,这时候t2是可以读a的,由于t1还没有commit,所以t2就发生了readuncommitted的情况。2.readcommitted还是1中的情况,如果t1把a的写锁一直保持到t1commit成功之后再释放,那么t2在t1commit之前都是不可以读a的,就可以避免readuncommitted,这就是readcommitted。但是这种隔离级别下,如果t1插入了一个以前不存在的新行b,t2是可以读的,就造成了幻读的情况。3.repeatableread为了避免幻读,可以加谓词锁延迟新行的添加,比如t2要读大于5的行,那么就加个谓词锁,使得大于5的行不能被添加进去,这种实现基本上也是实现了serializableread基于锁的事物只是一种方法,一般被成为悲观并发控制。此外,还有:乐观并发控制:执行读写的时候不加锁,commit的时候检测是否有冲突,若没有冲突commit成功,否则需要rollback,值得注意的是,虽然读写的时候没有加锁,但是检测的时候是要加锁的,否则2个冲突的事物可能同时检测成功,这一点绝大多数材料里都没有指出。mvcc:给每个数据一个版本号,读的时候完全不需要锁,写的时候看具体实现,可以选择乐观并发控制,也可以选择悲观并发控制,这种实现很容易实现snapshotisolation(也是一种隔离级别,只不过没有以上几个名气大,可以保证每个事物可以看到一个在它发生之前的数据库完整实例)。