文章目录

  • 如何理解 springboot 中的 starter?
  • 什么是嵌入式服务器,为什么使用嵌入式服务器?
  • mybatis 的优缺点有哪些?
  • mybatis 和 hibernate 有什么区别?
  • mybatis 中#{}和\${}的区别是什么?
  • 简述一下 mybatis 插件运行原理及开发流程?
  • mysql 聚簇和非聚簇索引的区别是什么?
  • mysql 索引结构有哪些,各自的优劣是什么?
  • 索引的设计原则有哪些?
  • mysql 锁的类型有哪些?
  • mysql 执行计划怎么看?
  • 事务的基本特性是什么?
  • MySQL 的隔离级别有哪些?
  • 怎么处理 MySQL 的慢查询?
  • ACID 是靠什么保证的?
  • 什么是 MVCC?
  • MVCC 解决的问题是什么?
  • MVCC 实现原理是什么?
  • 什么是 mysql 的主从复制?
  • mysql 为什么需要主从同步?
  • mysql 复制原理是什么?
  • 简述 Myisam 和 Innodb 的区别?
  • 简述 mysql 中索引类型有哪些,以及对数据库的性能的影响?
  • 什么是字节码?
  • 字节码的组成结构是什么?
  • class 初始化过程是什么?
  • JVM 内存模型如何分配的?
    • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-o3e4XxCW-1645802647207)(images/javammode.png)]
  • JVM 性能调优的原则有哪些?
  • 什么情况下需要 JVM 调优?
  • 在 JVM 调优时,你关注哪些指标?
  • JVM 常用参数有哪些?
  • JVM 常用性能调优工具有哪些?
  • 线上排查问题的一般流程是怎么样的?
  • **什么情况下,会抛出 OOM 呢?**
  • **系统 OOM 之前都有哪些现象?**
  • 如何进行堆 Dump 文件分析?
  • 如何进行 GC 日志分析?
  • 线上死锁是如何排查的?
  • 微信公众号

如何理解 springboot 中的 starter?

​ 使用 spring+springmvc 框架进行开发的时候,如果需要引入 mybatis 框架,那么需要在 xml 中定义需要的 bean 对象,这个过程很明显是很麻烦的,如果需要引入额外的其他组件,那么也需要进行复杂的配置,因此在 springboot 中引入了 starter

​ starter 就是一个 jar 包,写一个@Configuration 的配置类,将这些 bean 定义在其中,然后再 starter 包的 META-INF/spring.factories 中写入配置类,那么 springboot 程序在启动的时候就会按照约定来加载该配置类

​ 开发人员只需要将相应的 starter 包依赖进应用中,进行相关的属性配置,就可以进行代码开发,而不需要单独进行 bean 对象的配置

什么是嵌入式服务器,为什么使用嵌入式服务器?

​ 在 springboot 框架中,大家应该发现了有一个内嵌的 tomcat,在之前的开发流程中,每次写好代码之后必须要将项目部署到一个额外的 web 服务器中,只有这样才可以运行,这个明显要麻烦很多,而使用 springboot 的时候,你会发现在启动项目的时候可以直接按照 java 应用程序的方式来启动项目,不需要额外的环境支持,也不需要 tomcat 服务器,这是因为在 springboot 框架中内置了 tomcat.jar,来通过 main 方法启动容器,达到一键开发部署的方式,不需要额外的任何其他操作。

mybatis 的优缺点有哪些?

1、Mybait 的优点:

(1)简单易学,容易上手(相比于 Hibernate) 基于 SQL 编程;
(2)JDBC 相比,减少了 50%以上的代码量,消除了 JDBC 大量冗余的代码,不需要手动开关连接;
(3)很好的与各种数据库兼容(因为 MyBatis 使用 JDBC 来连接数据库,所以只要 JDBC 支持的数据库 MyBatis 都支持,而 JDBC 提供了可扩展性,所以只要这个数据库有针对 Java 的 jar 包就可以就可以与 MyBatis 兼容),开发人员不需要考虑数据库的差异性。
(4)提供了很多第三方插件(分页插件 / 逆向工程);
(5)能够与 Spring 很好的集成;
(6)MyBatis 相当灵活,不会对应用程序或者数据库的现有设计强加任何影响,SQL 写在 XML 里,从程序代码中彻底分离,解除 sql 与程序代码的耦合,便于统一管理和优化,并可重用。
(7)提供 XML 标签,支持编写动态 SQL 语句。
(8)提供映射标签,支持对象与数据库的 ORM 字段关系映射。
(9)提供对象关系映射标签,支持对象关系组建维护。
2、MyBatis 框架的缺点:

(1)SQL 语句的编写工作量较大,尤其是字段多、关联表多时,更是如此,对开发人员编写 SQL 语句的功底有一定要求。
(2)SQL 语句依赖于数据库,导致数据库移植性差,不能随意更换数据库。

mybatis 和 hibernate 有什么区别?

Hibernate 的优点:

1、hibernate 是全自动,hibernate 完全可以通过对象关系模型实现对数据库的操作,拥有完整的 JavaBean 对象与数据库的映射结构来自动生成 sql。

2、功能强大,数据库无关性好,O/R 映射能力强,需要写的代码很少,开发速度很快。

3、有更好的二级缓存机制,可以使用第三方缓存。

4、数据库移植性良好。

5、hibernate 拥有完整的日志系统,hibernate 日志系统非常健全,涉及广泛,包括 sql 记录、关系异常、优化警告、缓存提示、脏数据警告等

Hibernate 的缺点:

1、学习门槛高,精通门槛更高,程序员如何设计 O/R 映射,在性能和对象模型之间如何取得平衡,以及怎样用好 Hibernate 方面需要的经验和能力都很强才行

2、hibernate 的 sql 很多都是自动生成的,无法直接维护 sql;虽然有 hql 查询,但功能还是不及 sql 强大,见到报表等变态需求时,hql 查询要虚,也就是说 hql 查询是有局限的;hibernate 虽然也支持原生 sql 查询,但开发模式上却与 orm 不同,需要转换思维,因此使用上有些不方便。总之写 sql 的灵活度上 hibernate 不及 mybatis。

Mybatis 的优点:

1、易于上手和掌握,提供了数据库查询的自动对象绑定功能,而且延续了很好的 SQL 使用经验,对于没有那么高的对象模型要求的项目来说,相当完美。

2、sql 写在 xml 里,便于统一管理和优化, 解除 sql 与程序代码的耦合。

3、提供映射标签,支持对象与数据库的 orm 字段关系映射

4、 提供对象关系映射标签,支持对象关系组建维护

5、提供 xml 标签,支持编写动态 sql。

6、速度相对于 Hibernate 的速度较快

Mybatis 的缺点:

1、关联表多时,字段多的时候,sql 工作量很大。

2、sql 依赖于数据库,导致数据库移植性差。

3、由于 xml 里标签 id 必须唯一,导致 DAO 中方法不支持方法重载。

4、对象关系映射标签和字段映射标签仅仅是对映射关系的描述,具体实现仍然依赖于 sql。

5、DAO 层过于简单,对象组装的工作量较大。

6、不支持级联更新、级联删除。

7、Mybatis 的日志除了基本记录功能外,其它功能薄弱很多。

8、编写动态 sql 时,不方便调试,尤其逻辑复杂时。

9、提供的写动态 sql 的 xml 标签功能简单,编写动态 sql 仍然受限,且可读性低。

mybatis 中#{}和${}的区别是什么?

a、#{}是预编译处理,${}是字符串替换。
b、Mybatis 在处理#{}时,会将 sql 中的#{}替换为?号,调用 PreparedStatement 的 set 方法来赋值;
c、Mybatis 在处理 ${}时,就是把${}替换成变量的值。
d、使用#{}可以有效的防止 SQL 注入,提高系统安全性

简述一下 mybatis 插件运行原理及开发流程?

mybatis 只支持针对 ParameterHandler、ResultSetHandler、StatementHandler、Executor 这四种接口的插件,mybatis 使用 jdk 的动态代理,为需要拦截的接口生成代理对象以实现接口方法拦截功能,每当执行这四种接口对象的方法时,就会进入拦截方法,具体就是 InvocationHandler 的 invoke 方法,拦截那些你指定需要拦截的方法。

编写插件:实现 Mybatis 的 Interceptor 接口并复写 intercept 方法啊,然后给插件编写注解,指定要拦截哪一个接口的哪些方法,在配置文件中配置编写的插件即可。

@Intercepts({@Signature(type = StatementHandler.class,method = "parameterize",args = Statement.class)})

索引的基本原理

