当前位置: 首页 > news >正文

白话Mysql的锁和事务隔离级别!死锁、间隙锁你都知道吗?

听说微信搜索《Java鱼仔》会变更强哦!

本文收录于JavaStarter ,里面有我完整的Java系列文章,学习或面试都可以看看哦

(一)概述

我们把那些可能会被多个线程同时操作的资源称为临界资源,加锁的目的就是让这些临界资源在同一时刻只能有一个线程可以访问。这是当时在讲synchronized锁时提出的锁的概念。

数据库作为用户共享的一个资源,如何保证数据并发访问一致性也是所有数据库必须解决的问题,如何加锁是数据库并发访问性能的一个重要因素。

(二)关于数据库的锁

从加锁形式上分为乐观锁悲观锁

从对数据库操作的类型分为读锁(共享锁)写锁(排他锁)

读锁:多个读操作可以同时进行不会互相影响
写锁:当前写操作没有完成前,会排斥其他的读锁和写锁

从数据操作粒度上可以分为表锁行锁

2.1 表锁

顾名思义,表锁就是锁住整张表。这种操作开销小,加锁快,不会出现死锁,但是锁的粒度大,并发度低。MyISAM引擎在操作数据时就会自动给数据加上表锁。

可以通过下面几个参数手动增加表锁:

lock table 表名 read(write)

查看表上加过的锁:

show open tables;

在这里插入图片描述

删除锁:

unlock tables;

加了读锁后,将限制写入;加了写锁之后,将限制读和写;

2.2 行锁

行锁就是锁住一行数据,这种做法开销大,加锁慢,会出现死锁。但是锁的粒度小,并发度最高。InnoDB支持行锁,同时InnoDB还支持事务

通过一个简单的例子来介绍一下行锁,我们开启两个session(navicat中就是开启两个查询窗口)去操作数据库,其中在第一个查询中修改数据,但是不提交:

begin;
update test_innodb set name='javayz2' where id=1;

这个时候用另外一个查询窗口去修改同一行数据:

update test_innodb set name='javayz3' where id=1;

这时候这行更新语句会被一直阻塞,现在把第一个查询中修改的数据提交:

commit

你会发现第二个查询中的修改也立刻生效了:
查看结果,阻塞了30秒

在这里插入图片描述

注意:InnoDB的行锁是加在索引上的,如果索引失效或者修改的是非索引字段,行锁就会升级为表锁。

(三)Mysql的事务隔离级别

在讲事务隔离级别之前需要确保知道以下几个概念:

事务的ACID属性:原子性、一致性、隔离性、持久性

并发可能带来的问题:更新丢失、脏读、不可重复读、幻读

更新丢失:即多个事务同时更新一行数据,导致最后的更新覆盖了前面事务的更新。
脏读:即事务A读取到了事务B已修改但是未提交的数据。
不可重复读:即事务A读取某些数据后再读取这些数据,发现这些数据发生了改变。因为事务A读取到了其他事务提交的数据,不符合隔离性。
幻读:即事务A读取某些数据后按相同条件再读取发现多了新的数据,也是因为事务A读取到了其他事务新增的数据,不符合隔离性。

了解上面的概念之后,就可以知道Mysql的事务隔离级别了。事务隔离级别分为四种,下面通过表格的形式进行展示:

隔离级别脏读不可重复读幻读
读未提交(read-uncommitted)
读已提交(read- committed)×
可重复读(repeatable- read)××
可串行化(Serializable)×××

隔离级别越严格,并发带来的问题就越小,但是并发度也越低。

Mysql默认的事务隔离级别是可重复读。通过下列语句可查看

show VARIABLES like 'tx_isolation'

通过下列语句修改事务隔离级别:

set tx_isolation='READ-COMMITTED'  
//READ-UNCOMMITTED、READ-COMMITTED、REPEATABLE-READ、SERIALIZABLE

(四)MVCC机制

当事务隔离级别设置为可重复读时,事务A执行select之后,如果事务B修改了数据,事务A再select依然是上一次的数据,但是如果用insert、update和delete时,采用的数据就会是事务B修改后的数据,

比如事务A第一次查询到的age是23、事务B将age加1,事务A第二次查询age依然是23;如果事务A将age加1,再查询就是25。

可重复读的隔离级别采用的是MVCC(multi-version concurrency control)的机制,它的实现机制比较复杂,我后续会出一篇专门的文章来讲一下。他所实现的目的是select操作读取的是历史的快照版本,insert、update、delete更新的是最新的版本

(五)可重复读的情况下如何解决幻读问题

Mysql默认的事务隔离级别是可重复读,但是没有解决幻读的问题,如果改为可串行化,并发度又太低,因此能否在可重复读的情况下解决幻读问题也是面试中比较常遇见的。

