数据库锁

mysql数据库中存在多种锁。

按照锁模式可以分为乐观锁,悲观锁。
按照范围可以分为行锁和表锁。
按照算法可以分为临间锁、间隙锁、记录锁。
按照属性可以分为共享锁、排它锁。共享锁又称为读锁。排它锁又称为写锁。写锁的优先级比读锁高。
按照状态可以分为意向共享锁和意向排它锁。

行锁和表锁

行锁就是锁住一行或者多行记录。mysql的行锁是基于索引加的。所以行锁是要加在索引响应的行上,即命中索引。行锁锁定的数据量少,并发程度高,但是加锁的开销大。
表锁会锁住整张表,在表锁定期间,仅有一个事务能对该表进行操作。表锁响应的是非索引字段,即全表扫描。

读锁和写锁

select for update和select lock in share mode

在执行select for update时会对

如果只有一个事务对一个表执行select lock in share mode,此后该事务还可以对该表进行update操作,但是如果有多于一个事务对该表进行了select lock in share mode,则任何事务都无法对该表执行update操作。

select for update和select lock in share mode同样遵循行锁和表锁的规则。

建立一张成绩表:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
mysql> create table scores(id int not null auto_increment, name varchar(20) not null,      not null default 0, primary key(id))engine InnoDB charset=utf8;
Query OK, 0 rows affected (0.08 sec)

mysql> insert into scores(name, chinese, math, english) values("Bob", 75, 90, 75);
Query OK, 1 row affected (0.01 sec)

mysql> select * from scores; +----+----------+---------+------+---------+
| id | name | chinese | math | english |
+----+----------+---------+------+---------+
| 1 | zhangsan | 78 | 89 | 94 |
| 2 | lisi | 98 | 89 | 91 |
| 3 | Alice | 66 | 82 | 100 |
| 4 | Bob | 75 | 90 | 75 |
+----+----------+---------+------+---------+
4 rows in set (0.00 sec)

表级写锁

在第一个事务中执行:

1
2
3
4
5
6
7
mysql> select * from scores where english = 94 for update;
+----+----------+---------+------+---------+
| id | name | chinese | math | english |
+----+----------+---------+------+---------+
| 1 | zhangsan | 78 | 89 | 94 |
+----+----------+---------+------+---------+
1 row in set (0.00 sec)

在另一个事务中执行:

1
2
mysql> select * from scores where english = 91 for update;
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

这时候第二个事务会被阻塞,因为,第一个事务中的select for update加的表级的写锁,会禁止其他事务对整个表的写操作。所以,这里的事务无法执行,直到超时退出。

行级写锁

如果在第一个事务中执行:

1
2
3
4
5
6
7
mysql> select * from scores where id = 1 for update;
+----+----------+---------+------+---------+
| id | name | chinese | math | english |
+----+----------+---------+------+---------+
| 1 | zhangsan | 78 | 89 | 94 |
+----+----------+---------+------+---------+
1 row in set (0.00 sec)

然后在第二个事务中执行:

1
2
3
4
5
6
7
mysql> select * from scores where id = 2 for update;
+----+------+---------+------+---------+
| id | name | chinese | math | english |
+----+------+---------+------+---------+
| 2 | lisi | 98 | 89 | 91 |
+----+------+---------+------+---------+
1 row in set (0.00 sec)

此时,select for update加的是行锁,所以不会阻塞对另一行执行另一个select for update加另一把写锁。

参考文献

[1]深入理解数据库行锁与表锁:https://zhuanlan.zhihu.com/p/52678870
[2]select for update/lock in share mode:https://www.cnblogs.com/liushuiwuqing/p/3966898.html
[3]基本建表语句:https://www.runoob.com/mysql/mysql-create-tables.html
[4]数据库系统原理简单总结:https://github.com/CyC2018/CS-Notes/blob/master/docs/notes/%E6%95%B0%E6%8D%AE%E5%BA%93%E7%B3%BB%E7%BB%9F%E5%8E%9F%E7%90%86.md
[5]数据库加锁过程详解:https://www.cnblogs.com/crazylqy/p/7611069.html