什么是锁
锁是计算机协调多个进程或线程并发访问某一资源的机制。在数据库中,除传统的计算资(
CPU
、 RAM、
I/O
)的争用以外,数据也是一种供许多用户共享的资源。如何保证数据并发访的一致性、有 效性是所有数据库必须解决的一个问题,锁冲突也是影响数据库并发访问性能的一个重要因素。
MySQL
中的锁,按照锁的粒度分,分为以下三类:
全局锁:锁定数据库中的所有表。
表级锁:每次操作锁住整张表。
行级锁:每次操作锁住对应的行数据。
按照锁类型,可分问共享锁(读锁)和排他锁(写锁)
1. 全局锁
全局锁就是对整个数据库实例加锁,加锁后整个实例就处于只读状态,后续的
DML
的写语句,
DDL
语 句,已经更新操作的事务提交语句都将被阻塞。
其典型的使用场景是做全库的逻辑备份,对所有的表进行锁定,从而获取一致性视图,保证数据的完整性。
加全局锁:
flush tables with read lock ;
数据备份:
mysqldump -uroot –p1234 itcast > itcast .sql
释放锁:
unlock tables ;
2. 表级锁
2.1表锁
对于表锁,分为两类:
表共享读锁(read lock)
表独占写锁(
write lock
)
语法:
加锁:
lock tables
表名
... read/write
。
释放锁:
unlock tables /
客户端断开连接 。
2.2元数据锁
meta data lock ,
元数据锁,简写
MDL
。
MDL加锁过程是系统自动控制,无需显式使用,在访问一张表的时候会自动加上。MDL
锁主要作用是维
护表元数据的数据一致性,在表上有活动事务的时候,不可以对元数据进行写入操作。
为了避免
DML
与
DDL
冲突,保证读写的正确性。
2.3 意向锁
假如没有意向锁,客户端一对表加了行锁后,客户端二如何给表加表锁呢,来通过示意图简单分析一 下:
首先客户端一,开启一个事务,然后执行
DML
操作,在执行
DML语句时,会对涉及到的行加行锁。
当客户端二,想对这张表加表锁时,会逐行检查当前表是否有行锁,如果没有,则添加表锁,此时就会从第一行数据,检查到最后一行数据,效率较低。
1
有了意向锁之后
:
客户端一,在执行
DML操作时,会对涉及的行加行锁,同时也会对该表自动加上意向锁。
而其他客户端,在对这张表加表锁的时候,不用逐行判断行锁情况了。
意向共享锁(IS): 由语句select ... lock in share mode添加 。 与 表锁共享锁 (read)兼容,与表锁排他锁(write)互斥。
意向排他锁(IX): 由insert、update、delete、select...for update添加 。与表锁共 享锁(read)及排他锁(write)都互斥,意向锁之间不会互斥。
3. 行级锁
分为3类:
3.1 行锁
锁定单个行记录的锁,防止其他事务对此行进行update和delete。在 RC、RR隔离级别下都支持。
3.2 间隙锁
锁定索引记录间隙(不含该记录),确保索引记录间隙不变,防止其他事 务在这个间隙进行insert,产生幻读。在RR隔离级别下都支持。
3.3 临键锁
行锁和间隙锁组合,同时锁住数据,并锁住数据前面的间隙Gap
。 在RR
隔离级别下支持。
默认情况下,
InnoDB在 RR事务隔离级别运行,InnoDB使用 next-key
锁进行搜索和索引扫描,以防止幻读。
索引上的等值查询(唯一索引
)
,给不存在的记录加锁时
,
优化为间隙锁 。
索引上的等值查询(
非唯一普通索引
)
,向右遍历时最后一个值不满足查询需求时,
next-key
lock
退化为间隙锁。
索引上的范围查询
(
唯一索引
)--
会访问到不满足条件的第一个值为止。
注意:间隙锁唯一目的是防止其他事务插入间隙。间隙锁可以共存,一个事务采用的间隙锁不会 阻止另一个事务在同一间隙上采用间隙锁。
4 乐观锁与悲观锁
4.1 悲观锁
悲观锁假设最坏的情况,即在数据处理过程中,总是假设会发生冲突,因此在数据处理之前先加锁,这种锁通常由数据库自身提供支持,如MySQL的 SELECT FOR UPDATE。
使用场景:
当数据竞争较多,并发量高
更新和删除操作多的应用场景。
4.2 乐观锁
乐观锁采用一种宽松的加锁机制,它假设多个事务在大多数时间不会同时修改同一数据。通常是通过版本号或时间戳来实现。每次数据更新过程中,检查版本号或时间戳是否发生变化,如果没有变化,则进行更新;如果已经变化,则放弃更新或重试。
使用场景:
当数据竞争较少,冲突不频繁时,乐观锁能减少锁的开销,提高系统的整体性能。
适用于读多写少的应用场景。