Java面试题(5)mybatis、MySQL

一、mybatis的优缺点

二、Mybatis 与 Hibernate 对比

三、#{} 和${}的区别是什么?

四、简述Mybatis 的插件运行原理,如何编写一个插件

五、数据库-索引的原理

六、mysql聚簇和非聚簇索引的区别

七、mysql索引的数据结构,各自优劣

八、索引涉及的原则?

九、锁的类型有哪些?

十、MySQL执行计划怎么看(mysql优化)

十一、数据库事务的基本特性和隔离级别

十二、关心过业务系统里面的sql耗时吗?统计过慢查询吗?对慢查询都怎么优化过?

十三、MySQL 中ACID是靠什么来保证的?

十四、什么是MVCC?

十五、MySQL主从同步原理

十六、简述MyISAM和InnoDB的区别

十七、简述MySQL中索引类型及对数据库的性能的影响

 

一、mybatis的优缺点

优点:

1、基于sql语句编程,相当灵活,不会对应用程序或数据库的现有设计造成任何影响,SQL写在xml里,解除sql与程序代码的耦合,便于统一管理;提供xml标签,支持编写动态的sql语句,并可重用。

2、与JDBC相比,减少了50%以上的代码量,消除了JDBC大量冗余的代码,不需要手动开关连接。

3、很好的与各种数据库兼容(因为mybatis使用JDBC来连接数据库,所以只要JDBC支持的数据库Mybatis都支持)

4、能够与Spring很好的集成。

5、提供映射标签,支持对象与数据库的ORM字段关系映射;提供对象关系映射标签,支持对象关系组件维护。

缺点:

1、sql 语句的编写工作量较大,尤其当字段多,关联表多时,对开发人员编写sql语句的功底有一定的要求。

2、sql 语句依赖于数据库,导致数据库移植性差,不能随意更换数据库。

 

二、Mybatis 与 Hibernate 对比

Hibernate :面向对象,Mybatis :面向表结构

SQL 和 ORM的争论,永远都不会终止

开发速度的对比:

Hibernate 的真正掌握的话比mybatis要难一些,mybatis框架相对简单很容易上手,但也相对简陋些。

比起两者的开发速度,不仅仅要考虑到两者的特性及性能,更要根据项目需求去考虑究竟哪一个更适合项目开发,比如:一个项目中用到的复杂查询基本没有,就是简单的增删改查,这样选择Hibernate 效率就很快了,因为基本的sql语句已经被封装好了,根本不需要你去写sql语句,这就节省了大量的时间,但是对于一个大型项目,复杂语句较多,这样再去选择Hibernate 就不是一个太好的选择,选择mybatis就会加快许多,而且语句的管理也比较方便。

开发工作量的对比:

Hibernate 和 nybatis都有相应的代码生成工具。可以生成简单基本的DAO层方法。针对高级查询,mybatis需要手动编写sql语句,以及resultMap。而Hibernate 有良好的映射机制,开发者无需关心SQL的生成与结果映射,可以更专注于业务流程。

SQL 优化方面:(mybatis会比较灵活)

Hibernate 的查询会将表中的所有字段查询处理,这一点会有性能消耗。Hibernate 也可以自己写SQL来指定需要查询字段,但这样就破坏了Hibernate 开发的简洁性。而mybatis的SQL是手动编写的,所以可以按需求指定查询的字段。

Hibernate SQL语句的调优需要将SQL打印出来,而Hibernate 的SQL被很多人嫌弃因为太丑了。mybatis的SQL是自己手动写的所以调整方便。但Hibernate 具有自己的日志统计。mybatis本身不带日志统计,使用Log4j进行日志记录。

对象管理对比:(Hibernate 会比较好)

Hibernate 是完整的对象/关系映射解决方案,它提供了对象状态管理(state management)的功能,使开发者不再需要理会底层数据库系统的细节。也就是说,相当于常见的JDBC/SQL 持久层方案中需要管理SQL语句,Hibernate 采用了更自然的面向对象的视角来持久化Java应用中的数据。

换句话说,使用Hibernate 的开发者应该总是关注对象的状态(state),不必考虑SQL语句的执行。这部分细节已经由Hibernate 掌控妥当,只有开发者在进行系统性能调优的时候才需要进行了解。而mybatis在这一块没有文档说明,用户需要对对象自己进行详细的管理。

缓存机制对比:

相同点:都可以实现自己的缓存或使用其它第三方缓存方案,创建适配器来完全覆盖缓存行为。

