mysql 锁

 

https://blog.csdn.net/weixin_50838394/article/details/126130616

 按照锁粒度从大到小分类:表锁,页锁和行锁;以及特殊场景下使用的全局锁

 如果按锁级别分类有:共享(读)锁、排他(写)锁,意向共享(读)锁,意向排他(写)锁;还有就是我们面向编程的两种锁思想,乐观锁和悲观锁。

 表锁:MySQL 中锁定粒度最大的一种锁,是针对非索引字段加的锁,对当前操作的整张表加锁,实现简单,资源消耗也比较少,加锁快,不会出现死锁。其锁定粒度最大,触发锁冲突的概率最高,并发度最低,MyISAM 和 InnoDB 引擎都支持表级锁。 

    表级锁:开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低。

 

 

行锁:MySQL 中锁定粒度最小的一种锁,是针对索引字段加的锁,只针对当前操作的记录进行加锁。 行级锁能大大减少数据库操作的冲突。其加锁粒度最小,并发度高,但加锁的开销也最大,加锁慢,会出现死锁。

   虽然能够在并发处理能力上面有较大的优势,但是行级锁定也因此带来了不少弊端。由于锁定资源的颗粒度很小,所以每次获取锁和释放锁需要做的事情也更多,带来的消耗自然也就更大了。此外,行级锁定也最容易发生死锁

   使用行级锁定的主要是InnoDB存储引擎

   行级锁:开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。

 

行级锁的使用有什么注意事项?

  InnoDB 的行锁是针对索引字段加的锁,表级锁是针对非索引字段加的锁。当我们执行 UPDATE、DELETE语句时,如果 WHERE条件中字段没有命中索引或者索引失效的话,就会导致扫描全表对表中的所有记录进行加锁。这个在我们日常工作开发中经常会遇到,一定要多多注意!!!

 

 

 

 

页锁:相对偏中性的页级锁,页锁是MySQL中比较独特的一种锁定级别,在其他数据库管理软件中也并不是太常见。页级锁定的特点是锁定颗粒度介于行级锁定与表级锁之             间,所以获取锁定所需要的资源开销,以及所能提供的并发处理能力也同样是介于上面二者之间。另外,页级锁定和行级锁定一样,会发生死锁。

全局锁:是对整个数据库实例加锁。使用场景一般在全库逻辑备份时

 

悲观锁:就是比较悲观的锁,总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁,Java中synchronized是悲观锁思想的实现

 

乐观锁:总是假设最好的情况,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号机制和CAS算法实现,适用于多读的应用类型,这样可以提高吞吐量。

 

锁的颗粒度:

  在给定的资源上,锁定的数据量越少,则系统的并发程度越高,如果系统花费大量的时间来管理锁,而不是存取数据,那么系统的性能可能会因此受到影响。

 

 

无论是表锁还是行锁,都存在共享锁(share Lock)和排他锁(Exclusive Lock)

读锁:事务在读取记录的时候获取共享锁,允许多个事务同时获取。对当前行加读锁,不会阻塞其他事务对同一行的读请求,但会阻塞对同一行的写请求。只有当读锁释放后,才会执行其它事物的写操作。

写锁:事务在修改记录的时候获取写锁,不允许多个事务同时获取。如果一个记录已经被加了排他锁,那么其他事物不能再对这条食物加任何类型的锁。

    对于写锁而言,会阻塞其他事务对同一行的读和写操作,只有当写锁释放后,才会执行其它事务的读写操作。

 

 

 

 

 

 读锁会阻塞写(X),但是不会堵塞读(S)。而写锁则会把读(S)和写(X)都堵塞

 对于InnoDB 在RR(MySQL默认隔离级别) 而言,对于 update、delete 和 insert 语句, 会自动给涉及数据集加排它锁(X);

 对于普通 select 语句,innodb 不会加任何锁。如果想在select操作的时候加上 S锁 或者 X锁,需要我们手动加锁。

 

-- 加共享锁(S)
select * from table_name where ... lock in share mode
-- 加排它锁(X)
select * from table_name where ...
for update


用 select… in share mode 获得共享锁,主要用在需要数据依存关系时来确认某行记录是否存在,并确保没有人对这个记录进行 update 或者 delete 操作。

