数据库事务-锁机制
1.什么是锁
锁,其实就是一个内存种的结构,在事务还没有来之前是没有锁存在的。在事务未开始前只有一条记录,是没有锁和记录之间的关联关系的。
锁结构种有很多的信息,主要的有两个:
- trx信息:代表这个锁结构是哪个事务生成的。
- is_waiting:代表当前事务是否在等待。
当一条事务想要对某条记录进行改动时,就会生成一把锁,在生成锁的时候,会去检查该条记录有没有被其他的锁关联。① 如果没有,is_wating就是false,不需要等待,此时表示事务上锁成功,可以进行后续操作;② 如果有其他事务上锁,则is_wating就是true,加锁失败,需要等待其他事务释放锁,才能后续操作该记录。
2.锁解决的问题
数据库锁主要解决并发情况下,数据隔离问题。数据库是可以有多个客户端连接并访问的,这种情况就会有并发操作同一数据记录的情况。因此数据库出现了锁机制,解决各种并发情况下出现的隔离问题。
3.并发访问相同记录的几种情况
- 读-读:多个事务同事对数据库的同一条记录进行读操作,这种情况对数据库的记录没有发生变化,不会出现什么问题,这种情况不需要解决什么问题。
- 写-写:多个事务同事对数据库的同一条记录进行写操作,这种情况会发生脏写问题,不管那种隔离级别,都不允许这种情况的发生。所有有多个未提交的事务去操作同一条记录时,需要让其他事务处于阻塞排队等待。
- 读-写/写-读:在多并发操作同个数据记录时,有的事务在读,有的事务在进行写操作,这种情况会出现脏读、不可重复读、幻读等情况。MySQL在repeatable read隔离级别上,已经解决了幻读的问题。
4.理解读锁和写锁
对于MySQL的innoDB存储引擎来说,读锁/写锁可以作用在表上,也可以作用在行上
4.1 读锁
读锁(S)也称共享锁
,在多个事务共同读同一个记录时,是可以同时读取,互不影响,互不干扰的。
在进行SELECT查询操作的时候,可以使用读锁,使多个任务之间可以共同读取同一条记录。但在查询操作时,也可以使用写锁
,后面讲写锁的时候再说。
如何在读取的时候加锁:
SELECT * FROM student LOCK IN SHARE MODE;
#或者
SELECT * FROM student FOR SHARE;#(mysql 8.0新写法)
4.2 写锁
写锁(X)也称排他锁,在事务在进行写操作时,会上X锁,导致当前事务未完成写操作时,其他事务的读/写会被阻塞。保证在同一个时间内,只有一个事务能对事务进行读/写操作。
- 在进行读操作的时候,也可以给记录加X锁,防止在读取记录的时候,其他事务对当前记录进行更新。
- 查询语句加上X锁有,其他事务不能再给该记录加S锁或X锁。其他事务会阻塞,知道当前事务释放X锁。
- 给查询操作加X锁:
SELECT * FROM student FOR UPDATE;
进行写操作时,会自动给该条记录加X锁。写记录:INSERT\DELETE\UPDATE 4.3 读锁和写锁的兼容情况
5.表锁
表锁是指给所操作的整张表进行加锁,相对行锁,对表加锁的颗粒度比较大,因此它的开销也比较小。由于是对整张数据表进行加锁,因此可以避免死锁的出现。即使这样,是对整张表进行加锁,就会导致大量的事务无法继续操作表,所有表锁的性能是较差的。
在MySQL中,InnoDB提供了表锁
和行锁
由于表锁的性能比较差,一般我们都很少用到表锁。只有特殊场景下会用到表锁,比如:数据崩溃恢复。
5.1 表级的读/写锁
- 对表加S锁:MySQL的InnoDB对整个表加S锁。
- 对表加X锁:MySQL的InnoDB对整个表加X锁。
查看有那些表被加锁:
SHOW OPEN TABLES;
#或者
SHOW OPEN TABLES WHERE In_use >0;
手动给表加锁:
LOCK TABLES student READ;#给student表加S锁
LOCK TABLES stdent WRITE;#给student表加X锁
释放锁:
UNLOCK TABLES;
小结
5.2 意向锁
MySQL的InnoDB存储引擎中,支持表锁和行锁同时存在,而意向锁就是表锁的一种。
意向锁是为了协调表锁
和行锁
同时共存而存在的。意向锁是一中不于行锁
冲突的表级锁。意向锁用户是无法手动添加的,它是InnoDB存储引擎自动给加的。当在给某个行添加S锁或X锁
时,需要先获取当前行所在表的意向锁。
意向锁分为两种:
- 意向共享锁(IS):事务有意向对表中的某行加共享锁(S锁)。当给某一行记录加了
共享锁(S)
后,数据库就会给当前的数据表或数据页加上意向共享锁
,当想给当前表加入一个排他锁(X)
时,就会检测到意向共享锁,就会被排斥阻塞,不能加锁。但是加表的共享锁
时,是可以的。 - 意向排他锁(IX):事务有意向对表中的某行加排他锁(X锁)。当给某一行记录加了
排他锁(X)
后,数据库就会给当前的数据表或数据页加上意向排他锁
,当想给当前表加入一个排他锁(X)
或者意向锁(S)
时,就会检测到意向排他锁,就会被排斥阻塞,不能加锁。
理解意向锁的意义:当没有意向锁的时候,当事务想要给表加表锁的时候,需要去检测每行记录是否加有锁,这样每条检测的效率非常的低。意向锁的出现,很好的解决这种情况。有意向锁后,事务要表添加锁,只要检查当前表是否有意向锁就可以了。
总结:意向锁之间是互相兼容的和读写锁不兼容:
6.行锁
行锁就是给表中的某条记录上把锁,将该条记录锁住。行锁是在InnoDB存储引擎层实现的,这是InnoDB与MyISAM最大的区别之一。行锁是粒度小的,发生锁冲突的概率很小,因此会实现高并发的效果,但是因粒度较小,加锁较慢,会出现死锁的情况
6.1 记录锁
记录锁就是一把行锁,顾名思义,给某条记录上锁。
- 共享记录锁(S):当一个事务得到了某个事务的共享锁后,其他事务还能继续获取该记录的共享锁,但是不能获取该记录的排他锁。
- 排他记录锁(X):当一个事务得到了某个事务的排他锁后,其他事务不能获取该记录的共享锁和排他锁。
6.2 间隙锁
间隙锁是在某个记录前的间隙加入一个锁,这样就使该记录前面的间隙是不能添加数据的。这种间隙锁有效的防止了幻读
的出现。间隙锁的出现,也是为了解决幻读
而提出来的。
不管是共享锁还是排他锁,起到的作用是一样的,
举例:
select * from student where id=11 for update;
此时id为18加了间隙锁
这种会出现一个问题,因为间隙锁只会锁住行前面的间隙,那么,如果此时给id为25后面的间隙插入数据,就会有问题,此时,数据库做了两个提供了两个伪记录: Infimum
记录,表示该页面中最小的记录。 Supremun
记录,表示该页面中最大的记录。
加入如下,就可以阻止其他事务加入(25,+∞)的数据了。
select * from student where id > 20 lock in share mode;
间隙锁的出现,会发生死锁,如下:
当记录中存在间隙锁时,有其他事务想在这个间隙插入数据,由于锁的存在,会阻止插入,让事务进行等待,知道释放间隙锁。此时内存中会生成一个插入意向锁
,这个意向锁是一种间隙锁,并不是表锁中的意向锁。这种插入意向锁可以有多个,一个间隙中有多个插入意向锁并不冲突,插入意向锁之间不会有排斥。
6.3 临界锁
临界锁是用来补充上面说的间隙锁,因为间隙锁只是锁住了记录前面的间隙,但是并不包含自己,因此临界锁就出现了,临界锁就是记录锁和间隙锁的组合体。
select * from student where id <=18 and id > 10 for update;
7.悲观锁和乐观锁
悲观锁、乐观锁其实并不是一种锁,而是一中并发下锁的一种设计思想
7.1 悲观锁
- 悲观锁,就是很悲观,对数据被其他事务操作的时候,保持悲观态度。总是认为数据会被其他事务修改,所以每次操作数据的时候,都会被数据上锁,使别他事务操作数据的时候处于阻塞状态,知道释放锁,保证数据具有排他性。
- 行锁、表锁、读锁、写锁就是悲观锁的体现,每次在操作数据的时候,都会加锁,使其他数据在访问的时候被阻塞挂起,直到释放锁。
- 其实悲观锁的使用场景并不是很多,因为它在每次操作的时候,都会给数据上锁,这样会在事务比较长的时候,性能会比较差。
- 悲观锁的适用场景是写操作多的情况下,因为写具有排他性,在写操作的时候,阻止其他事务对数据的读或写的操作,这样很大的避免了读-写/写-读的冲突,进而避免了脏读、不可重复读、幻读的问题。
7.2 乐观锁
乐观锁,就是对数据的操作持有乐观的态度,任务每次操作数据时,都不会有其他的事务对数据进行修改操作。但是在每次一修改数据的时候,都会判断在此期间数据是否被其他事务修改过。
乐观锁是在操作数据的时候,通过程序来进行控制的,比如:使用版本号,或者时间戳来进行比对。
- 版本号的乐观锁: 在数据表中,添加一个字段’version’,这个字段使用来记录每次数据跟新后的数据版本。在进行数据的更新操作时,会先拿到’version’字段的值,更新数据的时候,会拿之前的version的值和现在数据库里面的‘version’进行比较,如果两个‘version’的值是一样的,说明在此期间数据没有被修改过,可以进行更新操作。反过来,如果两个‘version’的值不一样,说明此期间有其他事务修改过这条记录,则更新失败。
- 在更新数据的时候,会使’version’的值加1。
`UPDATE student SET name= '李四' ,version=version+1 WHERE version=version`
乐观锁的适用场景读多写少
的场景,由程序实现,不会出现死锁问题。
7.3 总结
到此这篇关于MySQL 数据库锁的实现的文章就介绍到这了,更多相关MySQL 数据库锁内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!