1、为什么要有索引?
一般的应用系统,读写比例在 10:1 左右,而且插入操作和一般的更新操作很少出现性能问题,在生产环境中,我们遇到最多的,也是最容易出问题的,还是一些复杂的查询操作,因此对查询语句的优化显然是重中之重。说起加速查询,就不得不提到索引了。
2、什么是索引?
索引在 MySQL 中也叫是一种“键”,是存储引擎用于快速找到记录的一种数据结构。索引对于良好的性能
非常关键,尤其是当表中的数据量越来越大时,索引对于性能的影响愈发重要。
索引优化应该是对查询性能优化最有效的手段了。索引能够轻易将查询性能提高好几个数量级。
索引相当于字典的音序表,如果要查某个字,如果不使用音序表,则需要从几百页中逐页去查。

3、索引的原理

索引的目的在于提高查询效率,与我们查阅图书所用的目录是一个道理:先定位到章,然后定位到该章下的一个小节,然后找到页数。相似的例子还有:查字典,查火车车次,飞机航班等

本质都是:通过不断地缩小想要获取数据的范围来筛选出最终想要的结果,同时把随机的事件变成顺序的事件,也就是说,有了这种索引机制,我们可以总是用同一种查找方式来锁定数据。

数据库也是一样,但显然要复杂的多,因为不仅面临着等值查询,还有范围查询(>、<、between、in)、模糊查询(like)、并集查询(or)等等。数据库应该选择怎么样的方式来应对所有的问题呢?我们回想字典的例子,能不能把数据分成段,然后分段查询呢?最简单的如果 1000 条数据,1 到 100 分成第一段,101 到 200 分成第二段,201 到 300 分成第三段…这样查第 250 条数据,只要找第三段就可以了,一下子去除了 90%的无效数据。但如果是 1 千万的记录呢,分成几段比较好?按照搜索树的模型,其平均复杂度是 lgN,具有不错的查询性能。但这里我们忽略了一个关键的问题,复杂度模型是基于每次相同的操作成本来考虑的。而数据库实现比较复杂,一方面数据是保存在磁盘上的,另外一方面为了提高性能,每次又可以把部分数据读入内存来计算,因为我们知道访问磁盘的成本大概是访问内存的十万倍左右,所以简单的搜索树难以满足复杂的应用场景。

4、索引的数据结构

MySQL 主要用到两种结构:B+ Tree 索引和 Hash 索引
Inodb 存储引擎 默认是 B+Tree 索引
Memory 存储引擎 默认 Hash 索引;
MySQL 中,只有 Memory(Memory 表只存在内存中,断电会消失,适用于临时表)存储引擎显示支持 Hash 索引,是 Memory 表的默认索引类型,尽管 Memory 表也可以使用 B+Tree 索引。Hash 索引把数据以 hash 形式组织起来,因此当查找某一条记录的时候,速度非常快。但是因为 hash 结构,每个键只对应一个值,而且是散列的方式分布。所以它并不支持范围查找和排序等功能。
B+Tree 是 mysql 使用最频繁的一个索引数据结构,是 InnoDB 和 MyISAM 存储引擎模式的索引类型。相对 Hash 索引,B+Tree 在查找单条记录的速度比不上 Hash 索引,但是因为更适合排序等操作,所以它更受欢迎。毕竟不可能只对数据库进行单条记录的操作。
对比:
hash 类型的索引:查询单条快,范围查询慢
btree 类型的索引:b+树,层数越多,数据量指数级增长(我们就用它,因为 innodb 默认支持它)

mysql 聚簇和非聚簇索引的区别是什么?

​ mysql 的索引类型跟存储引擎是相关的,innodb 存储引擎数据文件跟索引文件全部放在 ibd 文件中,而 myisam 的数据文件放在 myd 文件中,索引放在 myi 文件中,其实区分聚簇索引和非聚簇索引非常简单,只要判断数据跟索引是否存储在一起就可以了。

​ innodb 存储引擎在进行数据插入的时候,数据必须要跟索引放在一起,如果有主键就使用主键,没有主键就使用唯一键,没有唯一键就使用 6 字节的 rowid,因此跟数据绑定在一起的就是聚簇索引,而为了避免数据冗余存储,其他的索引的叶子节点中存储的都是聚簇索引的 key 值,因此 innodb 中既有聚簇索引也有非聚簇索引,而 myisam 中只有非聚簇索引。

mysql 索引结构有哪些,各自的优劣是什么?

索引的数据结构和具体存储引擎的实现有关,mysql 中使用较多的索引有 hash 索引,B+树索引,innodb 的索引实现为 B+树,memory 存储引擎为 hash 索引。

B+树是一个平衡的多叉树,从根节点到每个叶子节点的高度差值不超过 1,而且同层级的二节点间有指针相关连接,在 B+树上的常规检索,从根节点到叶子节点的搜索效率基本相当,不会出现大幅波动,而且基于索引的顺序扫描时,也可以利用双向指针快速左右移动,效率非常高。因为,B+树索引被广泛应用于数据库、文件系统等场景。

哈希索引就是采用一定的哈希算法,把键值换算成新的哈希值,检索时不需要类似 B+树那样从根节点到叶子节点逐级查找,只需一次哈希算法即可立刻定位到相应的位置,速度非常快。

如果是等值查询,那么哈希索引明显有绝对优势,因为只需要经过一次算法即可找到相应的键值,前提是键值都是唯一的。如果键值不是唯一的,就需要先找到该键所在位置,然后再根据链表往后扫描,知道找到对应的数据

如果是范围查询检索,这时候哈徐索引就毫无用武之地了,因为原先是有序的键值,经过哈希算法后,有可能变成不连续的了,就没办法再利用索引完成范围查询检索

哈希所有也没办法利用索引完成排序,以及 like 这样的部分模糊查询

哈希索引也不支持多列联合索引的最左匹配规则

B+树索引的关键字检索效率比较平均,不像 B 树那样波动大,在有大量重复键值情况下,哈希索引的效率也是极低的,因此存在哈希碰撞问题。

索引的设计原则有哪些?

​ 在进行索引设计的时候,应该保证索引字段占用的空间越小越好,这只是一个大的方向,还有一些细节点需要注意下:

​ 1、适合索引的列是出现在 where 字句中的列,或者连接子句中指定的列

​ 2、基数较小的表,索引效果差,没必要创建索引

​ 3、在选择索引列的时候,越短越好,可以指定某些列的一部分,没必要用全部字段的值

​ 4、不要给表中的每一个字段都创建索引,并不是索引越多越好

​ 5、定义有外键的数据列一定要创建索引

​ 6、更新频繁的字段不要有索引

​ 7、创建索引的列不要过多,可以创建组合索引,但是组合索引的列的个数不建议太多

​ 8、大文本、大对象不要创建索引

mysql 锁的类型有哪些?

基于锁的属性分类:共享锁、排他锁。

基于锁的粒度分类:行级锁(innodb )、表级锁( innodb 、myisam)、页级锁( innodb 引擎)、记录锁、间隙锁、临键锁。

基于锁的状态分类:意向共享锁、意向排它锁。

共享锁(share lock): 共享锁又称读锁,简称 S 锁;当一个事务为数据加上读锁之后,其他事务只能对该数据加读锁,而不能对数据加写锁,直到所有的读锁释放之后其他事务才能对其进行加持写锁。共享锁的特性主要是为了支持并发的读取数据,读取数据的时候不支持修改,避免出现重复读的问题。

排他锁(exclusive lock):排他锁又称写锁,简称 X 锁;当一个事务为数据加上写锁时,其他请求将不能再为数据加任何锁,直到该锁释放之后,其他事务才能对数据进行加锁。排他锁的目的是在数据修改时候,不允许其他人同时修改,也不允许其他人读取,避免了出现脏数据和脏读的问题。

表锁(table lock):表锁是指上锁的时候锁住的是整个表,当下一个事务访问该表的时候,必须等前一个事务释放了锁才能进行对表进行访问;特点:粒度大,加锁简单,容易冲突;

行锁:行锁是指上锁的时候锁住的是表的某一行或多行记录,其他事务访问同一张表时,只有被锁住的记录不能访问,其他的记录可正常访问,特点:粒度小,加锁比表锁麻烦,不容易冲突,相比表锁支持的并发要高

记录锁(Record lock):记录锁也属于行锁中的一种,只不过记录锁的范围只是表中的某一条记录,记录锁是说事务在加锁后锁住的只是表的某一条记录,加了记录锁之后数据可以避免数据在查询的时候被修改的重复读问题,也避免了在修改的事务未提交前被其他事务读取的脏读问题

页锁:页级锁是 MysQL 中锁定粒度介于行级锁和表级锁中间的一种锁.表级锁速度快,但冲突多,行级冲突少,但速度慢。所以取了折衷的页级,一次锁定相邻的一组记录。特点:开销和加锁时间界于表锁和行锁之间,会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般。

