Welcome everyone

mysql不得不学的恶心锁事 概念篇

mysql 汪明鑫 342浏览 0评论

就问你锁恶不恶心人

锁在程序员的世界里无处不在,因为并发无处不在

锁住共享资源,防止并发造成的问题

从Java锁到数据库锁,悲观锁、乐观锁,行锁、表锁,读锁、写锁,自旋锁,可重入锁,CAS,死锁,分布式锁等等等。。。

我是觉得锁这一大块很难啃,上面每一个点都需要花很多时间学习了解和思考

本篇主要初步探索mysql锁

 

 

 

全局锁

对整个数据库实例加锁

使整个库处于只读状态

flush tables with read lock

一般不怎么用,很少用到,个人觉得,开发了解即可,就算用到也是dba用吧估计

 

使用场景:
做全库逻辑备份,也就是把整个库的所有表都select出来存成文本

 

表级锁

表锁:

lock tables  表名  read/write;

unlock tables  (主动释放锁);

 

 

元数据锁(meta data lock,MDL):

也称为字典锁

MDL不需要显式使用, 在访问一个表的时候会被自动加上。
MDL的作用是:保证读写的正确性。
字典锁是为了保护数据对象被改变,一般是一些DDL会对字典对象改变,
如两个事务,事务1先查询表,然后事务2试图alter,
其首先需要等待事务1结束(提交或回滚),
此时的状态便是Waiting for table metadata lock,然后才能获得字典锁。
如果是线上业务的核心表出现了这样的锁等待队列,就会造成灾难性的后果。
字典锁与数据锁相对应。
数据锁是保护表中的数据,如两个事物同时更新一行时,先得到row lock的事务会先执行,后者只能等待。

详见 https://blog.51cto.com/277963679/1976802

 

页锁

页锁是一个页,锁的粒度基于行锁和表锁之间
(了解即可,myisam和innodb并没有页锁)

 

行锁

这一部分是我们需要关注的重点

Myisam是表锁,不支持行锁

mysql默认存储引擎Innodb支持行锁,因此我们重点关注Innodb的行锁即可

顾名思义,行锁就是锁住某些行,锁的力度更细,比表锁的并发性更好,表锁一锁就锁整张表

注意:在innodb事务中,行锁是在需要的时候才加上的。但并不是不需要了就立即释放,而是等到事务结束才释放。【两阶段锁协议】

误解:Innodb有行锁也有表锁,不是Innodb只有行锁

 

 

select for update 详述

select for update 是为了在查询时,避免其他用户以该表进行插入,修改或删除等操作,造成表的不一致性.
这个是面试经常爱问的

 

举几个例子:

 

select * from t for update 会等待行锁释放之后,返回查询结果。
select * from t for update nowait 不等待行锁释放,提示锁冲突,不返回结果
select * from t for update wait 5 等待5秒,若行锁仍未释放,则提示锁冲突,不返回结果
select * from t for update skip locked 查询返回查询结果,但忽略有行锁的记录

 

各场景下锁的情况
举个例子: 假设有个表单products ,里面有id跟name二个栏位,id是主键。
例1: (明确指定主键,并且有此笔资料,row lock)
SELECT * FROM products WHERE id=’3′ FOR UPDATE;
SELECT * FROM products WHERE id=’3′ and type=1 FOR UPDATE;
例2: (明确指定主键,若查无此笔资料,无lock)
SELECT * FROM products WHERE id=’-1′ FOR UPDATE;
例2: (无主键,table lock)
SELECT * FROM products WHERE name=’Mouse’ FOR UPDATE;
例3: (主键不明确,table lock)
SELECT * FROM products WHERE id<>’3′ FOR UPDATE;
例4: (主键不明确,table lock)
SELECT * FROM products WHERE id LIKE ‘3’ FOR UPDATE;

 

注:FOR UPDATE仅适用于InnoDB,且必须在交易区块(BEGIN/COMMIT)中才能生效。

 

 

MVCC

(Multi-Version Concurrency Control)多版本并发控制

锁具表中的一行记录,可能有多个版本,每个版本都有自己的row_trx_id(按申请顺序严格递增)

 

对数据库的任何修改的提交都不会直接覆盖之前的数据,而是产生一个新的版本与老版本共存,使得读取时可以完全不加锁。

 

Information_schema

在Information_schema有三个和锁相关的表INNODB_TRX,INNODB_LOCKS,INNODB_LOCK_WAITS

SELECT * from information_schema.INNODB_TRX;

详见 https://www.jianshu.com/p/f2906978f283

 

 

意向锁

为了允许行锁和表锁共存,实现多粒度锁机制

InnoDB还有两种内部使用的意向锁(Intention Locks),这两种意向锁都是表锁:

  • 意向共享锁(IS):事务打算给数据行加行共享锁,事务在给一个数据行加共享锁前必须先取得该表的IS锁。
  • 意向排他锁(IX):事务打算给数据行加行排他锁,事务在给一个数据行加排他锁前必须先取得该表的IX锁。

 

意向锁数据库隐式帮我们做了

 

间隙锁

只会在Repeatable read隔离级别下使用

Repeatable read隔离级别下再通过GAP锁即可避免了幻读

 

举个例子:

小明,小红,小花三个人依次站成一排(小明和小红是情侣,小红和小花是闺蜜)

如何让新来的小刚不能站在小红旁边,这时候只要将小红和她前面的小明之间的空隙封锁,将小红和她后面的小花之间的空隙封锁,那么小刚就不能站到小红的旁边。
这里的小红,小明,小花,小刚就是数据库的一条条记录。
他们之间的空隙也就是间隙,而封锁他们之间距离的锁,叫做间隙锁。

 

````
session 1:
start  transaction ;
select  * from news where number=5 for update;

session 2:
start  transaction ;
insert into news value(4,4);#(阻塞)
insert into news value(4,5);#(阻塞)
insert into news value(5,5);#(阻塞)
insert into news value(7,11);#(阻塞)
insert into news value(9,12);#(执行成功)
insert into news value(12,11);#(阻塞)
update news set number=5 where id=1;#(阻塞)
update news set id=11 where number=11;#(阻塞)
update news set id=2 where number=4 ;#(执行成功)
update news set id=4 where number=4 ;#(阻塞)
````

检索条件number=5,向左取得最靠近的值4作为左区间,向右取得11为右区间,因此,session 1的间隙锁的范围(4,5),(5,11)

 

锁定的意思是不得在(3,4)、(6,5)、(8,5)、(10,5)、(13,11)插入新值

摘自下面这篇文章,感受下大佬对间隙锁详细的分析和案例:

https://www.cnblogs.com/crazylqy/p/7821481.html

 

 

如有任何问题敬请指正

 

彩蛋

阿里本地生活双十一燥起来=-=

 

恭喜fpx这五个分奴夺得冠军

super carry doinb

 

转载请注明:汪明鑫的个人博客 » mysql不得不学的恶心锁事 概念篇

喜欢 (0)

说点什么

您将是第一位评论人!

提醒
avatar
wpDiscuz