共计 4176 个字符,预计需要花费 11 分钟才能阅读完成。
这篇“MySQL 数据库锁如何实现”文章的知识点大部分人都不太理解,所以丸趣 TV 小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“MySQL 数据库锁如何实现”文章吧。
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
手动给表加锁:
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,+ infin;)的数据了。
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 乐观锁
乐观锁,就是对数据的操作持有乐观的态度,任务每次操作数据时,都不会有其他的事务对数据进行修改操作。但是在每次一修改数据的时候,都会判断在此期间数据是否被其他事务修改过。
乐观锁是在操作数据的时候,通过程序来进行控制的,比如:使用版本号,或者时间戳来进行比对。
版本号的乐观锁:在数据表中,添加一个字段 rsquo;version rsquo;,这个字段使用来记录每次数据跟新后的数据版本。在进行数据的更新操作时,会先拿到 rsquo;version rsquo; 字段的值,更新数据的时候,会拿之前的 version 的值和现在数据库里面的 lsquo;version rsquo; 进行比较,如果两个 lsquo;version rsquo; 的值是一样的,说明在此期间数据没有被修改过,可以进行更新操作。反过来,如果两个 lsquo;version rsquo; 的值不一样,说明此期间有其他事务修改过这条记录,则更新失败。
在更新数据的时候,会使 rsquo;version rsquo; 的值加 1。
`UPDATE student SET name= 李四 ,version=version+1 WHERE version=version`
乐观锁的适用场景读多写少的场景,由程序实现,不会出现死锁问题。
以上就是关于“MySQL 数据库锁如何实现”这篇文章的内容,相信大家都有了一定的了解,希望丸趣 TV 小编分享的内容对大家有帮助,若想了解更多相关的知识内容,请关注丸趣 TV 行业资讯频道。