一、什么是悲观锁
前文说明了MySQL事务在并发环境下会导致脏读和不可重复读等等的现象,数据库通过事务隔离级别也就是我们常说的锁来解决这个问题。悲观锁的工作原理是假设每次事务都会被其他事务打断,所以在每次操作数据时,都把数据设置成锁定状态,使得其他事务无法再操作。悲观锁能有效的防止数据并发带来的各种未知情况,但是随之而来的效率的低下。
二、使用
使用select ... for update
即开启了悲观锁。
三、案例
创建测试表:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
CREATE TABLE `lock_test` ( `id` int(11) NOT NULL, `name` varchar(20) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- 取消自动提交 SET AUTOCOMMIT = 0; -- 插入两行记录 mysql> insert into lock_test values(1, "maqian"); Query OK, 1 row affected (0.00 sec) mysql> insert into lock_test values(2, "xiaoming"); Query OK, 1 row affected (0.00 sec) -- 提交 mysql> commit; Query OK, 0 rows affected (0.02 sec) mysql> select * from lock_test; +----+----------+ | id | name | +----+----------+ | 1 | maqian | | 2 | xiaoming | +----+----------+ 2 rows in set (0.00 sec) |
使用悲观锁执行事务:
1 2 3 4 5 6 7 |
mysql> select * from lock_test where id = 1 for update; +----+--------+ | id | name | +----+--------+ | 1 | maqian | +----+--------+ 1 row in set (0.00 sec) |
开启另一个终端查询,查询将被阻塞,直至超时:
1 2 |
mysql> select * from lock_test where id = 1 for update; ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction |
四、锁表范围
当查询时明确包含了主键的名字时,锁表范围为查询行,例如上面的例子查询了id=1
的记录行,由于id
是主键,此时就会锁住符合条件的行,另外的事务无法再操作这些数据。
但是还能操作其他行:
1 2 3 4 5 6 7 |
mysql> select * from lock_test where id = 2 for update; +----+----------+ | id | name | +----+----------+ | 2 | xiaoming | +----+----------+ 1 row in set (0.00 sec) |
当查询没有明确指定主键字段值时,锁的范围是整张表。例如我们使用终端一通过name="maqian"
来查询的时候,整个表都会被锁住,其他事务查询任何内容都会失败:
1 2 |
mysql> select * from lock_test where name = "xiaoming" for update; ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction |
评论