间隙锁:是属于行锁的一种,间隙锁是在事务加锁后其锁住的是表记录的某一个区间,当表的相邻 ID 之间出现空隙则会形成一个区间,遵循左开右闭原则。范围查询并且查询未命中记录,查询条件必须命中索引、间隙锁只会出现在 REPEATABLE_READ(重复读)的事务级别中。

临键锁(Next-Key lock):也属于行锁的一种,并且它是 INNODB 的行锁默认算法,总结来说它就是记录锁和间隙锁的组合,临键锁会把查询出来的记录锁住,同时也会把该范围查询内的所有间隙空间也会锁住,再之它会把相邻的下一个区间也会锁住。

mysql 执行计划怎么看?

​ 在企业的应用场景中,为了知道优化 SQL 语句的执行,需要查看 SQL 语句的具体执行过程,以加快 SQL 语句的执行效率。

​ 可以使用 explain+SQL 语句来模拟优化器执行 SQL 查询语句,从而知道 mysql 是如何处理 sql 语句的。

​ 官网地址: https://dev.mysql.com/doc/refman/5.7/en/explain-output.html

1、执行计划中包含的信息

Column Meaning
id The SELECT identifier
select_type The SELECT type
table The table for the output row
partitions The matching partitions
type The join type
possible_keys The possible indexes to choose
key The index actually chosen
key_len The length of the chosen key
ref The columns compared to the index
rows Estimate of rows to be examined
filtered Percentage of rows filtered by table condition
extra Additional information

id

select 查询的序列号,包含一组数字,表示查询中执行 select 子句或者操作表的顺序

id 号分为三种情况:

​ 1、如果 id 相同,那么执行顺序从上到下

explain select * from emp e join dept d on e.deptno = d.deptno join salgrade sg on e.sal between sg.losal and sg.hisal;

​ 2、如果 id 不同,如果是子查询,id 的序号会递增,id 值越大优先级越高,越先被执行

explain select * from emp e where e.deptno in (select d.deptno from dept d where d.dname = 'SALES');

​ 3、id 相同和不同的,同时存在:相同的可以认为是一组,从上往下顺序执行,在所有组中,id 值越大,优先级越高,越先执行

explain select * from emp e join dept d on e.deptno = d.deptno join salgrade sg on e.sal between sg.losal and sg.hisal where e.deptno in (select d.deptno from dept d where d.dname = 'SALES');

select_type

主要用来分辨查询的类型,是普通查询还是联合查询还是子查询

select_type Value Meaning
SIMPLE Simple SELECT (not using UNION or subqueries)
PRIMARY Outermost SELECT
UNION Second or later SELECT statement in a UNION
DEPENDENT UNION Second or later SELECT statement in a UNION, dependent on outer query
UNION RESULT Result of a UNION.
SUBQUERY First SELECT in subquery
DEPENDENT SUBQUERY First SELECT in subquery, dependent on outer query
DERIVED Derived table
UNCACHEABLE SUBQUERY A subquery for which the result cannot be cached and must be re-evaluated for each row of the outer query
UNCACHEABLE UNION The second or later select in a UNION that belongs to an uncacheable subquery (see UNCACHEABLE SUBQUERY)
--sample:简单的查询,不包含子查询和union
explain select * from emp;--primary:查询中若包含任何复杂的子查询,最外层查询则被标记为Primary
explain select staname,ename supname from (select ename staname,mgr from emp) t join emp on t.mgr=emp.empno ;--union:若第二个select出现在union之后,则被标记为union
explain select * from emp where deptno = 10 union select * from emp where sal >2000;--dependent union:跟union类似,此处的depentent表示union或union all联合而成的结果会受外部表影响
explain select * from emp e where e.empno  in ( select empno from emp where deptno = 10 union select empno from emp where sal >2000)--union result:从union表获取结果的select
explain select * from emp where deptno = 10 union select * from emp where sal >2000;--subquery:在select或者where列表中包含子查询
explain select * from emp where sal > (select avg(sal) from emp) ;--dependent subquery:subquery的子查询要受到外部表查询的影响
explain select * from emp e where e.deptno in (select distinct deptno from dept);--DERIVED: from子句中出现的子查询,也叫做派生类,
explain select staname,ename supname from (select ename staname,mgr from emp) t join emp on t.mgr=emp.empno ;--UNCACHEABLE SUBQUERY:表示使用子查询的结果不能被缓存explain select * from emp where empno = (select empno from emp where deptno=@@sort_buffer_size);--uncacheable union:表示union的查询结果不能被缓存:sql语句未验证

table

对应行正在访问哪一个表,表名或者别名,可能是临时表或者 union 合并结果集
1、如果是具体的表名,则表明从实际的物理表中获取数据,当然也可以是表的别名

​ 2、表名是 derivedN 的形式,表示使用了 id 为 N 的查询产生的衍生表

​ 3、当有 union result 的时候,表名是 union n1,n2 等的形式,n1,n2 表示参与 union 的 id

type

type 显示的是访问类型,访问类型表示我是以何种方式去访问我们的数据,最容易想的是全表扫描,直接暴力的遍历一张表去寻找需要的数据,效率非常低下,访问的类型有很多,效率从最好到最坏依次是:

system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL

一般情况下,得保证查询至少达到 range 级别,最好能达到 ref

--all:全表扫描,一般情况下出现这样的sql语句而且数据量比较大的话那么就需要进行优化。
explain select * from emp;--index:全索引扫描这个比all的效率要好,主要有两种情况,一种是当前的查询时覆盖索引,即我们需要的数据在索引中就可以索取,或者是使用了索引进行排序,这样就避免数据的重排序
explain  select empno from emp;--range:表示利用索引查询的时候限制了范围,在指定范围内进行查询,这样避免了index的全索引扫描,适用的操作符: =, <>, >, >=, <, <=, IS NULL, BETWEEN, LIKE, or IN()
explain select * from emp where empno between 7000 and 7500;--index_subquery:利用索引来关联子查询,不再扫描全表
explain select * from emp where emp.job in (select job from t_job);--unique_subquery:该连接类型类似与index_subquery,使用的是唯一索引explain select * from emp e where e.deptno in (select distinct deptno from dept);--index_merge:在查询过程中需要多个索引组合使用,没有模拟出来
explain select * from rental where rental_date like '2005-05-26 07:12:2%' and inventory_id=3926 and customer_id=321\G--ref_or_null:对于某个字段即需要关联条件,也需要null值的情况下,查询优化器会选择这种访问方式
explain select * from emp e where  e.mgr is null or e.mgr=7369;--ref:使用了非唯一性索引进行数据的查找create index idx_3 on emp(deptno);explain select * from emp e,dept d where e.deptno =d.deptno;--eq_ref :使用唯一性索引进行数据查找
explain select * from emp,emp2 where emp.empno = emp2.empno;--const:这个表至多有一个匹配行,
explain select * from emp where empno = 7369;--system:表只有一行记录(等于系统表),这是const类型的特例,平时不会出现

possible_keys

​ 显示可能应用在这张表中的索引,一个或多个,查询涉及到的字段上若存在索引,则该索引将被列出,但不一定被查询实际使用

explain select * from emp,dept where emp.deptno = dept.deptno and emp.deptno = 10;

key

​ 实际使用的索引,如果为 null,则没有使用索引,查询中若使用了覆盖索引,则该索引和查询的 select 字段重叠。

explain select * from emp,dept where emp.deptno = dept.deptno and emp.deptno = 10;

key_len

表示索引中使用的字节数,可以通过 key_len 计算查询中使用的索引长度,在不损失精度的情况下长度越短越好。

explain select * from emp,dept where emp.deptno = dept.deptno and emp.deptno = 10;

ref

显示索引的哪一列被使用了,如果可能的话,是一个常数

explain select * from emp,dept where emp.deptno = dept.deptno and emp.deptno = 10;

rows

根据表的统计信息及索引使用情况,大致估算出找出所需记录需要读取的行数,此参数很重要,直接反应的 sql 找了多少数据,在完成目的的情况下越少越好

explain select * from emp;

extra

包含额外的信息。

--using filesort:说明mysql无法利用索引进行排序,只能利用排序算法进行排序,会消耗额外的位置
explain select * from emp order by sal;--using temporary:建立临时表来保存中间结果,查询完成之后把临时表删除
explain select ename,count(*) from emp where deptno = 10 group by ename;--using index:这个表示当前的查询时覆盖索引的,直接从索引中读取数据,而不用访问数据表。如果同时出现using where 表名索引被用来执行索引键值的查找,如果没有,表面索引被用来读取数据,而不是真的查找
explain select deptno,count(*) from emp group by deptno limit 10;--using where:使用where进行条件过滤
explain select * from t_user where id = 1;--using join buffer:使用连接缓存,情况没有模拟出来--impossible where:where语句的结果总是false
explain select * from emp where empno = 7469;

