谈谈MySQL中的锁
锁的定义
在生活中锁的例子就非常多了,所以应该很容易理解锁的含义。在计算机领域,可以这样来概述,锁是计算机协调多个进行进程并发访问某一资源的机制。
在数据库中,锁也是一个非常重要的特性,DB的锁是为了支持对数据的并发访问,保证数据的一致性以及处理统一数据时不破坏事务的隔离性和一致性。
锁的机制
从锁的机制来看,大致可分为乐观锁和悲观锁两类。不管是乐观锁还是悲观锁,他们是一种思想,而不是具体的锁,而且不仅仅在数据库系统中有这种概念。
乐观锁
总是假设最好的情况,每次去拿数据的时候都认为别人不会去修改,所以不上锁。但是在更新数据的时候会判断在这个期间这个数据有没有被更新。
在mysql实际使用过程中,有一种比较常用的实现方式:基于数据版本(vserion)记录机制。每当数据更新一次,version自增加1。读取数据记录时,将version值读出,当需要提交更新时,将判断当前version值与读出的值大小比较,如果一致,则进行更新,反之则说明过期,拒绝更新数据。
悲观锁
总是假设最坏的情况,每次拿数据的时候都认为别人会修改,所以每次拿数据的时候就会上锁,这样其他人想拿这个数据时就会阻塞,直到原来上锁的进程释放锁。目前大多数数据库广泛使用的锁机制就是悲观锁思想,比如行锁/表锁/页锁等。下面会进行一些锁的详细讲解。
悲观锁与乐观锁各有优点,使用到的场景不同,乐观锁实用于多读场景,而悲观锁适用于多写场景。
锁的粒度
相对其他数据库来说,Mysql的锁比较简单,但有一个特点,不同的存储引擎支持不同锁的粒度。
表锁:颗粒度最大,锁冲突的概率变大,并发度最低;开销小,加锁快,不会死锁。
页锁:mysql特有的,粒度在表锁与行锁之间,所以获取锁资源的时间上,并发度上也是介于表锁和行锁之间,同时,页锁也会出现死锁的情况。
行锁:颗粒度最小,锁冲突概率小,并发最高;开销大,加锁慢,容易死锁。
表锁适合以查询为主的业务,行锁更适合于有并发更新少量数据且有并发查询的业务。
不同引擎支持锁的粒度
引擎 | 表锁 | 页锁 | 行锁 |
---|---|---|---|
InnoDB | ✔ | × | ✔ |
MyISAM | ✔ | × | × |
BerkeleyDB | × | ✔ | × |
锁的类型
InnoDB存储引擎还存在着不同类型的锁,其中就包含下面两种标准的行级锁。
共享锁 S:Shared Lock 读锁 允许事务去读一行数据,阻止其他事务获得相同数据集的排他锁。
若事务T对数据对象A加上S锁,则事务T可以读A但不能修改A,其他事务只能再对A加S锁,而不能加X锁,直到T释放A上的S锁。这保证了其他事务可以读A,但在T释放A上的S锁之前不能对A做任何修改。
排他锁 X:Exclusive Lock,写锁 允许获取排他锁的事务更新数据,阻止其他事务取得相同的数据集共享读锁和排他写锁 。
若事务T对数据对象A加上X锁,事务T可以读A也可以修改A,其他事务不能再对A加任何锁,直到T释放A上的锁。加过排他锁的数据行在其他事务种是不能修改数据的,也不能通过for update和lock in share mode锁的方式查询数据,但可以直接通过select …from…查询数据,因为普通查询没有任何锁机制。
共享锁 S 与 排他锁 X 兼容性
锁类型 | S锁 | X锁 |
---|---|---|
S锁 | ✔ | X |
X锁 | X | X |
另外,为了实现多粒度锁机制,同时允许多粒度锁机制,InnoDB还有两种意向锁,均为表锁。
意向共享锁 IS: 事务打算给数据行共享锁,事务在给一个数据行加共享锁前必须先取得该表的IS锁。 (表达一个事务想要获取一张表中某几行的共享锁) 。
意向排他锁 IX: 事务打算给数据行加排他锁,事务在给一个数据行加排他锁前必须先取得该表的IX锁。( 表达一个事务想要获取一张表中某几行的排他锁)。
意向共享锁 S 与 意向 排他锁 X 兼容性
锁类型 | IS | IX | S | X |
---|---|---|---|---|
IS | ✔ | ✔ | ✔ | X |
IX | ✔ | ✔ | X | X |
S | ✔ | X | ✔ | X |
X | X | X | X | X |