Exclusive Locks,英译:排他锁,简称 X 锁,又称为写锁或独占锁。排他锁分为表级排他锁和行级排他锁。
如果事务 T1 对数据行对象 A 加上了行级排他锁,那么事务 T1 可以对数据行对象 A 进行读取和更新操作,其他事务则只能对数据行对象 A 进行读取操作,而不能进行更新操作,并且其它事务不能再往数据行对象 A 上加任何类型的锁,直到 T1 释放了行级排他锁。
MySQL 的 InnoDB 引擎默认的修改数据语句 update、delete、insert 都会自动给涉及到的数据加上行级排他锁,而 select 语句默认不会加任何锁,如果查询的时候要加行级排他锁可以使用 select ...for update
语句,加行级共享锁可以使用 select ... lock in share mode
语句。
所以加过行级排他锁的数据行其他事务是不能通过 for update
和 lock in share mode
方式查询数据,但可以直接通过 select ...from...
查询数据,因为普通查询没有加任何锁。
事务 T1 如果对某张表加了表级排他锁,表示事务 T1 可以对该表中的所有记录进行查询和修改,而其它事务只能查询不能修改。并且其它事务不能再往这张表加任何类型的表级锁,也不能给表中的数据行加任何的行级锁。
Shared Lock,英译:共享锁,简称 S 锁,又称读锁。共享锁分为表级共享锁和行级共享锁。
顾名思义,共享锁就是多个事务对于同一数据可以共享一把锁,都能访问到数据,但是只能读不能修改。即共享锁不阻塞其他事务的读操作,但阻塞写操作。
当事务 T1 为数据行对象 A 加上行级共享锁后,事务 T1 可以对数据行对象 A 进行读操作,但不能进行写操作,并且事务 T2 可以再次对数据行对象 A 加行级共享锁,但是不能加行级排他锁,也不能加表级排他锁。大家都可以正常地读取数据行对象 A,在数据行对象 A 上的所有共享锁释放之前,任何事务不可以对数据行对象 A 进行写操作。
数据行对象 A 可以共存多个行级共享锁,这被称为行级共享锁兼容。加了行级共享锁的数据行对象 A 不能再加行级排他锁,所以行级共享锁和行级排他锁是不兼容的。
当事务 T1 为某张表添加了表级共享锁,表示事务 T1 可以查看表中的所有记录,但是不能修改,而且其它事务也只能查看数据不能修改数据。但是其它事务可以再往这张表添加表级共享锁和意向共享锁,其它事务也可以往这张表中的记录添加行级共享锁;但是其它事务不能再往这张表添加表级的排他锁,也不能添加意向排他锁,其它事务也不能往这张表中的记录添加行级排他锁。
Update Lock,英译:更新锁,简称 U 锁。更新锁只有行级的更新锁。
当事务 T1 给数据行对象 A 加上更新锁后,代表数据行对象 A 将在稍后被更新。更新锁允许其他事务在事务 T1 操作更新之前读取数据行对象 A,但不可以修改。其他事务修改数据行对象 A 之前会先往数据行对象 A 加行级排他锁,但是发现数据行对象 A 已存在 U 锁,所以加行级排他锁失败,只能等待 U 锁释放。
更新锁与行级共享锁兼容;更新锁与表级共享锁不兼容;更新锁与更新锁互不兼容;更新锁与行级排他锁不兼容,更新锁与表级排他锁不兼容。
因此数据行对象 A 不可以再添加更新锁,但是可以添加行级共享锁,但是添加行级共享锁的意义不大,因为事务 T1 找到需要更新的数据时,更新锁直接转为行级排他锁,开始更新数据,不需要等待其他事务释放行级共享锁,所以在有更新锁的数据资源上加行级共享锁就毫无意义了。
在数据行对象上加 U、X、S 之前都会先为表或者页加意向锁。意向锁是一种表级锁,表明“某个事务对表中的记录加了某种锁或准备加某种锁。
常用的意向锁有三种:
1.Intent Share Lock
,英译:意向共享锁,简称 IS 锁
事务 T1 在给数据行对象添加行级 S 锁前,要先获得 IS 锁。如果表被加了 IS 锁,说明某个事务对这个表中的某些数据行加了行级 S 锁。当其它事务想要在这个表上加一个表级排他锁时,发现这个表已经加了意向共享锁,那么就不可以加表级的排他锁了。
2.Intent Exclusive Lock
,英译:意向排他锁,简称 IX 锁
事务在请求行级 X 锁前,要先获得 IX 锁
事务 T1 修改 user 表的数据行对象 A,会给数据行对象 A 上一把行级的排他锁,但是在给数据行对象 A 上行级排他锁前会先给 user 表上一把意向排他锁,这时事务 T2 要给 user 表上一个表级的排他锁就会被阻塞。
3.Share Intent Exclusive Lock
,英译:共享意向排他锁,简称 SIX 锁
共享意向排他锁的意思是,某事务要读取整个表,并更新其中的某些数据。
我质疑存在这种锁,在网络上找不到关于共享意向排它锁的任何资料。
锁的粒度指的是锁生效的范围,即行锁、页锁、表锁等。锁的粒度一般由数据库自主管理,不同的事务隔离级别,数据库会有不同的加锁策略(比如加什么类型的锁,加什么粒度的锁)。
其实锁在大多数情况下都是数据库自动加的,数据库会根据隔离级别的不同,按照策略来加锁。
比如这么一条语句:
mysql> update student set class = '高二(3)班';
通过性能分析工具 Profiler 跟踪 sql 发现,数据库系统会逐行先获取 U 锁,然后转为 X 锁,对数据行进行更新,更新完后并不释放 X 锁,接着继续获取下一行的 U 锁,转为 X 锁,更新数据…,不断重复以上流程,直到全部数据行更新完成后,再逐行释放掉所有的 X 锁。
而如果加上 where 条件,如:
mysql> update student set class = '高二(3)班' where name = 'liudehua';
如果字段 name 没有索引,则逐行获取 U 锁,如果符合条件,转为 X 锁,执行更新,不释放 X 锁;如果不符合条件,释放 U 锁。
如果有索引的话,则不需要全表扫描逐行处理,而是直接定位到要更新的数据行,然后逐行加上 X 锁,接着执行更新,更新后不释放 X 锁,直到所有定位到的数据行更新完成后,再逐行释放 X 锁。
手动加行级排他锁,如下所示:
mysql> begin; -- 开始事务 Query OK, 0 rows affected (0.00 sec) mysql> select * from account where id = 5 for update; -- 查询到的数据行全部加上行级的排他锁 +----+----------+---------+ | id | NAME | balance | +----+----------+---------+ | 5 | zhangsan | 1000.00 | +----+----------+---------+ 1 row in set (0.00 sec)
手动加行级共享锁,如下所示:
mysql> begin; Query OK, 0 rows affected (0.00 sec) mysql> select * from account where id = 5 lock in share mode; +----+----------+---------+ | id | NAME | balance | +----+----------+---------+ | 5 | zhangsan | 0.00 | +----+----------+---------+ 1 row in set (0.01 sec)
手动给表加表级的排他锁,如下所示:
mysql> lock tables account write; -- 给表account加排他锁,即写锁 Query OK, 0 rows affected (0.00 sec)
注:
1.会话 S1 对表 account 加上表级排他锁后,其它会话不能对该表 account 进行查询和修改操作。
2.会话 S1 对表 account 加上任何表级锁后,该会话不能对其它非锁表进行任何的操作。
手动给表加表级的共享锁,如下所示:
mysql> lock tables account read; -- 给表account 加共享锁,即读锁 Query OK, 0 rows affected (0.00 sec)
给多个表加表级锁,如下所示:
mysql> lock table user write,account write; Query OK, 0 rows affected (0.00 sec)
手动解锁,如下所示:
mysql> unlock tables;
- | 行级 S | 行级 U | 行级 X | 表级 S | 表级 X | IS | IX |
---|---|---|---|---|---|---|---|
行级 S | 兼容 | 加了 S 锁不能再加 U 锁,但是先加 U 锁,可以再加 S 锁 | 不兼容 | 兼容 | 不兼容 | 兼容 | 兼容 |
行级 U | 加了 S 锁不能再加 U 锁,但是先加 U 锁,可以再加 S 锁 | 不兼容 | 不兼容 | 不兼容 | 不兼容 | 兼容 | 兼容 |
行级 X | 不兼容 | 不兼容 | 不兼容 | 不兼容 | 不兼容 | 兼容 | 兼容 |
表级 S | 兼容 | 不兼容 | 不兼容 | 兼容 | 不兼容 | 兼容 | 不兼容 |
表级 X | 不兼容 | 不兼容 | 不兼容 | 不兼容 | 不兼容 | 不兼容 | 不兼容 |
IS | 兼容 | 不兼容 | 不兼容 | 兼容 | 不兼容 | 兼容 | 兼容 |
IX | 兼容 | 兼容 | 兼容 | 不兼容 | 不兼容 | 兼容 | 兼容 |