事务的基本特性是什么?

事务四大特征:原子性,一致性,隔离性和持久性。

  1. 原子性(Atomicity)
    一个原子事务要么完整执行,要么干脆不执行。这意味着,工作单元中的每项任务都必须正确执行。如果有任一任务执行失败,则整个工作单元或事务就会被终止。即此前对数据所作的任何修改都将被撤销。如果所有任务都被成功执行,事务就会被提交,即对数据所作的修改将会是永久性的。
  2. 一致性(Consistency)
    一致性代表了底层数据存储的完整性。它必须由事务系统和应用开发人员共同来保证。事务系统通过保证事务的原子性,隔离性和持久性来满足这一要求; 应用开发人员则需要保证数据库有适当的约束(主键,引用完整性等),并且工作单元中所实现的业务逻辑不会导致数据的不一致(即,数据预期所表达的现实业务情况不相一致)。例如,在一次转账过程中,从某一账户中扣除的金额必须与另一账户中存入的金额相等。支付宝账号 100 你读到余额要取,有人向你转 100 但是事物没提交(这时候你读到的余额应该是 100,而不是 200) 这种就是一致性
  3. 隔离性(Isolation)
    隔离性意味着事务必须在不干扰其他进程或事务的前提下独立执行。换言之,在事务或工作单元执行完毕之前,其所访问的数据不能受系统其他部分的影响。
  4. 持久性(Durability)
    持久性表示在某个事务的执行过程中,对数据所作的所有改动都必须在事务成功结束前保存至某种物理存储设备。这样可以保证,所作的修改在任何系统瘫痪时不至于丢失。

MySQL 的隔离级别有哪些?

MySQL 定义了四种隔离级别,包括一些具体规则,用于限定事务内外哪些改变是可见的,哪些改变是不可见的。低级别的隔离一般支持更高的并发处理,并且拥有更低的系统开销。
REPEATABLE READ 可重复读
MySQL 数据库默认的隔离级别。该级别解决了 READ UNCOMMITTED 隔离级别导致的问题。它保证同一事务的多个实例在并发读取事务时,会“看到同样的”数据行。不过,这会导致另外一个棘手问题“幻读”。InnoDB 和 Falcon 存储引擎通过多版本并发控制机制解决了幻读问题。
READ COMMITTED 读取提交内容
大多数数据库系统的默认隔离级别(但是不是 MySQL 的默认隔离级别),满足了隔离的早先简单定义:一个事务开始时,只能“看见”已经提交事务所做的改变,一个事务从开始到提交前,所做的任何数据改变都是不可见的,除非已经提交。这种隔离级别也支持所谓的“不可重复读”。这意味着用户运行同一个语句两次,看到的结果是不同的。
READ UNCOMMITTED 读取未提交内容
在这个隔离级别,所有事务都可以“看到”未提交事务的执行结果。在这种级别上,可能会产生很多问题,除非用户真的知道自己在做什么,并有很好的理由选择这样做。本隔离级别很少用于实际应用,因为它的性能也不必其他性能好多少,而别的级别还有其他更多的优点。读取未提交数据,也被称为“脏读”
SERIALIZABLE 可串行化
该级别是最高级别的隔离级。它通过强制事务排序,使之不可能相互冲突,从而解决幻读问题。简而言之,SERIALIZABLE 是在每个读的数据行上加锁。在这个级别,可能导致大量的超时 Timeout 和锁竞争 Lock Contention 现象,实际应用中很少使用到这个级别,但如果用户的应用为了数据的稳定性,需要强制减少并发的话,也可以选择这种隔离级。

  1. 脏读

脏读是指一个事务读取了未提交事务执行过程中的数据。
当一个事务的操作正在多次修改数据,而在事务还未提交的时候,另外一个并发事务来读取了数据,就会导致读取到的数据并非是最终持久化之后的数据,这个数据就是脏读的数据。

  1. 不可重复读

不可重复读是指对于数据库中的某个数据,一个事务执行过程中多次查询返回不同查询结果,这就是在事务执行过程中,数据被其他事务提交修改了。
不可重复读同脏读的区别在于,脏读是一个事务读取了另一未完成的事务执行过程中的数据,而不可重复读是一个事务执行过程中,另一事务提交并修改了当前事务正在读取的数据。

  1. 虚读(幻读)

幻读是事务非独立执行时发生的一种现象,例如事务 T1 批量对一个表中某一列列值为 1 的数据修改为 2 的变更,但是在这时,事务 T2 对这张表插入了一条列值为 1 的数据,并完成提交。此时,如果事务 T1 查看刚刚完成操作的数据,发现还有一条列值为 1 的数据没有进行修改,而这条数据其实是 T2 刚刚提交插入的,这就是幻读。
幻读和不可重复读都是读取了另一条已经提交的事务(这点同脏读不同),所不同的是不可重复读查询的都是同一个数据项,而幻读针对的是一批数据整体(比如数据的个数)。

怎么处理 MySQL 的慢查询?

1、开启慢查询日志,准确定位到哪个 sql 语句出现了问题

2、分析 sql 语句,看看是否 load 了额外的数据,可能是查询了多余的行并且抛弃掉了,可能是加载了许多结果中并不需要的列,对语句进行分析以及重写

3、分析语句的执行计划,然后获得其使用索引的情况,之后修改语句或者修改索引,使得语句可以尽可能的命中索引

4、如果对语句的优化已经无法进行,可以考虑表中的数据量是否太大,如果是的话可以进行横向或者纵向的分表。

ACID 是靠什么保证的?

原子性由 undolog 日志来保证,它记录了需要回滚的日志信息,事务回滚时撤销已经执行成功的 sql

一致性是由其他三大特性保证,程序代码要保证业务上的一致性

隔离性是由 MVCC 来保证

持久性由 redolog 来保证,mysql 修改数据的时候会在 redolog 中记录一份日志数据,就算数据没有保存成功,只要日志保存成功了,数据仍然不会丢失

什么是 MVCC?

1、MVCC

​ MVCC,全称 Multi-Version Concurrency Control,即多版本并发控制。MVCC 是一种并发控制的方法,一般在数据库管理系统中,实现对数据库的并发访问,在编程语言中实现事务内存。

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

2、当前读

​ 像 select lock in share mode(共享锁), select for update ; update, insert ,delete(排他锁)这些操作都是一种当前读,为什么叫当前读?就是它读取的是记录的最新版本,读取时还要保证其他并发事务不能修改当前记录,会对读取的记录进行加锁。

3、快照读(提高数据库的并发查询能力)

​ 像不加锁的 select 操作就是快照读,即不加锁的非阻塞读;快照读的前提是隔离级别不是串行级别,串行级别下的快照读会退化成当前读;之所以出现快照读的情况,是基于提高并发性能的考虑,快照读的实现是基于多版本并发控制,即 MVCC,可以认为 MVCC 是行锁的一个变种,但它在很多情况下,避免了加锁操作,降低了开销;既然是基于多版本,即快照读可能读到的并不一定是数据的最新版本,而有可能是之前的历史版本

4、当前读、快照读、MVCC 关系

​ MVCC 多版本并发控制指的是维持一个数据的多个版本,使得读写操作没有冲突,快照读是 MySQL 为实现 MVCC 的一个非阻塞读功能。MVCC 模块在 MySQL 中的具体实现是由三个隐式字段,undo 日志、read view 三个组件来实现的。

MVCC 解决的问题是什么?

​ 数据库并发场景有三种,分别为:

​ 1、读读:不存在任何问题,也不需要并发控制

​ 2、读写:有线程安全问题,可能会造成事务隔离性问题,可能遇到脏读、幻读、不可重复读

​ 3、写写:有线程安全问题,可能存在更新丢失问题

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

​ 1、在并发读写数据库时,可以做到在读操作时不用阻塞写操作,写操作也不用阻塞读操作,提高了数据库并发读写的性能

​ 2、解决脏读、幻读、不可重复读等事务隔离问题,但是不能解决更新丢失问题

MVCC 实现原理是什么?

​ mvcc 的实现原理主要依赖于记录中的三个隐藏字段,undolog,read view 来实现的。

隐藏字段

​ 每行记录除了我们自定义的字段外,还有数据库隐式定义的 DB_TRX_ID,DB_ROLL_PTR,DB_ROW_ID 等字段

​ DB_TRX_ID

​ 6 字节,最近修改事务 id,记录创建这条记录或者最后一次修改该记录的事务 id

