锁:用于在多个事务访问同一个对象时根据这些操作访问同一对象的先后次序给事务排序。
不同数据库的锁实现:
InnoDB:行级锁;
Oracle:行级锁;
MyISAM:表级锁;
Microsoft SQL Service:行级锁、锁升级
InnoDB存储引擎中的锁:
表级锁:
IS(意向共享锁):事务给行加共享锁时会先取得该表的IS锁;
IX(意向排他锁):事务给行加排他锁时必须取得该表的IX锁;
行级锁:
S(行级共享锁):允许一个事务去读一行,阻止其他事务获得相同数据的排他锁;
X(行级排他锁):允许获取排他锁的事务更新数据,阻止其他事务获取相同数据的共享锁和排他锁;
比如如下这条语句:
update t1 set name = 'aaaaa' where id = 1;
这里先回给表t1添加IX锁,然后再给id=1的行添加X锁。
下面给出兼容性表格:
这里为什么要用2个锁:因为在高并发下,如果有事务去删这个表,就会去查有没有这2个锁,先找表锁,再找行锁。这样的话效率就高得多。
下面是InnoDB行锁的介绍:
Record Lock:行记录锁;
Gap Lock:间隙锁,在索引记录间隙上的锁,在第一条索引记录之前,最后一条索引记录之后上的间隙锁;
Next-key lock:下键锁,上面2个锁的组合锁;
比如下面这个表:
create table t2(id int,
name varchar(10),
primary key(id),
key(name)
);
insert into t2 values(1, 'A'), (3, 'A'), (5, 'C'), (7, 'G'), (10, 'I');
select id, name from t2;
这里有一点要注意:只有可重复读的事务等级才会有Gap lock锁。对应普通索引和普通列。基本上只针对普通索引。在空隙间加锁,解决幻读。因为不能插入数据。
在事务中常常用到的2组关键字:
for update:可以为数据库中的行上一个排它锁。当一个事务的操作未完成时候,其他事务可以读取但是不能写入或更新。
lock in share mode:共享锁,允许其他线程读,但不能进行修改。
下面做一个关于Record Lock的例子:
当session 1 进行:
begin;
select * from t2 where name = 'C' for update;
session 2会被阻塞吗?
begin;
select * from t2 where id = 5 lock in share mode;
for update为id为5的行加了排他锁,导致在session2查的时候,阻塞。
但是mysql有套超时解锁机制:
当超时会把自动释放事务。
下面是第二个关于Record Lock的测试:
先来看下t2的所有数据:
select * from t2;
session1:
begin;
select * from t2 where id = 5 and name = 'C' for update;
session2:
begin;
select * from t2 where id = 5 and name = 'B' for update;
从中可以看到,也是被阻塞的。
这里可以通过这条命令查询下哪个事务用了什么锁:
在mysql中InnoDB中的锁会被记录到innodb_locks这个表中:
select * from information_schema.innodb_locks;
普通索引上的锁,都要追溯到主键上。mysql是聚蔟索引表。最终会关联到主键上。
下面是关于Gap Lock的测试
理论我们都知道了,加了Gap Lock,解决幻读,是不能插入其他数据的,在此还是演示下:
session1:
begin;
select * from t2 where name = 'C' for update
session2:
begin;
insert into t2 values(4, 'C');
查看其阻塞的情况。
这里要知道的一点:Gap lock在InnoDB中存在的条件是事务等级为可重复读,先查下事务等级:
show variables like '%iso%'
演示如下:
从中可以看到session2被阻塞了,看下锁:
也就是为什么MySQL中可重复读,可以解决幻读问题,但是这种默认设计在理论上来说,并发量没有oracle高。但好处也是很多的。