MySQL 的锁
锁
排他锁和共享锁(按照锁的属性分)
-
排他锁 ,也叫 写锁 简称
X锁
,即事务 A 读数据对象加上X锁
以后,只能有事务 A 完成读取和修改操作,其他任何事物在此期间,都不能再加任何类型的锁,直到锁释放为停止。 -
共享锁, 又叫 读锁,简称
S锁
,即事务 A 可以对对象添加 S 锁,则其他事务就只能再添加 S锁,而不能添加 X锁。
按照锁的颗粒分
-
表锁:锁住的是整张表,下一个事务要访问,必须等上一个事务释放了锁才行。
-
行锁:锁住的是表的某一行或者某几个行。
-
记录锁:记录锁锁住的是表中的
一行
数据。命中条件是唯一索引。 -
页锁:页锁是介于行锁和表锁之间,他锁定的是一组相邻的记录,锁定的范围是可调的。
-
间隙锁:锁住的是表中的某一个区间。
-
临键锁
乐观锁和悲观锁
悲观锁
顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据
的时候都会上锁,这样别人想拿这个数据就会block直到它拿到锁。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。它指的是对数据被外界(包括本系统当前的其他事务,以及来自外部系统的事务处理)修改持保守态度,因此,在整个数据处理过程中,将数据处于锁定状态。悲观锁的实现,往往依靠数据库提供的锁机制(也只有数据库层提供的锁机制才能真正保证数据访问的排他性,否则,即使在本系统中实现了加锁机制,也无法保证外部系统不会修改数据)。
悲观锁分为
共享锁
和排他锁
乐观锁
顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,所以在数据进行提交更新的时候,才会正式对数据的冲突与否进行检测,如果冲突,则返回给用户异常信息,让用户决定如何去做。以使用版本号等机制。乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库如果提供类似于write_condition机制的其实都是提供的乐观锁。
乐观所和悲观锁的比较
两种锁各有优缺点,不可认为一种好于另一种,像乐观锁适用于写比较少的情况下,即冲突真的很少发生的时候,这样可以省去了锁的开销,加大了系统的整个吞吐量。但如果经常产生冲突,上层应用会不断的进行retry,这样反倒是降低了性能,所以这种情况下用悲观锁就比较合适。
乐观锁比较适合于读多写少的情况,悲观锁比较适合写多读少的情况
悲观锁的实现,往往依靠数据库提供的锁机制
乐观锁不会刻意使用数据库本身的锁机制,而是依据数据本身来保证数据的正确性。
行锁、表锁、间隙锁
行锁
在下面的事务中,因为第一个窗口开启了手动的事务,所以当第一个数据更改了但是没有提交的时候,第二个窗口读到的还是老数据,所以这个就避免了 脏读
这种情况。
此时,我们在第二个窗口,执行更新语句
UPDATE a SET a.age=15 WHERE a.id=1;
此时,第二个窗口会一直阻塞,而不会执行,只有当从第一个窗口执行完 COMMIT
命令的时候,第二个窗口才执行。
表锁
某些情况下,如果使用 OR
作为连接关键字,会使索引失效,而把行锁变成表锁,如在表一中,更改id=1 or id=2
的时候,在第二个窗口里,更新 id=3
的值也不行。
间隙锁
如果索引不连续,比如 id
为 1、3、8 这种,执行如下语句。
UPDATE a SET a.age=15 WHERE a.id>0 AND a.id<5;
因为选择的范围是 0-5 中间,那么如果插入 2
,那么显然不行,这就是间隙锁,他把这个范围内的都锁住了,你往这个范围内插入都不行