​ DB_ROLL_PTR

​ 7 字节,回滚指针,指向这条记录的上一个版本,用于配合 undolog,指向上一个旧版本

​ DB_ROW_JD

​ 6 字节,隐藏的主键,如果数据表没有主键,那么 innodb 会自动生成一个 6 字节的 row_id

​ 记录如图所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-84M0LUa5-1645802647179)(images/数据案例.png)]

​ 在上图中,DB_ROW_ID 是数据库默认为该行记录生成的唯一隐式主键,DB_TRX_ID 是当前操作该记录的事务 ID,DB_ROLL_PTR 是一个回滚指针,用于配合 undo 日志,指向上一个旧版本

undo log

​ undolog 被称之为回滚日志,表示在进行 insert,delete,update 操作的时候产生的方便回滚的日志

​ 当进行 insert 操作的时候,产生的 undolog 只在事务回滚的时候需要,并且在事务提交之后可以被立刻丢弃

​ 当进行 update 和 delete 操作的时候,产生的 undolog 不仅仅在事务回滚的时候需要,在快照读的时候也需要,所以不能随便删除,只有在快照读或事务回滚不涉及该日志时,对应的日志才会被 purge 线程统一清除(当数据发生更新和删除操作的时候都只是设置一下老记录的 deleted_bit,并不是真正的将过时的记录删除,因为为了节省磁盘空间,innodb 有专门的 purge 线程来清除 deleted_bit 为 true 的记录,如果某个记录的 deleted_id 为 true,并且 DB_TRX_ID 相对于 purge 线程的 read view 可见,那么这条记录一定时可以被清除的)

下面我们来看一下 undolog 生成的记录链

​ 1、假设有一个事务编号为 1 的事务向表中插入一条记录,那么此时行数据的状态为:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WKwzyeWm-1645802647183)(images/1.png)]

​ 2、假设有第二个事务编号为 2 对该记录的 name 做出修改,改为 lisi

​ 在事务 2 修改该行记录数据时,数据库会对该行加排他锁

​ 然后把该行数据拷贝到 undolog 中,作为 旧记录,即在 undolog 中有当前行的拷贝副本

​ 拷贝完毕后,修改该行 name 为 lisi,并且修改隐藏字段的事务 id 为当前事务 2 的 id,回滚指针指向拷贝到 undolog 的副本记录中

​ 事务提交后,释放锁

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PWE2VRhh-1645802647186)(images/2.png)]

​ 3、假设有第三个事务编号为 3 对该记录的 age 做了修改,改为 32

​ 在事务 3 修改该行数据的时,数据库会对该行加排他锁

​ 然后把该行数据拷贝到 undolog 中,作为旧纪录,发现该行记录已经有 undolog 了,那么最新的旧数据作为链表的表头,插在该行记录的 undolog 最前面

​ 修改该行 age 为 32 岁,并且修改隐藏字段的事务 id 为当前事务 3 的 id,回滚指针指向刚刚拷贝的 undolog 的副本记录

​ 事务提交,释放锁

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OYfbpMhn-1645802647188)(images/3.png)]

​ 从上述的一系列图中,大家可以发现,不同事务或者相同事务的对同一记录的修改,会导致该记录的 undolog 生成一条记录版本线性表,即链表,undolog 的链首就是最新的旧记录,链尾就是最早的旧记录。

Read View

​ 上面的流程如果看明白了,那么大家需要再深入理解下 read view 的概念了。

​ Read View 是事务进行快照读操作的时候生产的读视图,在该事务执行快照读的那一刻,会生成一个数据系统当前的快照,记录并维护系统当前活跃事务的 id,事务的 id 值是递增的。

​ 其实 Read View 的最大作用是用来做可见性判断的,也就是说当某个事务在执行快照读的时候,对该记录创建一个 Read View 的视图,把它当作条件去判断当前事务能够看到哪个版本的数据,有可能读取到的是最新的数据,也有可能读取的是当前行记录的 undolog 中某个版本的数据

​ Read View 遵循的可见性算法主要是将要被修改的数据的最新记录中的 DB_TRX_ID(当前事务 id)取出来,与系统当前其他活跃事务的 id 去对比,如果 DB_TRX_ID 跟 Read View 的属性做了比较,不符合可见性,那么就通过 DB_ROLL_PTR 回滚指针去取出 undolog 中的 DB_TRX_ID 做比较,即遍历链表中的 DB_TRX_ID,直到找到满足条件的 DB_TRX_ID,这个 DB_TRX_ID 所在的旧记录就是当前事务能看到的最新老版本数据。

​ Read View 的可见性规则如下所示:

​ 首先要知道 Read View 中的三个全局属性:

​ trx_list:一个数值列表,用来维护 Read View 生成时刻系统正活跃的事务 ID(1,2,3)

​ up_limit_id:记录 trx_list 列表中事务 ID 最小的 ID(1)

​ low_limit_id:Read View 生成时刻系统尚未分配的下一个事务 ID,(4)

​ 具体的比较规则如下:

​ 1、首先比较 DB_TRX_ID < up_limit_id,如果小于,则当前事务能看到 DB_TRX_ID 所在的记录,如果大于等于进入下一个判断

​ 2、接下来判断 DB_TRX_ID >= low_limit_id,如果大于等于则代表 DB_TRX_ID 所在的记录在 Read View 生成后才出现的,那么对于当前事务肯定不可见,如果小于,则进入下一步判断

​ 3、判断 DB_TRX_ID 是否在活跃事务中,如果在,则代表在 Read View 生成时刻,这个事务还是活跃状态,还没有 commit,修改的数据,当前事务也是看不到,如果不在,则说明这个事务在 Read View 生成之前就已经开始 commit,那么修改的结果是能够看见的。

7、MVCC 的整体处理流程

假设有四个事务同时在执行,如下图所示:

事务 1 事务 2 事务 3 事务 4
事务开始 事务开始 事务开始 事务开始
修改且已提交
进行中 快照读 进行中

从上述表格中,我们可以看到,当事务 2 对某行数据执行了快照读,数据库为该行数据生成一个 Read View 视图,可以看到事务 1 和事务 3 还在活跃状态,事务 4 在事务 2 快照读的前一刻提交了更新,所以,在 Read View 中记录了系统当前活跃事务 1,3,维护在一个列表中。同时可以看到 up_limit_id 的值为 1,而 low_limit_id 为 5,如下图所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-T1BWHPhz-1645802647189)(images/image-20210520143604440.png)]

在上述的例子中,只有事务 4 修改过该行记录,并在事务 2 进行快照读前,就提交了事务,所以该行当前数据的 undolog 如下所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fqSZ4FGQ-1645802647190)(images/image-20210520143717928.png)]

​ 当事务 2 在快照读该行记录的是,会拿着该行记录的 DB_TRX_ID 去跟 up_limit_id,lower_limit_id 和活跃事务列表进行比较,判读事务 2 能看到该行记录的版本是哪个。

​ 具体流程如下:先拿该行记录的事务 ID(4)去跟 Read View 中的 up_limit_id 相比较,判断是否小于,通过对比发现不小于,所以不符合条件,继续判断 4 是否大于等于 low_limit_id,通过比较发现也不大于,所以不符合条件,判断事务 4 是否处理 trx_list 列表中,发现不再次列表中,那么符合可见性条件,所以事务 4 修改后提交的最新结果对事务 2 的快照是可见的,因此,事务 2 读取到的最新数据记录是事务 4 所提交的版本,而事务 4 提交的版本也是全局角度的最新版本。如下图所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NWWXGxJ3-1645802647191)(images/image-20210520143742317.png)]

当上述的内容都看明白了的话,那么大家就应该能够搞清楚这几个核心概念之间的关系了,下面我们讲一个不同的隔离级别下的快照读的不同。

8、RC、RR 级别下的 InnoDB 快照读有什么不同

​ 因为 Read View 生成时机的不同,从而造成 RC、RR 级别下快照读的结果的不同

​ 1、在 RR 级别下的某个事务的对某条记录的第一次快照读会创建一个快照即 Read View,将当前系统活跃的其他事务记录起来,此后在调用快照读的时候,还是使用的是同一个 Read View,所以只要当前事务在其他事务提交更新之前使用过快照读,那么之后的快照读使用的都是同一个 Read View,所以对之后的修改不可见

​ 2、在 RR 级别下,快照读生成 Read View 时,Read View 会记录此时所有其他活动和事务的快照,这些事务的修改对于当前事务都是不可见的,而早于 Read View 创建的事务所做的修改均是可见

​ 3、在 RC 级别下,事务中,每次快照读都会新生成一个快照和 Read View,这就是我们在 RC 级别下的事务中可以看到别的事务提交的更新的原因。