不同点:Hibernate 的二级缓存配置在SessionFactory生成的配置文件中进行详细配置,然后再在具体的表-对象映射中配置是哪种缓存。

mybatis的二级缓存配置都是在每个具体的表-对象映射中进行详细配置,这样针对不同的表可以自定义不同的缓存机制。并且mybatis可以在命名空间中共享相同的缓存配置和实例,通过Cache-ref来实现。

两者比较:因为Hibernate 对查询对象有着良好的管理机制,用户无需关心SQL。所以在使用二级缓存时如果出现脏数据,系统会报出错误并提示。

而mybatis在这一方面,使用二级缓存时需要特别小心。如果不能完全确定数据更新操作的波及范围,避免Cache的盲目使用。否则,脏数据的出现会给系统的正常运行带来很大的隐患。

Hibernate 功能强大,数据库无关性好,O/R映射能力强,如果你对Hibernate 相当精通,而且对Hibernate 进行了适当的封装,那么你的项目整个持久层代码会相当简单,需要写的代码很少,开发速度很快,非常爽。

Hibernate 的缺点是:学习门槛不低,要精通门槛更高,而且怎么设计O/R映射,在性能和对象模型之间如何权衡取得平衡,以及怎样用好Hibernate 方面需要你的经验和能力都很强才行。

iBatis 入门简单,即学即用,提供了数据库查询的自动对象绑定功能,而且延续了很好的SQL使用经验,对于没有那么高的对象模型要求的项目来说,相当完美。

iBatis 的缺点就是框架还是比较简陋,功能尚且缺失,虽然简化了数据绑定代码,但是整个底层数据库查询实际还是要自己写的,工作量也比较大,而且太不容易适应快速数据库修改。

 

三、#{} 和${}的区别是什么?

#{} 是预编译处理,是占位符,${}是字符串替换,是拼接符。

Mybatis 在处理#{}时,会将sql中的#{}替换为?号,调用PreparedStatement来赋值;

Mybatis 在处理${}时,就会把${}替换成变量的值,调用statement来赋值;

#{} 的变量替换是在DBMS中,变量替换后,#{} 对应的变量自动加上单引号

${} 的变量替换是在DBMS外,变量替换后,${} 对应的变量不会加上单引号

使用#{}可以有效的防止SQL注入,提高系统的安全性。

 

四、简述Mybatis 的插件运行原理,如何编写一个插件

mybatis插件指的就是mybatis的拦截器

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

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

 

五、数据库-索引的原理

索引用来快速地寻找那些具有特定值的记录。如果没有索引,一般来说执行查询时遍历整张表。

索引的原理是:就是把无序的数据变成有序的查询。

1、把创建了索引的列的内容进行排序

2、对排序结果生成倒排序

3、在倒排表内容上拼上数据地址链

4、在查询的时候,先拿到倒排表内容,再取出数据地址链,从而拿到具体数据。

 

六、mysql聚簇和非聚簇索引的区别

都是B+树的数据结构

聚簇索引:将数据存储与索引放到一块,并且是按照一定的顺序组织的,找到索引也就找到了数据,数据的物理存放顺序与索引顺序是一致的,即:只要索引是相邻的,那么对应的数据一定也是相邻地存放在磁盘上的。

非聚簇索引:叶子节点不存储数据、存储的是数据行地址、也就是说根据索引查找到数据行的位置再取磁盘查找数据,这个就有点类似一本书的目录,比如我们要找第三章第一节,那么我们先在这个目录里面找,找到对应的页码后再去对应的页码看文章。

优势:
1、查询通过聚簇索引可以直接获取数据,相比非聚簇索引需要第二次查询(非覆盖索引的情况下)效率更高
2、聚簇索引对于范围查询的效率很高,因为其数据是按照大小排列的
3、聚簇索引适合用在排序的场合,非聚簇索引不合适

劣势:
1、维护索引很昂贵,特别是插入新行或者主键被更新导致要分页的时候,建议在大量插入新行后,选在负载较低的时间段,通过OPTIMIZE TABLE优化表,
因为必须被移动的行数据可能造成碎片。使用独享表空间可以弱化碎片。
2、表因为使用UUID(随机ID)作为主键,使数据存储稀疏,这就会出现聚簇索引有可能有比全表扫描更慢,所以建议使用int的suto_increment作为主键 3、如果主键比较大的话,那辅助索引将会变得更大,因为辅助索引的叶子存储的是主键值;过长的主键值,会导致非叶子节点占用更多的物理空间

