并发处理时我们经常谈到的问题。在数据库操作中,有时候需要考虑并发的问题,比如我们常见的库存管理系统以及银行转账等问题,这些场景都需要用到数据库的锁机制以及事务,不然很容易造成”脏数据”等常见的在并发中遇到的问题。今天浅谈一下mysql锁机制。

表级锁与行级锁

Mysql的锁机制大概分为两种,一种是表级锁,一种是行级锁。顾名思义,表级锁就是在进行读写操作时,把涉及到的目标表锁住,阻塞其他连接对相关表的操作。行级锁就是粒度更细了,精确到了表里的某条纪录。

表级锁 行级锁
开销
冲突
是否可能死锁
并发度

Mysql中的Innodb引擎支持行级锁以及表级锁,MyIsam只支持表级锁。表级锁中,当一个连接给一个加锁后,只能访问或者修改加锁的表,而不能再访问其他表了,除非解锁,这就破坏了死锁中的占有且等待条件,因而不用担心造成死锁的问题。

并发问题

更新丢失(Lost Update):简单来说就是两个连接(事务)同时对一行进行更新,最后导致了晚一点点更新的会覆盖掉之前更新的。解决办法就是在一个连接进行更改操作时,不允许其他连接访问就可以避免更新丢失的问题。

脏读(Dirty Reads):一个事务对一条记录进行修改时(未提交),此时其他事务对这条记录进行读操作,导致读取的是旧数据,非最新的修改数据,因而数据脏读。

不可重复读(Non-Repeatable Reads):一个事务在读取某些数据后的某个时间,再次读取之前读过的数据,但是数据可能已经发生更改,之前读到的数据不一致。

幻读(Phantom Reads):一个事务按相同的查询条件重新读取以前检索过的数据,却发现其他事务插入了满足其查询条件的新数据,这种现象就称为“幻读”。

InnoDB的行锁模式及加锁方法

共享锁(S):允许一个事务去读一行,阻止其他事务获得相同数据集的排他锁。

排他锁(X):允许获得排他锁的事务更新数据,阻止其他事务取得相同数据集的共享读锁和排他写锁。另外,为了允许行锁和表锁共存,实现多粒度锁机制,InnoDB还有两种内部使用的意向锁(Intention Locks),这两种意向锁都是表锁。

意向共享锁(IS):事务打算给数据行加行共享锁,事务在给一个数据行加共享锁前必须先取得该表的IS锁。

意向排他锁(IX):事务打算给数据行加行排他锁,事务在给一个数据行加排他锁前必须先取得该表的IX锁。

InnoDB行锁是通过给索引上的索引项加锁来实现的,这一点MySQL与Oracle不同,后者是通过在数据块中对相应数据行加锁来实现的。InnoDB这种行锁实现特点意味着:只有通过 索引 条件检索数据,InnoDB才使用行级锁,否则,InnoDB将使用表锁.

避免死锁

  1. 如果不同程序会并发存取多个表,尽量约定以相同的顺序访问表,可以大大降低死锁机会。
  2. 在同一个事务中,尽可能做到一次锁定所需要的所有资源,减少死锁产生概率。
  3. 对于非常容易产生死锁的业务部分,可以尝试使用升级锁定颗粒度,通过表级锁定来减少死锁产生的概率。

参考

Mysql中那些锁机制之InnoDB