mysql的锁机制,你真的了解吗?进来吧!用图表告诉你(代码片段)

java叶新东老师 java叶新东老师     2022-12-17     209

关键词:

什么是锁?

锁的存在是为了数据的一致性,我们都知道mysql在修改数据层面是支持并发修改的,那么在多个线程同时修改一个数据时产生的线程安全问题;什么是线程安全呢?大家想象这样的场景,一个数据,在高并发场景下,如果大家都去修改它,那这个数据不就乱套了,所以就有了锁的限制,大家排队来修改这个数据,必须上一个人改完之后,下一个人才能进行修改;

关于锁这个概念,还有另一种解释, 想象这样的场景,马桶只有一个,有10个人都想要上厕所,那么这个马桶同一时间只能被一个人使用(我从没见过2个人同时用一个马桶的场景),第一个人进去之后,就会把们反锁,只有这样,外面的人才进不来,只能在外面等着,当第一个人方便完之后解锁,然后出来,第二个人才能进去,第二个人出来第三个人才能进去,以此类推,一直到第10个人上完;这就是锁的概念。

mysql有三个存储引擎,分别是

  • memory
  • myisam
  • innodb

每个存储引擎都有不同的锁方式,分别为表锁和行锁

表锁

表锁指的是将整个表锁住,锁住后,任何人都不能往表中添加、修改或者删除数据;这是一种独占思想,也就是当表被某一个线程独占后,在独占的这段时间里,这个线程可以对表做任何操作,而别的线程只能眼巴巴地看着;就跟承包鱼塘一样,比如我承包了一块鱼塘一年时间,那么在这一年时间里面我可以对鱼塘做任何事情,养鱼养虾养王八,别人都管不着。

行锁

行锁比较容易理解,就是之锁一行数据,还拿上面池塘的例子来说,如果把池塘比喻成表,那么池塘里的鱼就是一行行的数据,池塘里会有成千上万条鱼,而你表里面的数据也是成千上万条的的,锁住一行记录,就相当于是抓住某一条鱼;拔掉它的鳞片或者是把这条鱼揍一顿就是修改这条鱼的数据,修改完后在将鱼放回池塘;这就是行锁。

memory

内存级别的表,不支持持久化存储,断电丢失数据,只支持表锁。

myisam

myisam只支持表锁,并且也不支持事务;所有的读写操作都是串行的

myisam在执行查询语句之前,会自动给涉及的所有表加读锁,执行更新操作前,会自动给涉及的表加写锁,这个过程不需要用户干预,因此用户一般不需要使用命令来显示加锁,下面的例子加锁时是为了演示效果,

表共享读锁

加读锁命令

lock table table_name read;

-- 解锁
unlock tables;

当前线程读,其他线程读(myisam)

加上读锁后,所有会话都可以读取A表数据,并且不会受影响,也不会阻塞

当前会话先读后写(myisam)

一旦给当前会话加上共享读锁后,就不能有写操作,,否则会报错,写操作包括(insert、update、delete)

当前会话程读,其他线程写(myisam)

加上共享读锁后,其他线程线程写入会进入阻塞状态,待解锁后可进行写操作;

会话1给A表加读锁,会话1在查询查询其他表(myisam)

会话1给A表加读锁后,再读其他表会报错,要先释放A表的读锁才可以读其他表;这是一个比较特别的知识点

只锁当前会话的读锁(read local)

加上下面的语句之后,当前会话可读,不可写,其他会话可写可读;

-- local表示本地
lock table table_name read local;

但是会有并发插入问题,也就是说,在当前会话开启read local 锁之后,在别的会话插入的数据,当前会话查询不到;

表独占写锁

加写锁命令:

lock table table_name write;

-- 解锁
unlock tables;

加上写锁后当前线程可以进写入操作,当前线程也可以读,但是其他线程的读写操作都会阻塞;

innodb

事务的 四个特征(ACID)

innodb支持表锁和行锁,innodb锁的对象是索引,聊到innodb的索引,肯定要牵扯到事务,事务有4个属性,称为ACID属性

  1. 原子性(Actimicity): 事务是原子操作,要么同时修改,要么同时回滚
  2. 一致性(Consistent):事务完成时必须保证数据一致性
  3. 隔离性(Isolation):数据库系统提供一定的隔离机制,保证事务在不受外部并发操作时影响独立的环境之下
  4. 持久性(Durable):也称永久性,指一个事务一旦提交,它对数据库中的数据的改变就应该是永久性的。接下来的其它操作或故障不应该对其执行结果有任何影响。

脏读

脏读就是指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问 这个数据,然后使用了这个数据。

幻读

是指当事务不是独立执行时发生的一种现象,例如第一个事务对一个表中的数据进行了修改,这种修改涉及到表中的全部数据行。 同时,第二个事务也修改这个表中的数据,这种修改是向表中插入一行新数据。那么,以后就会发生操作第一个事务的用户发现表中还有没有修改的数据行,就好象 发生了幻觉一样。例如,一个编辑人员更改作者提交的文档,但当生产部门将其更改内容合并到该文档的主复本时,发现作者已将未编辑的新材料添加到该文档中。 如果在编辑人员和生产部门完成对原始文档的处理之前,任何人都不能将新材料添加到文档中,则可以避免该问题。