InnoDB中一定有主键,主键一定是聚簇索引,不手动设置,则会使用unique索引,没有unique索引,则会使用数据库内部的一个行的隐藏id来当作主键索引。在聚簇索引之上创建的索引称之为辅助索引,辅助索引访问数据总是需要第二次查找,非聚簇索引都是辅助索引,像复合索引,前缀索引,唯一索引,辅助索引叶子节点存储的不再是行的物理位置,而是主键值。

MyISM使用的是非聚簇索引,没有聚簇索引,非聚簇索引的两棵B+树看上去没什么不同,节点的结构完全一致只是存储的内容不同而已,主键索引B+树的节点存储了主键,辅助健索引B+树存储了辅助键。表数据存储在独立的地方,这两颗B+树的叶子节点都使用一个地址指向真正的表数据,对于表数据来说,这两个键没有任何差别。由于索引树是独立的,通过辅助键检索无需访问主键的索引树。

如果涉及到大数据量的排序,全表扫描,count之类的操作的话,还是MyISA占优势些,因为索引所占空间小,这些操作是需要在内存中完成的。

 

七、mysql索引的数据结构,各自优劣

索引的数据结构和具体存储引擎的实现有关,在mysql中使用较多的索引有Hash索引,B+树索引等,InnoDB存储引擎的默认索引实现为:B+树索引。对于哈希索引来说,底层的数据结构就是哈希表,因此在绝大多数需求为单条记录查询的时候,可以选择哈希索引,查询性能最快;其余大部分场景,建议选择B+Tree索引。

B+树:

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

哈希索引:

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

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

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

哈希索引也没办法利用索引完成排序,以及like 'xxx%' 这样的部分模糊查询(这种部分模糊查询,其实本质上也是范围查询);

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

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

 

八、索引涉及的原则?

查询更快,占用空间更小

适合建索引:

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

2、基数较小的类,索引效果较差,没有必要在此列建立索引

3、使用短索引,如果对长字符串列进行索引,应该指定一个前缀长度,这样能够节省大量索引空间,如果搜索词超过索引前缀长度,则使用索引排除不匹配的行,然后检查其余行是否可能匹配。

4、不要过度索引。索引需要额外的磁盘空间,并降低写操作的性能。在修改表内容的时候,索引会进行更新甚至重构,索引列越多,这个时间就会越长。所以只保持需要的索引有利于查询即可。

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

不适合建索引:

6、更新频繁字段不适合列创建索引

7、若是不能有效区分数据的列不适合做索引列(如性别、男女未知、最多也就三种,区分度实在太低)

8、尽量的扩展索引,不要新建索引。比如表中已经有a的索引,现在要加(a,b)的索引,那么只需要修改原来的索引即可。

9、对于那些查询中很少涉及的列,重复值比较多的不要建立索引

10、对于定义为text、image和bit的数据类型的列不要建立索引。

 

九、锁的类型有哪些?

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

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

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

  • 共享锁(Share Lock)
共享锁又称读锁,简称S锁;当一个事务为数据加上读锁之后,其它事务只能对该数据加读锁,而不能对数据加写锁,
直到所有的读锁释放之后其他事务才能对其进行加持写锁。共享锁的特性主要是为了支持并发的读取数据,读取数据的时候不支持修改,
避免出现重复读的问题。
  • 排他锁(exclusive Lock)
排他锁又称写锁,简称X锁;当一个事务为数据加上写锁时,其他请求将不能再为数据加任何锁,直到锁释放之后,其他事务才能对数据进行加锁。
排他锁的目的是在数据修改时候,不允许其它人同时修改,也不允许其他人读取。避免了出现脏数据和脏读的问题。
  • 表锁
表锁是指上锁的时候锁住的是整个表,当下一个事务访问该表的时候,必须等前一个事务释放了锁才能进行对表进行访问;
特点:粒度大,加锁简单,容易冲突;
  • 行锁
行锁是指上锁的时候锁住的是表的某一行或多行记录,其他事务访问同一张表时,只有被锁住的记录不能访问,其他的记录可正常访问;
特点:粒度小,加锁比表锁麻烦,不容易冲突,相比表锁支持的并发要高;
  • 记录锁(Record Lock)
记录锁也属于行锁中的一种,只不过记录锁的范围只是表中的某一条记录,记录锁是说事务在加锁后锁住的只是表的某一条记录。
精准条件命中,并且命中的条件字段是唯一索引
加了记录锁之后数据可以避免数据在查询的时候被修改的重复读问题,也避免了在修改的事务提交前被其他事务读取的脏读问题。
  • 页锁
