当前位置:首页 > Java技术 > 性能优化|使用这种方法,不信你还记不住mysql高频面试知识点

性能优化|使用这种方法,不信你还记不住mysql高频面试知识点

2022年08月05日 21:37:44Java技术2

微信搜一搜【AI码师】关注帅气的我,回复【干货领取】,领取2021最新面试资料一份

表锁与行锁的理解

表锁

  • 定义
    进行表操作的时候,mysql会锁住整张表,加锁和解锁效率非常高,不会出现死锁竞争,但是会造成锁阻塞,导致并发低。
  • 命令
    • 手动加表锁:
      lock table tableName1 read[write],tableName2 read[write];
    • 查看表上加过的锁:

      show open tables where in_use > 0;
    • 解除锁:

      UNLOCK tables;
  • 实战
    创建初始化的表:
   SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
DROP TABLE IF EXISTS `locak_table`;
CREATE TABLE `locak_table` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL,
  `score` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
BEGIN;
INSERT INTO `locak_table` VALUES (1, '刘备', 350);
INSERT INTO `locak_table` VALUES (2, '张飞', 16000);
INSERT INTO `locak_table` VALUES (3, '关羽', 2400);
COMMIT;
SET FOREIGN_KEY_CHECKS = 1;

图中圆圈在转表示被阻塞

  • 会话A给表加写锁,会话B读或者写被阻塞
    性能优化|使用这种方法,不信你还记不住mysql高频面试知识点 _ JavaClub全栈架构师技术笔记
    性能优化|使用这种方法,不信你还记不住mysql高频面试知识点 _ JavaClub全栈架构师技术笔记
    性能优化|使用这种方法,不信你还记不住mysql高频面试知识点 _ JavaClub全栈架构师技术笔记
  • 会话A给表加读锁,会话B写会被阻塞,读不阻塞
    性能优化|使用这种方法,不信你还记不住mysql高频面试知识点 _ JavaClub全栈架构师技术笔记
    性能优化|使用这种方法,不信你还记不住mysql高频面试知识点 _ JavaClub全栈架构师技术笔记
    性能优化|使用这种方法,不信你还记不住mysql高频面试知识点 _ JavaClub全栈架构师技术笔记

行锁

定义

为每一行数据添加锁,加锁慢,容易出现死锁竞争,因为锁的每一行数据,锁的力度小,所以并发高,Innodb支持行级锁,行级锁是支持事务的。

行级锁事务的ACID属性

  • 原子性:为了完成一个业务,需要执行多条数据库修改语句,这些语句中如果哪条执行失败,将会全部回滚第一条语句执行前的状态,如果没有执行失败的语句,则代表执行成功,一句话理解:要么全部执行,要么全部不执行。
  • 一致性:一致性关注的是事务开始前的状态和事物结束时和库中状态要保持一致。
  • 隔离性:在执行事物的过程中,对数据库的操作对其他事务是不可见的,只有提交了事务,外界才能看到变化后的数据。
  • 持久性:事务对数据库的修改都必须永久保存在磁盘文件中(数据库中)。