不可重复读

是指在一个事务内,多次读同一数据。在这个事务还没有结束时,另外一个事务也访问该同一数据。那么,在第一个事务中的两 次读数据之间,由于第二个事务的修改,那么第一个事务两次读到的的数据可能是不一样的。这样就发生了在一个事务内两次读到的数据是不一样的,因此称为是不 可重复读。例如,一个编辑人员两次读取同一文档,但在两次读取之间,作者重写了该文档。当编辑人员第二次读取文档时,文档已更改。原始读取不可重复。如果 只有在作者全部完成编写后编辑人员才可以读取文档,则可以避免该问题。

数据库的事务隔离越严格,并发副作用越小,但付出的代价也越大,因为事务隔离本质上就是将事务在一定程度上串行化,需要根据具体的业务需求来决定使用哪种隔离级别,

innodb的锁分为共享锁、排他锁、意向共享锁、意向排他锁、间隙锁、自增锁,间隙锁和自增锁问的比较少;

  • 共享锁,可以理解为读锁
  • 排他锁,可以理解为写锁;

注意:演示下面的案例时需要先关闭自动提交的功能

加共享锁(innodb)

select * from table where id =  1 lock in share mode;

事务1读,其他事务也可以读(innodb)

加了共享锁后,所有线程都可以读数据

事务1读,事务2写会阻塞(innodb)

当开启读锁后,当前事务可以读数据,其他事务也可以写数据,但是需要等事务1释放锁后才可以写,所以当其他事务进行写的时候会进入阻塞状态。

事务A写会报错(innodb)

当开启读锁后,当前事务只能读数据,而不能写数据,写数据时会报错。

加排它锁(innodb)

select * from table where id =  1 for update;
  • 事务A获取锁,其他的的事务都会进行阻塞等待

需要注意的是,innodb是支持行锁的,行锁是通过给索引项加锁实现的,所以你的表里面必须要有索引,否则就会退化成表锁;另外,insert 插入数据的时候因为你是针对整张表插入,而不是针对某一行数据,所以insert也是表锁;

意向共享锁

事务准备给数据行加入共享锁,也就是说一个数据行在加共享锁之前必须先取得意向共享锁;

意向排他锁

事务准备给数据行加入排他锁,也就是说一个数据行在加排他锁之前必须先取得意向排他锁;

意向这个东西,你就理解为加锁之前的准备工作,就好像我吃饭之前需要拿出筷子一样

意向锁是innodb存储引擎操作数据之前自动加的,不需要用户干预

自增锁

下面的 auto_increment表示该字段为自增字段,默认每次自增1

create table tablename(
  id int auto_increment primary key,
  name varchar(20)
) 

自增锁是保证每次都自增的,比如有如下场景

insert into tablename (name) values('yexindong'); -- 第1
insert into tablename (name) values('yexindong'); -- 第2
commit; -- 提交

insert into tablename (name) values('yexindong'); -- 第3
insert into tablename (name) values('yexindong'); -- 第4
rollback; -- 回滚

insert into tablename (name) values('yexindong');-- 第5
commit; -- 提交

假如我们的自增id 是从1开始的,前两次插入的提交了,id为1和2;

虽然第3、4次的插入回滚了,

但是第五次插入的id其实是5,有人会说了,3和4不是回滚了吗?按理说第5次插入的id应该是3啊;因为自增锁为了保证自增的,3和4已经被使用过了,不管你有没有插入,还会往上自增,所以第五次插入的id是5,而不是3;

模拟innodb死锁

注意:演示下面的案例时需要先关闭自动提交的功能

以下4个步骤下来,就会产生死锁

1、事务A

select * from user where user = 1 for update

2、事务B

select * from user where user = 2 for update

3、事务B

select * from user where user = 1 for update

4、事务A

select * from user where user = 2 for update

以上原理是因为谁都不想释放,又想得到对方的锁,所以就会产生死锁,就好像两个人吵架了,都不肯道歉,都在等对方认错,然后关系就越来越僵,最后导致绝交;

行锁演示

比如我有这么以下user表数据

create table user(
 id int(20) parmary key,
 name varchar(20) 
);

-- 给name加上普通索引
alter table user add index index_name(name);

-- 插入数据
insert into user (id,name) values(1,'yexindong');
insert into user (id,name) values(2,'zhangsan');

下面的例子,我查询下面2条语句时锁的其实是同一行,会有冲突


-- 查询下面2条语句时锁的其实是同一行,会有冲突
select * from user where id = 1 for update;
select * from user where name = 'yexindong' for update;

操作不同的行,不会有冲突,

-- 以下2条sql锁的是不同的行,不会冲突
select * from user where id = 1 for update;
select * from user where name = 'zhangsan' for update;