总结:在 RC 隔离级别下,是每个快照读都会生成并获取最新的 Read View,而在 RR 隔离级别下,则是同一个事务中的第一个快照读才会创建 Read View,之后的快照读获取的都是同一个 Read View.

什么是 mysql 的主从复制?

​ MySQL 主从复制是指数据可以从一个 MySQL 数据库服务器主节点复制到一个或多个从节点。MySQL 默认采用异步复制方式,这样从节点不用一直访问主服务器来更新自己的数据,数据的更新可以在远程连接上进行,从节点可以复制主数据库中的所有数据库或者特定的数据库,或者特定的表。

mysql 为什么需要主从同步?

1、在业务复杂的系统中,有这么一个情景,有一句 sql 语句需要锁表,导致暂时不能使用读的服务,那么就很影响运行中的业务,使用主从复制,让主库负责写,从库负责读,这样,即使主库出现了锁表的情景,通过读从库也可以保证业务的正常运作。

2、做数据的热备

3、架构的扩展。业务量越来越大,I/O 访问频率过高,单机无法满足,此时做多库的存储,降低磁盘 I/O 访问的频率,提高单个机器的 I/O 性能。

mysql 复制原理是什么?

​ (1)master 服务器将数据的改变记录二进制 binlog 日志,当 master 上的数据发生改变时,则将其改变写入二进制日志中;

​ (2)slave 服务器会在一定时间间隔内对 master 二进制日志进行探测其是否发生改变,如果发生改变,则开始一个 I/OThread 请求 master 二进制事件

​ (3)同时主节点为每个 I/O 线程启动一个 dump 线程,用于向其发送二进制事件,并保存至从节点本地的中继日志中,从节点将启动 SQL 线程从中继日志中读取二进制日志,在本地重放,使得其数据和主节点的保持一致,最后 I/OThread 和 SQLThread 将进入睡眠状态,等待下一次被唤醒。

也就是说:

  • 从库会生成两个线程,一个 I/O 线程,一个 SQL 线程;
  • I/O 线程会去请求主库的 binlog,并将得到的 binlog 写到本地的 relay-log(中继日志)文件中;
  • 主库会生成一个 log dump 线程,用来给从库 I/O 线程传 binlog;
  • SQL 线程,会读取 relay log 文件中的日志,并解析成 sql 语句逐一执行;

注意:

1–master 将操作语句记录到 binlog 日志中,然后授予 slave 远程连接的权限(master 一定要开启 binlog 二进制日志功能;通常为了数据安全考虑,slave 也开启 binlog 功能)。
2–slave 开启两个线程:IO 线程和 SQL 线程。其中:IO 线程负责读取 master 的 binlog 内容到中继日志 relay log 里;SQL 线程负责从 relay log 日志里读出 binlog 内容,并更新到 slave 的数据库里,这样就能保证 slave 数据和 master 数据保持一致了。
3–Mysql 复制至少需要两个 Mysql 的服务,当然 Mysql 服务可以分布在不同的服务器上,也可以在一台服务器上启动多个服务。
4–Mysql 复制最好确保 master 和 slave 服务器上的 Mysql 版本相同(如果不能满足版本一致,那么要保证 master 主节点的版本低于 slave 从节点的版本)
5–master 和 slave 两节点间时间需同步

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DcEkj5YE-1645802647192)(images/主从原理.png)]

具体步骤:

1、从库通过手工执行 change master to 语句连接主库,提供了连接的用户一切条件(user 、password、port、ip),并且让从库知道,二进制日志的起点位置(file 名 position 号); start slave

2、从库的 IO 线程和主库的 dump 线程建立连接。

3、从库根据 change master to 语句提供的 file 名和 position 号,IO 线程向主库发起 binlog 的请求。

4、主库 dump 线程根据从库的请求,将本地 binlog 以 events 的方式发给从库 IO 线程。

5、从库 IO 线程接收 binlog events,并存放到本地 relay-log 中,传送过来的信息,会记录到 master.info 中

6、从库 SQL 线程应用 relay-log,并且把应用过的记录到 relay-log.info 中,默认情况下,已经应用过的 relay 会自动被清理 purge

简述 Myisam 和 Innodb 的区别?

InnoDB 存储引擎: 主要面向 OLTP(Online Transaction Processing,在线事务处理)方面的应用,是第一个完整支持 ACID 事务的存储引擎(BDB 第一个支持事务的存储引擎,已经停止开发)。
特点:

1 支持行锁
2 支持外键
3 支持自动增加列 AUTO_INCREMENT 属性
4 支持事务
5 支持 MVCC 模式的读写
6 读的效率低于 MYISAM 7.写的效率高优于 MYISAM 8.适合频繁修改以及设计到安全性较高的应用 9.清空整个表的时候,Innodb 是一行一行的删除,

MyISAM 存储引擎: 是 MySQL 官方提供的存储引擎,主要面向 OLAP(Online Analytical Processing,在线分析处理)方面的应用。

特点:

1 独立于操作系统,当建立一个 MyISAM 存储引擎的表时,就会在本地磁盘建立三个文件,例如我建立 tb_demo 表,那么会生成以下三个文件 tb_demo.frm,tb_demo.MYD,tb_demo.MYI
2 不支持事务,
3 支持表锁和全文索引
4 MyISAM 存储引擎表由 MYD 和 MYI 组成,MYD 用来存放数据文件,MYI 用来存放索引文件。MySQL 数据库只缓存其索引文件,数据文件的缓存交给操作系统本身来完成;
5 MySQL5.0 版本开始,MyISAM 默认支持 256T 的单表数据; 6.选择密集型的表:MYISAM 存储引擎在筛选大量数据时非常迅速,这是他最突出的优点 7.读的效率优于 InnoDB 8.写的效率低于 InnoDB 9.适合查询以及插入为主的应用 10.清空整个表的时候,MYISAM 则会新建表

简述 mysql 中索引类型有哪些,以及对数据库的性能的影响?

普通索引:允许被索引的数据列包含重复的值

唯一索引:可以保证数据记录的唯一性

主键索引:是一种特殊的唯一索引,在一张表中只能定义一个主键索引,主键用于唯一标识一条记录,使用关键字 primary key 来创建

联合索引:索引可以覆盖多个数据列

全文索引:通过建立倒排索引,可以极大的提升检索效率,解决判断字段是否包含的问题,是目前搜索引擎使用的一种关键技术

索引可以极大地提高数据的查询速度

通过使用索引,可以在查询的过程中,使用优化隐藏器,提高系统的性能

但是会降低插入、删除、更新表的速度,因为在执行这些写操作的时候,还要操作索引文件

索引需要占物理空间,除了数据表占数据空间之外,每一个索引还要占一定的物理空间,如果要简历聚簇索引,那么需要的空间就会更大,如果非聚簇索引很多,一旦聚簇索引改变,那么所有非聚簇索引都会跟着变

什么是字节码?

因为 JVM 针对各种操作系统和平台都进行了定制,无论在什么平台,都可以通过 javac 命令将一个.java 文件编译成固定格式的字节码(.class 文件)供 JVM 使用。之所以被称为字节码,是因为**.class 文件是由十六进制值组成的,JVM 以两个十六进制值为一组,就是以字节为单位进行读取**
格式如下
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GChVivjt-1645802647193)(images/bytecode.png)]

字节码的组成结构是什么?

JVM 对字节码的规范是有要求的,要求每一个字节码文件都要有十部分固定的顺序组成,如下图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XzHmdmhe-1645802647195)(images/bytecode2.png)]

  1. 魔数

所有的.class 文件的前 4 个字节都是魔数,魔数以一个固定值:0xCAFEBABE,放在文件的开头,JVM 就可以根据这个文件的开头来判断这个文件是否可能是一个.class 文件,如果是以这个开头,才会往后执行下面的操作,这个魔数的固定值是 Java 之父 James Gosling 指定的,意为 CafeBabe(咖啡宝贝)

  1. 版本号

版本号是魔术之后的 4 个字节,前两个字节表示次版本号(Minor Version),后两个字节表示主版本号(Major Version),上面的 0000 0032,次版本号 0000 转为十进制是 0,主版本号 0032 转为十进制 50,对应下图的版本映射关系,可以看到对应的 java 版本号是 1.6

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oT9mBpua-1645802647196)(images/bytecodeversion.png)]

  1. 常量池

紧接着主版本号之后的字节为常量池入口,常量池中有两类常量:字面量和符号引用,字面量是代码中申明为 Final 的常量值,符号引用是如类和接口的全局限定名、字段的名称和描述符、方法的名称和描述符。常量池整体分为两个部分:常量池计数器以及常量池数据区
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-72pZQSls-1645802647199)(images/changlangchi.png)]

  1. 访问标志