页级锁是mysql中锁定粒度介于行级锁和表级锁中间的一种锁。表级锁速度快,但冲突多,行级冲突少,但速度慢。
所以取了折衷的页级,一次锁定相邻的一组记录。
特点:开销和加锁时间介于表锁和行锁之间;会出现死锁;锁定粒度介于表锁和行锁之间,并发度一般
  • 间隙锁(Gap Lock)
属于行锁中的一种,间隙锁是在事务加锁后其锁住的是表记录的某一个区间,当表的相邻ID之间出现空隙则会形成一个区间,遵循左开右闭原则。
范围查询并且查询未命中记录,查询条件必须命中索引、间隙锁只会出现在REPEATABLE_READ(重复读)的事务级别中。
触发条件:防止幻读问题,事务并发的时候,如果没有间隙锁,就会发生如下图的问题,在同一个事务里,A事务的两次查询的结果会不一样。
比如表里面的数据ID为1,4,5,7,10,那么会形成以下几个间隙区间,—n-1区间,7-10区间,10-n区间(-n代表负无穷大,n代表正无穷大)
  • 临键锁(next-key lock)
也属于行锁的一种,并且它是INNODB的行锁默认的算法,总结来说它就是记录锁和间隙锁的组合,临键锁会把查询出来的记录锁住,
同时也会把该范围查询内的所有间隙空间也会锁住,再之它会 把相邻的下一个区间也会锁住。 触发条件:范围查询并命中,查询命中了索引。 结合记录锁和间隙锁的特性,临键锁避免了在范围查询时出现脏读、重复读、幻读问题。加了临键锁之后,在范围区间内数据不允许被修改和插入。

 

场景:

如果当事务A加锁成功之后九设置一个状态告诉后面的人,已经有人对表里的行加了一个排他锁了,你们不能对整个表加共享锁或排他锁了,那么后面需要对整个表加锁的人只需要获取这个状态就知道自己是不是可以对表加锁,避免了对整个索引树的每个节点扫描是否加锁,而这个状态就是意向锁。

意向共享锁

当一个事务试图对整个表进行加共享锁之前,首先需要获得这个表的意向共享锁。

意向排他锁

当一个事务试图对整个表进行加排他锁之前,首先需要获得这个表的意向排他锁。

 

十、MySQL执行计划怎么看(mysql优化)

 执行计划就是sql的执行查询的顺序,以及如何使用索引查询,返回的结果集的行数。根据执行计划进行sql优化。

EXPLAN SELECT * FROM A where X=? and Y=?

type:优化sql的重要字段,也是我们判断sql性能和优化程度重要指标。它的取值类型范围:

const:通过索引一次命中,匹配一行数据。

system:表中只有一行记录,相当于系统表。

eq_ref:唯一性索引扫描,对于每个索引键,表中只有一条记录与之匹配。

ref:非唯一性索引扫描,返回匹配某个值的所有

range:只检索给定范围的行,使用一个索引来选择行,一般用于between、<、>;

index:只遍历索引树;

ALL: 表示全表扫描,这个类型的查询是性能最差的查询之一。那么基本就是随着表的数量增多,执行效率越慢。

针对其他参数的解释这里略。。。自行百度

 

十一、数据库事务的基本特性和隔离级别

基本事务特性ACID分别是:

原子性:一个事务中的操作要么全部成功,要么全部失败。

一致性:是数据库总是从一个一致性的状态转换到另外一个一致性的状态。比如A转账给B 100元。假设A只有90,支付之前我们数据库里的数据都是符合约束的,但是如果事务执行成功了,我们的数据库数据就破坏约束了,因此事务不能成功,这里我们说事务提供了一致性的保证。

隔离性:是一个事务的修改在最终提交前,对其他事务是不可见的。

持久性:是一旦事务提交,所做的修改就会 永久保存到数据库中。

隔离性有四个隔离级别,分别是:

(1)read uncommit 读未提交,可能会读到其他事务提交的数据,也叫做脏读。用户本来应该读取到id=1的用户age应该是10,结果读取到了其他事务还没有提交的事务,结果读取结果agq=20,这就是脏读。

(2)read commit 读已提交,两次读取结果不一致,叫做不可重复读。不可重复读解决了脏读的问题,它只会读取已经提交的事务。用户开启事务读取id=1用户,查询到age=10,再次读取发现结果=20,在同一个事务里同一个查询读取到不同的结果叫做不可重复读。

(3)repeatable read 可重复读,这是mysql的默认级别,就是每次读取结果都一样,但是有可能产生幻读。

