«

MySQL数据库锁如何实现

时间:2024-7-30 09:27     作者:韩俊     分类: Mysql


这篇“MySQL数据库锁如何实现”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“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锁。写记录:INSERTDELETEUPDATE 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 乐观锁

乐观锁,就是对数据的操作持有乐观的态度,任务每次操作数据时,都不会有其他的事务对数据进行修改操作。但是在每次一修改数据的时候,都会判断在此期间数据是否被其他事务修改过。

乐观锁是在操作数据的时候,通过程序来进行控制的,比如:使用版本号,或者时间戳来进行比对。

    版本号的乐观锁: 在数据表中,添加一个字段&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

热门推荐