解决办法是用间隙锁。在其中一个事务中执行update语句时,增加一个范围,比如:

update test_innodb set name='javayz3' where id>1 and id<10

id在1到10之间的数据就无法在其他事务中被插入或修改,自然也不会出现幻读的情况。

(六)关于死锁

在项目中偶尔会遇到死锁的情况,死锁的产生其实很简单,sessionA在等待sessionB的完成,sessionB在等待sessionA的完成,形成死循环。比如下面的更新语句:

A:begin;
   update test_innodb set name='aa' where id=1; //锁住id为1的行
B:begin;
   update test_innodb set name='bb' where id=2; //锁住id为2的行  
A:update test_innodb set name='aa' where id=2; 
B:update test_innodb set name='bb' where id=1;

最终A被B阻塞,B被A阻塞,从而导致了死锁。

在这里插入图片描述

Mysql中增加了死锁处理机制,对于死锁他会通过rollback解锁,但是有时遇到复杂的死锁情况就无法解锁。

(七)总结

整篇文章首先讲了Mysql的锁,Myisam使用表锁,InnoDB使用行锁。接着介绍了事务的基本原理,从而带出Mysql的事务隔离级别。简单介绍了MVCC机制,然后对于间隙锁、死锁进行讲解。掌握这些能让你在工作中或面试中遇到Mysql的问题有解决的思路。好了,这就是本期的所有内容了,我们下期再见!

相关文章:

  • Jquery datatables 使用方法
  • 基于SpringBoot实现文件的上传下载
  • 作为一个后端开发,你需要了解多少Nginx的知识?
  • CAShapeLayer(持续更新)
  • 一个成熟的Java项目如何优雅地处理异常
  • UITableView分页
  • 分布式集群环境下,如何实现每个服务的登陆认证?
  • 【中亦安图】Oracle内存过度消耗风险提醒(6)
  • 你知道JWT是什么吗?它和Session的区别又在哪里?
  • hadoop家族成员
  • 项目经理最近感觉系统慢了,想知道整个系统每个方法的执行时间
  • 获得指定文件夹所有文件的路径
  • 面试官问我:Zookeeper实现分布式锁的原理是什么?
  • typedef与#define的区别
  • 一步步教你如何在SpringBoot项目中引入支付功能
  • 《Java8实战》-第四章读书笔记(引入流Stream)
  • 【刷算法】从上往下打印二叉树
  • download使用浅析
  • echarts花样作死的坑
  • Essential Studio for ASP.NET Web Forms 2017 v2,新增自定义树形网格工具栏
  • Javascripit类型转换比较那点事儿,双等号(==)
  • JS进阶 - JS 、JS-Web-API与DOM、BOM
  • Logstash 参考指南(目录)
  • Map集合、散列表、红黑树介绍
  • 从重复到重用
  • 如何在 Tornado 中实现 Middleware
  • 入门到放弃node系列之Hello Word篇
  • 设计模式 开闭原则
  • 阿里云移动端播放器高级功能介绍
  • 积累各种好的链接
  • ​软考-高级-系统架构设计师教程(清华第2版)【第12章 信息系统架构设计理论与实践(P420~465)-思维导图】​
  • !!java web学习笔记(一到五)
  • # 计算机视觉入门
  • #DBA杂记1
  • #周末课堂# 【Linux + JVM + Mysql高级性能优化班】(火热报名中~~~)
  • (C语言)字符分类函数
  • (附源码)springboot 个人网页的网站 毕业设计031623
  • (附源码)计算机毕业设计SSM在线影视购票系统
  • (附源码)小程序儿童艺术培训机构教育管理小程序 毕业设计 201740
  • (五)大数据实战——使用模板虚拟机实现hadoop集群虚拟机克隆及网络相关配置
  • (一)RocketMQ初步认识
  • (原)Matlab的svmtrain和svmclassify
  • (转)视频码率,帧率和分辨率的联系与区别
  • (转载)Linux 多线程条件变量同步
  • .gitignore文件_Git:.gitignore
  • .net 获取url的方法
  • @Responsebody与@RequestBody
  • [AIR] NativeExtension在IOS下的开发实例 --- IOS项目的创建 (一)
  • [Android]如何调试Native memory crash issue
  • [android学习笔记]学习jni编程
  • [AS3]URLLoader+URLRequest+JPGEncoder实现BitmapData图片数据保存
  • [Assignment] C++1
  • [AutoSar]BSW_OS 02 Autosar OS_STACK
  • [BZOJ3211]:花神游历各国(小清新线段树)
  • [codeforces]Recover the String