1.悲观锁介绍
和他的名字一样,对数据被外界修改(包括本系统的其他事务,以及来自外部系统的事务)持保守态度,也就是说,认为外界很有可能对这个数据同时修改。
所以,需要在数据处理工程中,将数据处于锁定状态,也就是只能单线程处理。
2.悲观锁实现
因为要实现单线程访问,所以就只能通过数据库提供的锁来实现了。因为即使在本系统中实现了加锁机制,也无法保证外部系统不会修改数据
获取数据的时候使用:
(1)关闭自动提交(2)select .. for update 即可实现悲观锁
在一个线程使用select forupdate获取数据,如果这个线程的事务还没有提交,那么其他线程使用select for update就会卡在那里,直到前面的线程提交事务
注意:
(1)要使用悲观锁,必须关闭mysql数据库的自动提交,可以使用:set autocommit=0命令
(2)只有我们在事务中执行select for update同一条记录时会等待其他事务结束后才执行,一般的select不会受影响
补充:
使用select for update会把数据给锁住,但是锁也是有级别的。Mysql 的InnoDB默认是row-level lock,所以只有明确指定主键,mysql才会执行row lock,否则的话,将会执行table lock将整个表都锁住
3.悲观锁使用
在这里我们使用两个mysql控制台console1和console2
(1)明确指定主键,并且有这条数据(row lock)
console1:查询出结果,但是把这条数据锁定了mysql> select * from t_goods where id=1 for update;
+----+--------+------+
| id | status | name |
+----+--------+------+
| 1 | 1 | 道具 |
+----+--------+------+
1 row in set
mysql>
console2:查询被阻塞
mysql> select * from t_goods where id=1 for update;
console2:如果console1长时间未提交,则会报错
mysql> select * from t_goods where id=1 for update;
ERROR 1205 : Lock wait timeout exceeded; try restarting transaction
(2)无主键,table lock
console1:查询正常mysql> select * from t_goods where name='道具' for update;
+----+--------+------+
| id | status | name |
+----+--------+------+
| 1 | 1 | 道具 |
+----+--------+------+
1 row in set
mysql>
console2:同样条件查询
mysql> select * from t_goods where name='装备' for update;
console2:如果console1长时间未提交,则查询返回为空
mysql> select * from t_goods where name='装备' for update;
Query OK, -1 rows affected