多个事务同时操作数据带来的并发问题

  • 脏读
    有两个事务A和B,如果A事务修改了数据库的内容,被B事务读到了,可这个时候A回滚了事务,这个时候B读到的事务就是不存在,这个就是脏读(读到了不存在的数据

  • 不可重复读
    事务A使用同样的查询条件的SQL查询数据库,两次查询的结果不一致,事务A读到了其他事务已提交修改的数据,这就是不可重复读。

  • 幻读
    在事务中,
    1.事务A首先按照id为1的检索条件去库中查询,发现没有此条记录
    2.在事务A查询完之后,事务B插入了一条id为1的记录。
    3.事务A为了保证业务正确性,在进行一系列操作后,又查询了一遍,确保记录不存在,由于可重复的隔离性,这个时候还是查不到该条记录的。
    3.事务A在判断没有此条记录之后,就往库里面插入一条,但是此时会发现库中有两条记录,违背了业务逻辑。
    这个只是幻读的其中一个模拟场景。

    这纯属个人的理解,如果有不同见解,可以忽略此条解释!!!

如何解决这些并发问题呢?mysql使用四大隔离级别来解决,一级更比一级强,并发量一级比一级低。

事务的隔离级别

  • 读未提交:最低级别,也是并发数最高的,有可能出现脏读、不可重读读、幻读,也可以说根本没有解决事务并发带来的问题,写操作比较少的场景可以使用该级别
  • 读已提交:有可能出现不可重复读、幻读。
  • 可重复读:可能出现幻读
  • 可串型化:彻底解决并发问题,它使用“单线程”控制事务的执行顺序。

mysql默认的事务隔离级别是:读已提交(REPEATABLE-READ)
设置隔离级别:set tx_isolation=‘REPEATABLE-READ’;
查询支持的事务隔离级别: show variables like ‘tx_isolation’;

事务隔离级别的验证

初始化SQL:

CREATE TABLE `locak_table` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL,
  `age` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
INSERT INTO `xnyh`.`locak_table`(`id`, `name`, `age`) VALUES (1, '刘备', 350);
INSERT INTO `xnyh`.`locak_table`(`id`, `name`, `age`) VALUES (2, 'hanmei', 16000);
INSERT INTO `xnyh`.`locak_table`(`id`, `name`, `age`) VALUES (3, 'lucy', 2400);
  • 验证读未提交

事务A操作
性能优化|使用这种方法,不信你还记不住mysql高频面试知识点 _ JavaClub全栈架构师技术笔记

事务B对数据库进行了操作,但是还没有提交事务
性能优化|使用这种方法,不信你还记不住mysql高频面试知识点 _ JavaClub全栈架构师技术笔记

在事务A中再执行一下查询语句,发现事务B的修改已经被事务A查询到了
性能优化|使用这种方法,不信你还记不住mysql高频面试知识点 _ JavaClub全栈架构师技术笔记

在业务操作中,我们一般会将读到的数据取出来,做更新操作,如果现在需要对age+100,那么事务A更新到库中的结果是400,但是如果事务B回滚了,库中age就会恢复到350的状态,如果这时候事务B把400更新到库中,400相当于350并没有执行加100的操作,所以就出现了脏读,造成业务数据出现问题。

如果需要读到别人确保不会回滚的数据,那就得设置隔离级别为read-committed

  • 验证读已提交
    set tx_isolation=‘read-committed’;

    事务A执行同样的操作

    性能优化|使用这种方法,不信你还记不住mysql高频面试知识点 _ JavaClub全栈架构师技术笔记


事务B
性能优化|使用这种方法,不信你还记不住mysql高频面试知识点 _ JavaClub全栈架构师技术笔记


事务A再次执行查询语句发现并没有查询到B未提交的语句,解决了脏读问题
性能优化|使用这种方法,不信你还记不住mysql高频面试知识点 _ JavaClub全栈架构师技术笔记


事务B提交事务
性能优化|使用这种方法,不信你还记不住mysql高频面试知识点 _ JavaClub全栈架构师技术笔记

事务A再进行查询,发现读到了别人提交的修改数据
性能优化|使用这种方法,不信你还记不住mysql高频面试知识点 _ JavaClub全栈架构师技术笔记

虽然这个解决了脏读的问题,但是又会造成一个新的问题,就是相同的查询条件,查询结果不一致,产生了不可重复读的问题,这又得设置隔离级别为可重复读来解决。

  • 验证可重复读

    set tx_isolation='repeatable-read';

    因为在5.7中,mysql默认的事务隔离级别就是可重复读,所以可以不用进行声明设置。
    需要注意的是验证可重复读,不能再使用navcat客户端来执行sql的,我试了几次发现在navcat验证可重复读会失效,具体原因没有研究,我们现在使用mysql命令行来验证。
    事务A设置事务为可重读,并查询
    性能优化|使用这种方法,不信你还记不住mysql高频面试知识点 _ JavaClub全栈架构师技术笔记


    事务B修改数据并提交

    性能优化|使用这种方法,不信你还记不住mysql高频面试知识点 _ JavaClub全栈架构师技术笔记

事务A再次查询:

性能优化|使用这种方法,不信你还记不住mysql高频面试知识点 _ JavaClub全栈架构师技术笔记

发现查询结果和之前一致,如果这时候我们在事务A中对age进行修改操作会出现什么效果呢?
性能优化|使用这种方法,不信你还记不住mysql高频面试知识点 _ JavaClub全栈架构师技术笔记

大家觉得,现在库里面应该是多少呢,是300+300还是321+300?
性能优化|使用这种方法,不信你还记不住mysql高频面试知识点 _ JavaClub全栈架构师技术笔记

可以看到是300+300=600,使用了事务B的修改数据,也就是当事务A执行修改操作的时候,会使用库中最新的状态来进行修改,从而保证了数据的一致性,这里面的机制是在可重复读的隔离级别中,mysql使用MVCC(多版本并发控制)机制来实现的。具体实现机制,我会在最后介绍。

现在我们已经把可重复读验证完了,但是你不要觉得可重复读就是完美的了,它也是有缺陷的,有可能会出现幻读的现象,幻读的理解在前面已经解释过,不理解的同学可以回头再看一遍。

既然有幻读这个问题,mysql又是如何解决的呢?
1.使用可串型化,这个没有什么可解释的,像“单线程”一样按照顺序依次执行每个事务;
2.在某些场景下可以使用间隙锁解决

  • 间隙锁如何解决幻读的问题?
    间隙锁就是锁住某个范围区间的索引,当你开启事务后,使用了间隙锁
    ,那么别的用户就不能对操作者范围区间的数据了。如果在可重读隔离模式下,可以在sql语句上面加上for update,让mysql给索引加上间隙锁,如:
    我想查询id = 10 的记录是否存在,如果不存在就插入一条
    select * from locak_table where id = 10 for update;

    当mysql发现索引中有这条记录,就会加上行锁,如果没有就会使用间隙锁: 如果数据库中最大的自增ID是8,那么这个时候间隙锁的范围就是8-11区间的值都被锁住,其他数据无法插入。
    如果当前字段没有创建索引则会升级为表锁。

MVCC机制

定义

MVCC在MySQL InnoDB中的实现主要是为了提高数据库并发性能,用更好的方式去处理读-写冲突,做到即使有读写冲突时,也能做到不加锁,非阻塞并发读。

什么是当前读和快照读

  • 当前读
    读取事务中最新的数据记录,并且能够保证自己在读取之后,其他事务不能并发修改当前数据。
  • 快照读
    快照读不需要加锁去读取数据库中的数据,它依赖多版本的机制,在开启事务后,他会被分配一个最新的事务ID,它只会读取小于等于当前事务ID之前的数据,也就是有可能他会读到历史版本的数据,但是她读到的数据是不会变化的,这也就是MVCC的实现机制,这也就是为什么MVCC能够解决不可重复读的原因。

MVCC带来的好处

  • 多版本并发控制(MVCC)是一种用来解决读-写冲突的无锁并发控制,也就是为事务分配单向增长的时间戳,为每个修改保存一个事务ID,读操作只读该事务开始前的数据库的快照。 所以MVCC可以为数据库解决以下问题

  • 在并发读写数据库时,可以做到在读操作时不用阻塞写操作,写操作也不用阻塞读操作,提高了数据库并发读写的性能
    同时还可以解决脏读,幻读,不可重复读等事务隔离问题,但不能解决更新丢失问题

MVCC的具体实现

Innodb 的MVCC机制会在底层为数据库中的每行记录都扩展两个虚拟字段(创建事务ID、删除事务ID),我们对数据进行添加操作时,会在记录的创建事务ID字段中保存当前事务ID;删除记录的时候,会将当前事务ID保存在删除数据中的删除事务ID中,并不会直接删除数据;更新数据的时候,会将当前修改后的数据复制一条出来插入库中,然后记录的创建事务ID为当前事务ID,并且将旧记录的删除事务ID更新当前事务ID;我们接下来看增删改查是如何操作的。
假设当前库中的最新事务ID为1

  • 演示添加数据
begin;
 
  INSERT INTO `xnyh`.`locak_table`(`id`, `name`) VALUES (1, '张飞');
  INSERT INTO `xnyh`.`locak_table`(`id`, `name`) VALUES (2, '刘备');
  commit;

执行完上面的语句后,库中最新的事务ID为2了,需要注意的是,事务分配ID并不是在begin之后就会被分配新的事务ID,只有你在执行了update or insert or delete语句之后,才会向mysql申请新的事务ID

这时候表中的数据应该是这样的

id name 创建事务ID 删除事务ID
1 张飞 2 Undefined
2 刘备 2 Undefined

如果这个时候事务ID为1进行查询,是查询不到的,因为库中记录的创建事务ID是大于1 的。

  • 演示修改数据
begin;
update locak_table set name= '刘禅' where id =1;
commit;

当前已经为事务ID为3 了

  • mysql首先复制一条当前记录,插入数据库,并将name修改为新值,创建事务ID保存为当前事务ID,
  • 将老数据的删除事务ID更新为当前事务ID
    表中当前记录应该是这样:
id name 创建事务ID 删除事务ID
1 张飞 2 3
2 刘备 2 Undefined
1 刘禅 3 Undefined

如果这个时候事务ID为2 进行查询,是查询不到新修改的数据,它的查询条件是这样的
创建事务ID要小于等于2并且删除事务ID大于2 或者为undefined。
所以事务2查出来的数据还是2条

  • 删除验证
    删除和更新操作是一样的,不过不需要复制一条新的数据,只需要把当前事务ID赋值给删除记录的删除事务ID。

    文末给大家留一个疑问:

    如果当前有两个事务A和事务B,如果事务A进入开启事务后,并且获得事务ID为10,事务B开启事务后,并且获取事务ID为11,如果这个时候事务ID10向库中添加了一条记录,并提交,请问:事务ID为11的能查到么?如果查不到那是为什么呢?欢迎大家在下面留言回答。

性能优化|使用这种方法,不信你还记不住mysql高频面试知识点 _ JavaClub全栈架构师技术笔记

作者:乐哥聊编程
来源链接:https://lglbc.blog.csdn.net/article/details/109141560

版权声明:
1、JavaClub(https://www.javaclub.cn)以学习交流为目的,由作者投稿、网友推荐和小编整理收藏优秀的IT技术及相关内容,包括但不限于文字、图片、音频、视频、软件、程序等,其均来自互联网,本站不享有版权,版权归原作者所有。

2、本站提供的内容仅用于个人学习、研究或欣赏,以及其他非商业性或非盈利性用途,但同时应遵守著作权法及其他相关法律的规定,不得侵犯相关权利人及本网站的合法权利。
3、本网站内容原作者如不愿意在本网站刊登内容,请及时通知本站(javaclubcn@163.com),我们将第一时间核实后及时予以删除。


本文链接:https://www.javaclub.cn/java/17979.html

分享给朋友:

“性能优化|使用这种方法,不信你还记不住mysql高频面试知识点” 的相关文章

什么是重载or重写,二者的区别是?

目录 1.重写 2.重载 3.重载(Overload) 和重写(Override) 的区别? 1.重写 重写必须满足如...

Java空指针异常解决java.lang.NullPointerException解决心得

Java空指针异常解决java.lang.NullPointerException解决心得

今天做课设的时候运行程序报出以下错误 java.lang.NullPointerException 首先要理解的是此错误并不会在 程序中报错,只会在运行的时候报错。 是由于某个参数(集合,数组等数据)可能出现一个null值而导致后面的程序不能运行时...

一分钟搞定Java环境变量配置

一分钟搞定Java环境变量配置

对于学Java的人来说,成功配置环境变量是第一步,因为后期不论 你做什么工作,会发现都需要这些,接下来介绍如何安装与配置,我按照jdk1.6来说明,其他一致。 下载官网 首先将jdk安装好后进行配置。 右击“计算机”,右键打开“属性”,...

yum安装步骤(网络下载安装)

yum安装步骤(网络下载安装)

yum是Linux系统的安装必备神器,简直不要太方便。但是新系统一般是不自带yum工具的,所以需要手动安装一下。环境:centos7新建一个目录用来保存yum安装包 mkdir install  进入文件夹并输入命令wget http://yum.bas...

记一次SpringBoot项目使用RedisTemplate无法反序列化字节数组问题困扰8小时巨坑

记一次SpringBoot项目使用RedisTemplate无法反序列化字节数组问题困扰8小时巨坑

一、问题描述 问题描述: 前提:向Redis中SET值的是一个JFinal项目,使用的Jedis客户端API操作的,把一个对象转成byte[]形式存入Redis中。 然后在另一个SpringBoot项目中我通过Spring自带的RedisTempl...

[springboot]Slf4j日志框架的体系结构

文章目录 一、五花八门的日志工具包 1.1. 日志框架 1.2.日志门面 1.3日志门面存在的意义...

如何读取服务器中文本文件并转为文字

如何读取服务器中文本文件并转为文字 ​ 开发代码的过程中,当数据内容过长或者过大时,考虑性能,一般会选择将数据上传到文件服务器,而并非存到数据库中,当需要对数据进行解析处理时,需要把文件从服务器中读取下来并解析为想要的数据,这里将解析为字符串的方法进行示例...

SpringBoot之整合Mybatis篇

SpringBoot之整合Mybatis篇

开头语 啪嚓,醒木一拍(咳咳) ,上回书我们说到:SpringBoot的简单搭建,也就是SpringBoot的搭建流程,简单的将项目进行启动,本次,我们简单的把SpringBoot与持久层框架Mybatis进行整合,接下来,我们开始。 Mybatis名词解释   &...

Dubbo原理浅析

Dubbo原理浅析

一、Dubbo是什么 百度百科(没有什么比度娘知道的更多):Dubbo是阿里巴巴公司开源的一个高性能优秀的服务框架,使得应用可通过高性能的 RPC 实现服务的输出和输入功能,可以和Spring框架无缝集成。Dubbo是一款高性能、轻量级的开源Java RPC框架,它提供了三大核心能力:面向接...

SpringBoot整合Dubbo与zookeeper纯注解版

SpringBoot整合Dubbo与zookeeper纯注解版

一、Dubbo和zk的作用 上回讲到,Dubbo作为一款优秀的RPC框架,封装了dubbo-provider(提供者)和dubbo-consumer(消费者),而provider和consumer之间需要通过注册中心来作为可发现的服务目录。而zookeeper(此处简称zk)提供了服务接口注...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。