常量池结束后的两个字节,描述的是类还是接口,以及是否被 Public、Abstract、Final 等修饰符修饰,JVM 规范规定了 9 种访问标示(Access_Flag)JVM 是通过按位或操作来描述所有的访问标示的,比如类的修饰符是 Public Final,则对应的访问修饰符的值为 ACC_PUBLIC | ACC_FINAL,即 0x0001 | 0x0010=0x0011
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-G9iq5yQQ-1645802647201)(images/access_flag.png)]

  1. 当前类索引

访问标志后的两个字节,描述的是当前类的全限定名,这两个字节保存的值是常量池中的索引值,根据索引值就能在常量池中找到这个类的全限定名

  1. 父类索引

当前类名后的两个字节,描述的父类的全限定名,也是保存的常量池中的索引值

  1. 接口索引

父类名称后的两个字节,是接口计数器,描述了该类或者父类实现的接口数量,紧接着的 n 个字节是所有接口名称的字符串常量的索引值

  1. 字段表

用于描述类和接口中声明的变量,包含类级别的变量和实例变量,但是不包含方法内部声明的局部变量,字段表也分为两个部分,第一部分是两个字节,描述字段个数,第二部分是每个字段的详细信息 fields_info
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PDd6egtK-1645802647203)(images/field.png)]

  1. 方法表

字段表结束后为方法表,方法表也分为两个部分,第一个部分是两个字节表述方法的个数,第二部分是每个方法的详细信息
方法的访问信息比较复杂,包括方法的访问标志、方法名、方法的描述符和方法的属性:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-O1fsleKG-1645802647205)(images/method.png)]

  1. 附加属性

字节码的最后一部分,该项存放了在该文件中类或接口所定义属性的基本信息。

class 初始化过程是什么?

首先类加载的机制过程分为 5 个部分:加载、验证、准备、解析、初始化
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CahUYWwb-1645802647206)(images/class-int.png)]

我们现在主要分析类的初始化过程:

  1. 类的初始化阶段,是真正开始执行类中定义的 java 程序代码(字节码)并按程序员的意图去初始化类变量的过程。更直接地说,初始化阶段就是执行类构造器()方法的过程。()方法是由编译器自动收集类中的所有类变量的赋值动作和静态代码块 static{}中的语句合并产生的,其中编译器收集的顺序是由语句在源文件中出现的顺序所决定。
  2. 关于类初始化的顺序**(静态变量、静态初始化块:决于它们在类中出现的先后顺序)>(变量、初始化块:决于它们在类中出现的先后顺序)>构造器**
  3. 关于类初始化的详细过程,参见 Java 虚拟机规范一书中,其中类初始化过程如下:
    1. 每个类都有一个初始化锁 LC,进程获取 LC,这个操作会导致当前线程一直等待,直到获取到 LC 锁
    2. 如果 C 正在被其他线程初始化,当前线程会释放 LC 进去阻塞状态,并等待 C 初始化完成。此时当前线程需要重试这一过程。执行初始化过程时,线程的中断状态不受影响
    3. 如果 C 正在被本线程初始化,即递归初始化,释放 LC 并且正常返回
    4. 如果 C 已经被初始化完成,释放 LC 并且正常返回
    5. 如果 C 处于错误状态,表明不可能再完成初始化,释放 LC 并抛出异常 NoClassDefFoundError 异常
    6. 否则,将 C 标记为正在被本线程初始化,释放 LC;然后,初始化那些 final 且为基础类型的类成员变量
    7. 如果 C 是类而不是接口,且 C 的父类 Super Class(SC)和各个接口 SI_n(按照 implements 子句中的顺序来)还没有初始化,那么就在 SC 上面递归地进行完整的初始化过程,如果有必要,需要先验证和准备 SC ;如果 SC 或 SIn 初始化过程中抛出异常,则获取 LC,将 C 标记为错误状态,并通知所有正在等待的线程,然后释放 LC,然后再抛出同样的异常。
    8. 从 C 的 classloader 处获取 assertion 断言机制是否被打开
    9. 接下来,按照文本顺序执行类变量初始化和静态代码块,或接口的字段初始化,把它们当作是一个个单独的代码块。
    10. 如果执行正常,那就获取 LC,标记 C 对象为已初始化,并通知所有正在等待的线程,然后释放 LC,正常退出整个过程
    11. 否则,如果抛出了异常 E 那么会中断退出。若 E 不是 Error,则以 E 为参数创建新的异常 ExceptionInInitializerError 作为 E。如果因为 OutOfMemoryError 导致无法创建 ExceptionInInitializerError,则将 OutOfMemoryError 作为 E。
    12. 获取 LC,将 C 标记为错误状态,通知所有等待的线程,释放 LC,并抛出异常 E。

可以看到 JLS 确实规定了父类先初始化、static 块和类变量赋值按照文本顺序来

JVM 内存模型如何分配的?

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-o3e4XxCW-1645802647207)(images/javammode.png)]

JVM 性能调优的原则有哪些?

  1. 多数的 Java 应用不需要在服务器上进行 GC 优化,虚拟机内部已有很多优化来保证应用的稳定运行,所以不要为了调优而调优,不当的调优可能适得其反
  2. 在应用上线之前,先考虑将机器的 JVM 参数设置到最优(适合)
  3. 在进行 GC 优化之前,需要确认项目的架构和代码等已经没有优化空间。我们不能指望一个系统架构有缺陷或者代码层次优化没有穷尽的应用,通过 GC 优化令其性能达到一个质的飞跃
  4. GC 优化是一个系统而复杂的工作,没有万能的调优策略可以满足所有的性能指标。GC 优化必须建立在我们深入理解各种垃圾回收器的基础上,才能有事半功倍的效果
  5. 处理吞吐量和延迟问题时,垃圾处理器能使用的内存越大,即 java 堆空间越大垃圾收集效果越好,应用运行也越流畅。这称之为 GC 内存最大化原则
  6. 在这三个属性(吞吐量、延迟、内存)中选择其中两个进行 jvm 调优,称之为 GC 调优 3 选 2

什么情况下需要 JVM 调优?

  • Heap 内存(老年代)持续上涨达到设置的最大内存值
  • Full GC 次数频繁
  • GC 停顿(Stop World)时间过长(超过 1 秒,具体值按应用场景而定)
  • 应用出现 OutOfMemory 等内存异常
  • 应用出现 OutOfDirectMemoryError 等内存异常( failed to allocate 16777216 byte(s) of direct memory (used: 1056964615, max: 1073741824))
  • 应用中有使用本地缓存且占用大量内存空间
  • 系统吞吐量与响应性能不高或下降
  • 应用的 CPU 占用过高不下或内存占用过高不下

在 JVM 调优时,你关注哪些指标?

  1. **吞吐量:**用户代码时间 / (用户代码执行时间 + 垃圾回收时间)。是评价垃圾收集器能力的重要指标之一,是不考虑垃圾收集引起的停顿时间或内存消耗,垃圾收集器能支撑应用程序达到的最高性能指标。吞吐量越高算法越好。
  2. **低延迟:**STW 越短,响应时间越好。评价垃圾收集器能力的重要指标,度量标准是缩短由于垃圾收集引起的停顿时间或完全消除因垃圾收集所引起的停顿,避免应用程序运行时发生抖动。暂停时间越短算法越好
  3. 在设计(或使用)GC 算法时,我们必须确定我们的目标:一个 GC 算法只可能针对两个目标之一(即只专注于最大吞吐量或最小暂停时间),或尝试找到一个二者的折衷
  4. MinorGC 尽可能多的收集垃圾对象。我们把这个称作 MinorGC 原则,遵守这一原则可以降低应用程序 FullGC 的发生频率。FullGC 较耗时,是应用程序无法达到延迟要求或吞吐量的罪魁祸首
  5. 堆大小调整的着手点、分析点:
    1. 统计 Minor GC 持续时间
    2. 统计 Minor GC 的次数
    3. 统计 Full GC 的最长持续时间
    4. 统计最差情况下 Full GC 频率
    5. 统计 GC 持续时间和频率对优化堆的大小是主要着手点
    6. 我们按照业务系统对延迟和吞吐量的需求,在按照这些分析我们可以进行各个区大小的调整
  6. 一般来说吞吐量优先的垃圾回收器:-XX:+UseParallelGC -XX:+UseParallelOldGC,即常规的(PS/PO)
  7. 响应时间优先的垃圾回收器:CMS、G1