关于mysql的锁知识,到这里就讲完了,其实java里面的锁更多,而且锁的知识都是一通百通的,知道java 的锁就一定了解mysql的锁,那么问题又来了,你知道mysql的锁是公平锁还是非公平锁呢?

类加载机制你真的了解吗?(代码片段)

...使用。以下是《深入理解Java虚拟机第二版》对类加载器机制的定义原文:虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类加... 查看详情

面试必问的jvm类加载机制,你真的了解吗?(代码片段)

...问,读完本篇文章,你将收获满满。探索类加载机制1.加载的过程类从被加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期包括 查看详情

面试必问的jvm类加载机制,你真的了解吗?(代码片段)

...问,读完本篇文章,你将收获满满。探索类加载机制1.加载的过程类从被加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期包括 查看详情

asynctask你真的用对了吗?

...了Handler,《从Handler.post(Runnabler)再一次梳理Android的消息机制(以及handler的内存泄露)》我们知道了Android的消息机制主要靠Handler来实现,但是在Handler的使用中,忽略内存泄露的问题,不管是代码量还是理解程度上都显得有点不... 查看详情

你真的了解maven吗?(代码片段)

...跨平台的自动化项目构建方式。Maven并没有消除了解底层机制的需要,但是Ma 查看详情

你真的了解maven吗?(代码片段)

...跨平台的自动化项目构建方式。Maven并没有消除了解底层机制的需要,但是Ma 查看详情

你真的了解cdc吗?(代码片段)

ChangeDataCapture[1]简称CDC,用于异构数据同步,将database的数据同步到第三方,这里的DB可以是MySQL,PG,Mongo等等一切数据源,英文技术圈称之为SingleSourceOFTrue(SSOT),目标称为DerivedDataSystems。常见的使用场景有:缓存Cache,异... 查看详情

你真的了解asynctask吗?asynctask源码分析(代码片段)

转载请注明出处:http://blog.csdn.net/yianemail/article/details/516113261,概述AndroidUI是线程不安全的,如果想要在子线程很好的访问ui,就要借助Android中的异步消息处理机制http://blog.csdn.net/yianemail/article/details/5023 查看详情

[py]你真的了解多核处理器吗?了解多线程

你真的了解多核处理器吗?1.双核≠双性能多核不一定会使你的手机或电脑速度更快,但它将提高你的PC的整体性能,这是一个有所不同的细微的技术特色。多核处理器的性能提升并不是简单CPU核心的倍数,因为受到两(多)个... 查看详情

javascript你真的了解节点操作吗?

查看详情

javascript你真的了解节点操作吗?

查看详情

你真的了解java吗?

前言从今天开始,我会从java基础开始,给大家一步一步分享文章,尽量用简单,容易的语言去描述,把我的感想分享给大家,希望对大家有所帮助,也希望大家持续关注!一.介绍1.1、计算机语言指用于人与计算机之间通讯的语... 查看详情

你真的了解箭头函数吗(代码片段)

...的可能选择不用,多码几行字的事。但是用过的人说真的爽。那么本文就来搞明白箭头函数的玩法。箭头函数使用下面就根据普通函数和箭头函数的对比使用来更加深入的了解使用箭头函数吧1.语法格式上我们普通函数语法... 查看详情

你真的了解箭头函数吗(代码片段)

...的可能选择不用,多码几行字的事。但是用过的人说真的爽。那么本文就来搞明白箭头函数的玩法。箭头函数使用下面就根据普通函数和箭头函数的对比使用来更加深入的了解使用箭头函数吧1.语法格式上我们普通函数语法... 查看详情

你真的了解uitableviewcell重用吗?

一:首先查看一下关于UITableViewCell重用的定义-(nullable__kindofUITableViewCell*)dequeueReusableCellWithIdentifier:(NSString*)identifier;-(__kindofUITableViewCell*)dequeueReusableCellWithIdentifier:(NSString*)identif 查看详情

你真的了解uieventuitouch吗?

一:首先查看一下关于UIEvent的定义//事件类型typedefNS_ENUM(NSInteger,UIEventType){UIEventTypeTouches,UIEventTypeMotion,UIEventTypeRemoteControl,};//触摸事件的类型typedefNS_ENUM(NSInteger,UIEventSubtype){UIEventSubtypeNone=0,/ 查看详情

通过各种简单案例,让你彻底搞懂mysql中的锁机制与mvcc(代码片段)

文章目录锁的分类表级锁与行级锁共享锁与排他锁意向锁行级锁实现记录锁通过主键操作单个值通过唯一索引操作单个值间隙锁通过主键操作范围值通过唯一索引操作范围值Next-key锁通过普通索引操作单个值通过普通索引操作范... 查看详情

你真的了解uiapplication吗?

一:首先查看一下关于UIApplication的定义NS_CLASS_AVAILABLE_IOS(2_0)@interfaceUIApplication:UIResponder//获得单例对象+(UIApplication*)sharedApplicationNS_EXTENSION_UNAVAILABLE_IOS("Useviewcontrollerbasedsolutionswhereappr 查看详情