1.使用MYISAM引擎存储(只能加表锁)
- 给表加读锁 lock table table_name read;
- 给表加写锁 lock table table_name write;
建表语句:
下面进行读写锁的特性测试:
1.首先是读锁:
在对一个数据库中的表加了读锁时,自身还可以读取该表的记录。但是不能读取其它表的记录。
自己不能更新表中的记录。
那么在另一个新开的会话当中的情况呢?
可以读取任意表的信息,更新操作将会被阻塞。
2.再是写锁
在自身的会话中,写锁可以读,写,不能查看其它的表。
在新开的会话中,不能读,写。
结论:在MYISAM引擎体系下,读锁会限制自己更新,查看其它会话的。并且新开的会话的更新操作会被阻塞,可以读取记录。加写锁是,自身的更新和查询都没问题,新开的会话更新和读取都会阻塞。
2.在INNODB引擎体系下的锁(重点)
INNODB是支持行锁(锁住一条记录),也支持表锁的,下面进行详细的讲解。
实验用到的表结构:
id字段作为主键,category_id和views字段作为联合索引。
1.INNDOB的行锁
INNDOB引擎体系下,当我们使用set autocommit=false; 或者start transaction;开启事务时,update,insert,和delete操作会自动为我们操作的那一行数据加上共享锁(写阻塞,读共享)。注意,行锁只会在我们操作索引字段的时候生效。具体的原因我稍后解释。
会话一开启事务,并且where条件处根据索引字段查找,改变title记录为2,按照加共享锁的思想,这一行已经不能再被更新,除非我们提交事务。下面验证:
会话二同样开启事务,并尝试更新会话一已经上锁的一行记录,可以看到,此时阻塞时间太行,已经更新失败。但是我们查看该行行记录是没有问题的。并且也不会阻塞我们更新其它的行记录。
上面提到,为什么需要用索引来检索我们需要更新的行呢?其实了解索引工作原理的朋友应该都明白,如果我们没有为一个字段建立索引,那么where查找时,就会在原生的表中直接一一匹配,而Mysql是采取 "宁可错杀,不可放过"的思想,把我们检索过的行记录都一一上锁,这个时候,我们的行锁就相当于变成了表锁。一个字段上建立了索引并且生效时,我们只需要去索引表中快速查找指定记录,不会扫描整表。
2.间隙锁
何谓间隙锁?简而言之,当我们使用范围条件检索数据而不是等值条件,并且请求共享锁或者排它锁,对于在检索范围内但是实际上并不存在的数据记录,INNODB引擎体系下也会加锁。
做一个案例充分感受以下:
可以看到表中并不存在id为2的记录,这是我为了测试特意剔除的数据。尝试在另一个会话中插入id为2的数据:
由于阻塞时间过长导致更新失败。这就是间隙锁,注意要防范这种锁的产生。
3.手动加上共享锁和排它锁
上述的锁都是被动产生的,处于事务中时,Mysql的INNODB引擎会自动为更新操作加上共享锁。但是如果我们想自己加呢?
看接下来的演示。
select * from table_name where id=? for update表示为该行记录上排它锁。
可以看到在另一个会话当中更新上锁记录失败。注意,这种情况下不要求检索的字段是添加了索引索引的字段。