JVM 常用参数有哪些?

  1. Xms 是指设定程序启动时占用内存大小。一般来讲,大点,程序会启动的快一点,但是也可能会导致机器暂时间变慢
  2. Xmx 是指设定程序运行期间最大可占用的内存大小。如果程序运行需要占用更多的内存,超出了这个设置值,就会抛出 OutOfMemory 异常
  3. Xss 是指设定每个线程的堆栈大小。这个就要依据你的程序,看一个线程大约需要占用多少内存,可能会有多少线程同时运行等
  4. **-Xmn、-XX:NewSize/-XX:MaxNewSize、-XX:NewRatio **
    1. 高优先级:-XX:NewSize/-XX:MaxNewSize
    2. 中优先级:-Xmn(默认等效 -Xmn=-XX:NewSize=-XX:MaxNewSize=?)
    3. 低优先级:-XX:NewRatio
  5. 如果想在日志中追踪类加载与类卸载的情况,可以使用启动参数 **-XX:TraceClassLoading -XX:TraceClassUnloading **

JVM 常用性能调优工具有哪些?

  1. MAT

    1. 提示可能的内存泄露的点
  2. jvisualvm

  3. jconsole

  4. Arthas

  5. show-busy-java-threads

    1. https://github.com/oldratlee/useful-scripts/blob/master/docs/java.md#-show-busy-java-threads​

线上排查问题的一般流程是怎么样的?

  1. CPU 占用过高排查流程

    1. 利用 top 命令可以查出占 CPU 最高的的进程 pid ,如果 pid 为 9876
    2. 然后查看该进程下占用最高的线程 id【top -Hp 9876】
    3. 假设占用率最高的线程 ID 为 6900,将其转换为 16 进制形式 (因为 java native 线程以 16 进制形式输出) 【printf ‘%x\n’ 6900】
    4. 利用 jstack 打印出 java 线程调用栈信息【jstack 9876 | grep ‘0x1af4’ -A 50 --color】,这样就可以更好定位问题
  2. 内存占用过高排查流程
    1. 查找进程 id: 【top -d 2 -c】
    2. 查看 JVM 堆内存分配情况:jmap -heap pid
    3. 查看占用内存比较多的对象 jmap -histo pid | head -n 100
    4. 查看占用内存比较多的存活对象 jmap -histo:live pid | head -n 100

什么情况下,会抛出 OOM 呢?

  • JVM98%的时间都花费在内存回收
  • 每次回收的内存小于 2%

满足这两个条件将触发 OutOfMemoryException,这将会留给系统一个微小的间隙以做一些 Down 之前的操作,比如手动打印 Heap Dump。并不是内存被耗空的时候才抛出

系统 OOM 之前都有哪些现象?

  • 每次垃圾回收的时间越来越长,由之前的 10ms 延长到 50ms 左右,FullGC 的时间也有之前的 0.5s 延长到 4、5s
  • FullGC 的次数越来越多,最频繁时隔不到 1 分钟就进行一次 FullGC
  • 老年代的内存越来越大并且每次 FullGC 后,老年代只有少量的内存被释放掉

如何进行堆 Dump 文件分析?

可以通过指定启动参数 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/usr/app/data/dump/heapdump.hpro 在发生 OOM 的时候自动导出 Dump 文件

如何进行 GC 日志分析?

为了方便分析 GC 日志信息,可以指定启动参数 【-Xloggc: app-gc.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps】,方便详细地查看 GC 日志信息

  1. 使用 【jinfo pid】查看当前 JVM 堆的相关参数
  2. 继续使用 【jstat -gcutil 2315 1s 10】查看 10s 内当前堆的占用情况
  3. 也可以使用【jmap -heap pid】查看当前 JVM 堆的情况
  4. 我们可以继续使用 【jmap -F -histo pid | head -n 20】,查看前 20 行打印,即查看当前 top20 的大对象,一般从这里可以发现一些异常的大对象,如果没有,那么可以继续排名前 50 的大对象,分析
  5. 最后使用【jmap -F -dump:file=a.bin pid】,如果 dump 文件很大,可以压缩一下【tar -czvf a.tar.gz a.bin】
  6. 再之后,就是对 dump 文件进行分析了,使用 MAT 分析内存泄露
  7. 参考案例: https://www.lagou.com/lgeduarticle/142372.html

线上死锁是如何排查的?

  1. jps 查找一个可能有问题的进程 id
  2. 然后执行 【jstack -F 进程 id
  3. 如果环境允许远程连接 JVM,可以使用 jconsole 或者 jvisualvm,图形化界面检测是否存在死锁

微信公众号

【面试题】Java 高级工程师面试刷题100题(二)相关推荐

  1. Java高级工程师面试题目汇集

    Java高级工程师面试题目汇集(关于Struts,Spring,Hibernate三大框架的面试) 1.Hibernate工作原理及为什么要用? 工作原理: 1.读取并解析配置文件 2.读取并解析映射 ...

  2. 重庆社区计算机考试题库,2020重庆社区工作者考试题库:模拟题100题(64)

    2020年重庆社区工作者考试正在如火如荼的开展,为了帮助大家做好备考工作,社区工作者考试模拟题,希望考生们能与小编共同坚持--每日一练! 2020年社区工作者考试模拟题100题64 1. 在市场经济条 ...

  3. 面试题库 之 数据结构与算法 100题

    1.把二元查找树转变成排序的双向链表 题目: 输入一棵二元查找树,将该二元查找树转换成一个排序的双向链表. 要求不能创建任何新的结点,只调整指针的指向. 10 / \ 6 14 / \ / \ 4 8 ...

  4. JAVA高级工程师-面试经历(含面试问题及解答)

    经过了几个公司的面试,谈谈我这次找工作的面试经历. 工作快五年了,所以给自己定位是找一份Java高级工程师的工作. 由于疫情原因基本都是先电话面试(PS:更多的原因是me在上海,想要找重庆或成都的工作 ...

  5. java核心面试_前100多个核心Java面试问题

    java核心面试 Core Java interview questions help you in preparing for java based interviews. Whether you ...

  6. 阿里java高级工程师面试100题(建议收藏)

    1,java堆,分新生代老年代,新生代有Eden,from surviver,to surviver三个空间,堆被所有线程共.eden内存不足时,发生一次minor GC,会把from survivo ...

  7. 阿里java高级工程师面试100题

    1,java堆,分新生代老年代,新生代有Eden,from surviver,to surviver三个空间,堆被所有线程共.eden内存不足时,发生一次minor GC,会把from survivo ...

  8. 金三银四跳槽季,java面试突击(100题)进大厂就这么简单

    1.谈谈对面向对象思想的理解 首先,谈谈"面向过程"vs"面向对象" 我觉得这两者是思考角度的差异,面向过程更多是以"执行者"的角度来思考问 ...

  9. java面试突击(100题)进大厂就这么简单

    1.谈谈对面向对象思想的理解 首先,谈谈"面向过程"vs"面向对象" 我觉得这两者是思考角度的差异,面向过程更多是以"执行者"的角度来思考问 ...

最新文章

  1. ios查看帧率的软件_程序员必看!直播软件开发弱网下保障高清流畅推流的方法...
  2. 不同视图间的跳转方式
  3. poj 1020 深搜
  4. 【人工智能实战2019-何峥】第1次作业
  5. linux .vimrc教程,vim配置文件~/.vimrc
  6. sso和oauth2.0的简单了解学习
  7. Mysql数据库安全性问题【防注入】
  8. 施一公:“中国式科研”误国误民!表面上一片繁荣,实则深藏危机
  9. ## __VA_ARGS__ ... 宏和可变参数
  10. 大数据2019年的三大趋势你看了吗?
  11. XenDesktop 学习笔记1之DDC
  12. 解决ojdbc7依赖下载失败
  13. 我的世界服务器地图软件制作教程,我的世界RPG地图制作教程 利用MC小助手进行制作...
  14. 信息资源管理 笔记整理
  15. yolo3加载自己训练的模型出现NameError: name 'yolo_head' is not defined
  16. python文件双击闪退_解决python文件双击运行秒退的问题
  17. PHP发生Call to undefined function imagecreate()错误的解决办法
  18. 什么是 ASP.NET?
  19. Python深度学习-快速指南
  20. 自动驾驶路径规划:A*(Astar)算法

热门文章

  1. 用日志构建坚固的数据基础设施/为什么双写不好
  2. 华为OD机试 - 模拟商场优惠打折
  3. 关于0x016f2818这个幻数
  4. camunda7、camunda8对比分析,哪个版本好
  5. java过滤器修改响应,在过滤器中实现修改http请求体和响应体
  6. 微信支付常见错误和统一下单错误码详情
  7. js 色卡 (javascript 色卡 hsv 色卡 hsb 色卡)
  8. 如何从Google迁移到Amazon应用内购买
  9. JS内置对象和数组方法
  10. js内置对象Date