(4)serializable 串行,一般是不会使用的,它会给每一行读取的数据加锁,会导致大量超时和锁竞争的问题。

 

十二、关心过业务系统里面的sql耗时吗?统计过慢查询吗?对慢查询都怎么优化过?

慢查询的优化首先要搞明白慢的原因是什么?是查询条件没有命中索引?是load了不需要的数据列?还是数据量太大了?

针对慢查询优化的方向:

(1)首先分析语句,看是不是把不需要的字段一并都查出来了,不需要的字段不用查出来得以加快查询速度。

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

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

 

十三、MySQL 中ACID是靠什么来保证的?

MySQL的存储引擎是innodb,有一个undo log日志,MySQL级别有一个binlog日志。

A:原子性由 undo log 日志保证,它记录了需要回滚的日志信息,事务回滚时撤销已经执行成功的sql(反向执行的sql)。

C:一致性由其他三大特性保证,程序代码要保证业务上的一致性(数据一致性)例如通过主键id。

I:隔离性由MVCC(多版本控制)来保证。

D:持久性由内存+redo log来保证,mysql修改数据同时在内存和redo log记录这次操作,宕机的时候可以从redo log恢复。

InnoDB redo log 写盘,InnoDB 事务进入 prepare 状态。
如果前面 prepare 成功,binlog 写盘,再继续将事务日志持久化到bin log,
如果持久化成功,
那么 InnoDB 事务则进入 commit 状态(在redo log 里面写一个 commit 记录)

redolog 的刷盘会在系统空闲时进行。

 

十四、什么是MVCC?

MVCC是MySQL事务隔离级别重要的一个概念。

多版本并发控制:读取数据的时通过一种类似快照的方式将数据保存下来,这样读锁就和写锁不冲突了,不同的事务session会看到自己特定版本的数据。

 

十五、MySQL主从同步原理

 

十六、简述MyISAM和InnoDB的区别

MySQL中使用最多的两种引擎

MyISAM:

  • 不支持事务,但是每次查询都是原子的;
  • 支持表级锁,即每次操作是对整个表加锁;
  • 存储表的总行数;
  • 一个MyAM表有三个文件:索引文件、表结构文件、数据文件;
  • 采用非聚集索引,索引文件的数据或存储指向数据文件的指针。辅助索引与主索引基本一致(主键与索引一样),但是辅索引不用保证唯一性。

InnoDB:

  • 支持ACID的事务,支持事务的四种隔离级别;
  • 支持行级锁及外键约束,因此可以支持写并发写;
  • 不存储总行数(查表总条数的话需要全表扫描);
  • 一个InnoDB引擎存在在一个文件空间(共享表空间,表大小受操作系统控制,一个表可能分布在多个文件里),也可能为多个(设置为独立表空、表大小受操作系统文件大小限制,一般为2G),受操作系统文件大小限制。
  • 主键索引采用聚集索引(索引的数据或存储数据文件本身),辅索引的数据或存储主键的值,因此从辅索引查找数据,需要先通过辅索引找到主键值,再访问辅索引,最好使用自增主键,防止插入数据时,为维持B+树结构,文件的大调整。

十七、简述MySQL中索引类型及对数据库的性能的影响

  • 普通索引:允许被索引的数据列包含重复的值。
  • 唯一索引:可以保证数据记录的唯一性。
  • 主键:是一种特殊的唯一索引,在一张表中只能定义一个主键索引,主键用于唯一标识一条记录,使用关键字PRIMARY KEY来创建。
  • 联合索引:索引可以覆盖多个数据列,如像INDEX(columnA,columnB)索引。
  • 全文索引:通过建立倒排索引,可以极大的提升检索效率,解决判断字段是否包含的问题,是目前搜索引擎使用的一种关键技术。可以通过ALTER TABLE table_name ADD FULLTEXT(colum);创建全文索引。  select * from A where name like '%**%'

索引优点:

  • 索引可以极大的提高数据查询速度
  • 通过使用索引,可以在查询的过程中,使用优化隐藏器,提高系统的性能。

索引缺点:(索引不能滥用)

  • 但是会降低插入、删除、更新表的速度,因为在执行这些写操作时,还要操作索引文件
  • 索引需要占物理空间,除了数据表占数据空间之外,每一个索引还要占一定的物理空间,如果要建立聚簇索引,那么需要的空间就会更大,如果非聚集索引很多,一旦聚集索引改变,那么所有非聚集索引都会跟着变。
posted @ 2021-09-01 10:26  娜梓  阅读(329)  评论(0编辑  收藏  举报