MySQL中使用锁来处理并发读写的问题,通过两种类型的锁来实现,分别是共享锁(shared lock)和排它锁(exclusive lock),也叫做读锁(read lock)和写锁(write lock)。
共享锁能够保证一个事务在读取数据时,数据不会其他事务被修改,只能被读取,而且共享锁和排它锁是不能共存的,即加了共享锁就不能再加排它锁。
排它锁能够让一个事务读取和修改数据,但在该事务释放排它锁之前,其它事物既不能读取也不能修改数据。

MySQL有两种重要的锁策略,分别是表锁和行锁。
表锁的开销很小,但是它会锁住整张表,粒度大,因此并发性差。
行锁的开销很大,但是因为它只会锁住特定的行,粒度小,所以能够获取很好的并发性能。

事务隔离级别

SQL标准定义了四种隔离级别,隔离级别越高,并发性越差,系统的开销也越大。

  1. 读未提交(read uncommitted
    事务中的修改即使没有提交,对其他事务也是可见的。事务会读取到未提交的事务的数据,这就是所谓的脏读。
  2. 读已提交(read committed
    事务中的修改在提交之前对其它事务是不可见的。但是这个隔离级别下可能会发生不可重复读,例如事务A过程中,读取一次数据,另一个事务B修改了数据并提交,事务A再次读取该数据,发现两次读取的结果不同。
  3. 可重复读(repeatable read
    保证事务中多次读取同样的数据的结果的一致性,该级别也是MySQL的默认事务隔离级别。但是该级别下可能会发生幻读,即事务A中,读取一次数据,之后事务B插入了一条符合条件的数据并提交,此时事务A再次读取,发现多了一条数据。MySQLInnoDB通过多版本并发控制(MVCC, Multiversion Concurrency Control)解决了幻读的问题。
  4. 可串行化(serializable)
    该隔离级别避免了前面提到的所有问题,它会为每一行数据加锁,从而导致大量的锁竞争和超时的情况发生。
隔离级别 脏读可能性 不可重复读可能性 幻读可能性
读未提交 1 1 1
读已提交 0 1 1
可重复读 0 0 1
可串行化 0 0 0

下面来做一些验证
建立两个MySQLSession,可以看到默认的事务隔离级别为可重复读

建立如下数据库表,并让两个Session使用同一个数据库。

  1. 验证读未提交
    将两个Session的事务隔离级别都设置为读未提交

    Session A中开始事务进行查询

    Session B中开始事务,并修改数据,但未提交

    Session A中再次查询,发现已经读取到Session B中的未提交的事务中的修改,即出现了脏读的情况,如果Session B最终发生回滚,那么Sesseion A第二次的读取到的数据就是错误的。

  2. 验证读已提交
    将两个Session的事务隔离级别都设置为读已提交

    Session A中开始事务进行查询

    Session B中开始事务,修改数据并提交

    Session A中再次查询,发现两次重复读取的结果不同,即出现了不可重复读。

  3. 验证可重复读
    将两个Session的事务隔离级别都设置为可重复读

    Session A中开始事务进行查询

    Session B中开始事务,新增一条数据数据并提交

    Session A中再次查询,发现两次查询的结果相同,由此可见MySQL已经解决了在可重复读的事务隔离级别下会发生幻读的问题(我安装的MySQL版本为5.7.10

    参考资料
    高性能MySQL