但是如果当前事务也需要对该记录进行更新操作,则有可能造成死锁,对于锁定行记录后需要进行更新操作的应用,应该使用 select… for update 方式获得排他锁。

 

记录锁(Record Locks)

       记录锁其实很好理解,对表中的记录加锁,叫做记录锁,简称行锁。比如

   SELECT * FROM `test` WHERE `id`=1 FOR UPDATE;

       它会在 id=1 的记录上加上记录锁,以阻止其他事务插入,更新,删除 id=1 这一行。

 需要注意的是:
  • id 列必须为唯一索引列或主键列,否则上述语句加的锁就会变成临键锁(有关临键锁下面会讲)。

  • 同时查询语句必须为精准匹配(=),不能为 >、<、like等,否则也会退化成临键锁。

其他实现在通过 主键索引 与 唯一索引 对数据行进行 UPDATE 操作时,也会对该行数据加记录锁:

    -- id 列为主键列或唯一索引列
    UPDATE SET age = 50 WHERE id = 1;

记录锁是锁住记录,锁住索引记录,而不是真正的数据记录.
如果要锁的列没有索引,进行全表记录加锁

记录锁也是排它(X)锁,所以会阻塞其他事务对其插入、更新、删除。

 

间隙锁(Gap Locks)

  间隙锁 是 Innodb 在 RR(可重复读) 隔离级别 下为了解决幻读问题时引入的锁机制。间隙锁是innodb中行锁的一种。

  请务必牢记:使用间隙锁锁住的是一个区间,而不仅仅是这个区间中的每一条数据。

  举例来说,假如emp表中只有101条记录,其empid的值分别是1,2,…,100,101,下面的SQL:

   SELECT * FROM emp WHERE empid > 100 FOR UPDATE

  当我们用条件检索数据,并请求共享或排他锁时,InnoDB不仅会对符合条件的empid值为101的记录加锁,也会对empid大于101(这些记录并不存在)的“间隙”加锁。

    这个时候如果你插入empid等于102的数据的,如果那边事物还没有提交,那你就会处于等待状态,无法插入数据。

  有关间隙锁所需讲的东西还是蛮多的,我会单独写一篇文章来分析间隙锁,并在文章中附上完整的示例。

 

 https://mp.weixin.qq.com/s/9EtN8lN3dfRwfwMZR_B_0A

面试题

  1、如果没有行锁,只有表锁会产生什么影响?

    表锁同一张表在同一时刻只能有一个更新。

  2、什么是行锁?

    行锁就是针对数据表中行记录的锁。这很好理解,比如事务 A通过索引更新了一行,而这时候事务 B 也要更新同一行,则必须等事务 A 的操作完成后才能进行更新。

  3、表级锁的种类

    一种是针对于表记录数据的锁

    一种是针对于表结构的锁,MDL锁,MDL锁是为了保证并发环境下读写的正确性。

  4、什么时候加 MDL 锁?

     1、MDL读锁:在我们对表数据进行增删改查的的时候都需要对表加MDL读锁。

     2、MDL写锁:当我们对表结构进行修改的时候会加MDL写锁。

     3、读锁与写锁互斥,写锁与写锁互斥,读锁与读锁不影响。

     4、事务中的 MDL 锁,在语句执行开始时申请,但是语句结束后并不会马上释放,而会等到整个事务提交后再释放。

      5、怎么减少行锁对性能的影响?

        1、在 InnoDB 事务中,行锁是在需要的时候才加上的,但并不是不需要了就立刻释放,而是要等到事务结束时才释放.如果你的事务中需要锁多个行,

        要把最可能造成锁冲突、最可能影响并发度的锁尽量往后放.

     2、处理死锁:1.超时机制,线程超时退出。2.死锁检测,出现死锁,回滚死锁链条中的任意事务。

  

 

 

 

https://mp.weixin.qq.com/s/9EtN8lN3dfRwfwMZR_B_0A

 

数据库的锁定机制简单来说,就是数据库为了保证数据的一致性,而使得共享资源在被并发访问变得有序所设计的一直规则。

 

 

 

 

 

 

 

 

posted @ 2022-10-25 19:35  dogRuning  阅读(243)  评论(0编辑  收藏  举报