Java面试题系列——JavaSE面试题(MySQL一)

1、项目中SQL调优的场景有哪些,数据库调优如何做?

(1)创建索引

a、要尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引

b、在经常需要进行检索的字段上创建索引,比如要按照表字段username进行检索,那么就应该在姓名字段上创建索引,如果经常要按照员工部门和员工岗位级别进行检索,那么就应该在员工部门和员工岗位级别这两个字段上创建索引。

c、创建索引给检索带来的性能提升往往是巨大的,因此在发现检索速度过慢的时候应该首先想到的就是创建索引。

d、一个表的索引数最好不要超过6个,若太多则应考虑一些不常使用到的列上建的索引是否有 必要。索引并不是越多越好,索引固然可以提高相应的 select 的效率,但同时也降低了 insert 及 update 的效率,因为 insert 或 update 时有可能会重建索引,所以怎样建索引需要慎重考虑,视具体情况而定。

(2)避免在索引上使用计算

在where字句中,如果索引列是计算或者函数的一部分,DBMS的优化器将不会使用索引而使用全表查询,函数属于计算的一种,同时在in和exists中通常情况下使用EXISTS,因为in不走索引

效率低:

select * from user where salary*22>11000(salary是索引列)

效率高:

select * from user where salary>11000/22(salary是索引列)

(3)使用预编译查询

程序中通常是根据用户的输入来动态执行SQL,这时应该尽量使用参数化SQL,这样不仅可以避免SQL注入漏洞 攻击,最重要数据库会对这些参数化SQL进行预编译,这样第一次执行的时候DBMS会为这个SQL语句进行查询优化 并且执行预编译,这样以后再执行这个SQL的时候就直接使用预编译的结果,这样可以大大提高执行的速度。

(4)尽量将多条SQL语句压缩到一句SQL中

每次执行SQL的时候都要建立网络连接、进行权限校验、进行SQL语句的查询优化、发送执行结果,这个过程是非常耗时的,因此应该尽量避免过多的执行SQL语句,能够压缩到一句SQL执行的语句就不要用多条来执行。

(5)用where字句替换HAVING字句

避免使用HAVING字句,因为HAVING只会在检索出所有记录之后才对结果集进行过滤,而where则是在聚合前刷选记录,如果能通过where字句限制记录的数目,那就能减少这方面的开销。HAVING中的条件一般用于聚合函数的过滤,除此之外,应该将条件写在where字句中。

(6)使用表的别名

当在SQL语句中连接多个表时,请使用表的别名并把别名前缀于每个列名上。这样就可以减少解析的时间并减少哪些由于列名歧义引起的语法错误。

(7)用union all替换union

当SQL语句需要union两个查询结果集合时,即使检索结果中不会有重复的记录,如果使用union这两个结果集同样会尝试进行合并,然后在输出最终结果前进行排序,因此如果可以判断检索结果中不会有重复的记录时候,应该用union all,这样效率就会因此得到提高。

(8)考虑使用“临时表”暂存中间结果

简化SQL语句的重要方法就是采用临时表暂存中间结果,但是,临时表的好处远远不止这些,将临时结果暂存在临时表,后面的查询就在tempdb中了,这可以避免程序中多次扫描主表,也大大减少了程序执行中“共享锁”阻塞“更新锁”,减少了阻塞,提高了并发性能。但是也得避免频繁创建和删除临时表,以减少系统表资源的消耗。

(9)只在必要的情况下才使用事务begin translation

SQL Server中一句SQL语句默认就是一个事务,在该语句执行完成后也是默认commit的。其实,这就是begin tran的一个最小化的形式,好比在每句语句开头隐含了一个begin tran,结束时隐含了一个commit。

有些情况下,我们需要显式声明begin tran,比如做“插、删、改”操作需要同时修改几个表,要求要么几个表都修改成功,要么都不成功。begin tran 可以起到这样的作用,它可以把若干SQL语句套在一起执行,最后再一起commit。 好处是保证了数据的一致性,但任何事情都不是完美无缺的。Begin tran付出的代价是在提交之前,所有SQL语句锁住的资源都不能释放,直到commit掉。

可见,如果Begin tran套住的SQL语句太多,那数据库的性能就糟糕了。在该大事务提交之前,必然会阻塞别的语句,造成block很多。

Begin tran使用的原则是,在保证数据一致性的前提下,begin tran 套住的SQL语句越少越好!有些情况下可以采用触发器同步数据,不一定要用begin tran。

(10)尽量避免使用游标

尽量避免向客户端返回大数据量,若数据量过大,应该考虑相应需求是否合理。因为游标的效率较差,如果游标操作的数据超过1万行,那么就应该考虑改写。

(11)varchar/nvarchar 代替 char/nchar

尽可能的使用 varchar/nvarchar 代替 char/nchar ,因为首先变长字段存储空间小,可以节省存储空间,其次对于查询来说,在一个相对较小的字段内搜索效率显然要高些。
不要以为 NULL 不需要空间,比如:char(100) 型,在字段建立时,空间就固定了, 不管是否插入值(NULL也包含在内),都是占用 100个字符的空间的,如果是varchar这样的变长字段, null 不占用空间。


(12)查询select语句优化

1.任何地方都不要使用 select * from t ,用具体的字段列表代替“*”,不要返回用不到的任何字段
2.应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描。可以在num上设置默认值0,确保表中num列没有null值。

效率低:

select id from t where num is null  

效率高:

select id from t where num=0

3.不能前置百分

效率低:

select id from t where name like ‘%abc%’

效率高:

select id from t where name like ‘abc%’

4.对于连续的数值,能用 between 就不要用 in 了:

select id from t where num between 1 and 3 

5.当内表是大表时in的效率高,当内表是小表时exists的效率高

例子:表A(小表),表B(大表)

select * from A where exists(select cc from B where cc=A.cc)   效率高,用到了B表上cc列的索引。  
select * from B where cc in (select cc from A)  效率高,用到了B表上cc列的索引;

(13)更新Update语句优化

如果只更改1、2个字段,不要Update全部字段,否则频繁调用会引起明显的性能消耗,同时带来大量日志

(14)删除Delete语句优化语句

最高效的删除重复记录方法 ( 因为使用了ROWID)例子:

DELETE FROM EMP E WHERE E.ROWID > (SELECT MIN(X.ROWID) FROM EMP X WHERE X.EMP_NO = E.EMP_NO);

(15)插入Insert语句优化

在新建临时表时,如果一次性插入数据量很大,那么可以使用 select into 代替 create table,避免造成大量 log ,以提高速度;如果数据量不大,为了缓和系统表的资源,应先create table,然后insert。

2、最多有过几张表的连表处理?

最多的时候做过,四张表的联合处理,当时做的是一个人力资源管理项目,当时有四个表,第一张表是应聘者表,里面记录应聘者的学历,工作年限等信息。一张表是动物表里面记录了应聘者测评的结果,一张是人格表里面记录了应聘者的另一个测评结果,人格表嵌套在动物表内。另一张表是面试官对应聘者的评价表。我用到四表联合的场景是,人力部门在导出应聘者应聘所有信息的时候,我需要把这四张表联合起来组成一个Excel表格,用spring的response会写数据给前端。

3、为什么选择MySQL5.7,而不用5.5   5.6?

(1)安全性

1、用户表 mysql.user 的 plugin字段不允许为空, 默认值是 mysql_native_password,而不是 mysql_old_password,不再支持旧密码格式;
2、增加密码过期机制,过期后需要修改密码,否则可能会被禁用,或者进入沙箱模式;
3、提供了更为简单SSL安全访问配置,并且默认连接就采用SSL的加密方式。

(2)灵活性

1、MySQL数据库从5.7.8版本开始,也提供了对JSON的支持。
2、可以混合存储结构化数据和非结构化数据,同时拥有关系型数据库和非关系型数据库的优点。
能够提供完整的事务支持。
3、generated column是MySQL 5.7引入的新特性,所谓generated column,就是数据库中这一列由其他列计算而得。


(3)易用性

1、在MySQL 5.7 之前,如果用户输入了错误的SQL语句,按下 ctrl+c ,虽然能够”结束”SQL语句的运行,但是,也会退出当前会话,MySQL 5.7对这一违反直觉的地方进行了改进,不再退出会话。
2、MySQL 5.7可以explain一个正在运行的SQL,这对于DBA分析运行时间较长的语句将会非常有用。
3、sys schema是MySQL 5.7.7中引入的一个系统库,包含了一系列视图、函数和存储过程, 该项目专注于MySQL的易用性。例如:如何查看数据库中的冗余索引;如何获取未使用的索引;如何查看使用全表扫描的SQL语句。
 

(4)可用性

1、在线设置 复制的过滤规则 不再需要重启MySQL,只需要停止SQLthread,修改完成以后,启动SQLthread。
2、在线修改buffer pool的大小。
3、Online DDL MySQL 5.7支持重命名索引和修改varchar的大小,这两项操作在之前的版本中,都需要重建索引或表。
4、在线开启GTID ,在之前的版本中,由于不支持在线开启GTID,用户如果希望将低版本的数据库升级到支持GTID的数据库版本,需要先关闭数据库,再以GTID模式启动,所以导致升级起来特别麻烦。
 

(5)性能

1.临时表的性能改进

  • 临时表只在当前会话中可见
  • 临时表的生命周期是当前连接(MySQL宕机或重启,则当前连接结束)

2.只读事务性能改进

  • MySQL 5.7通过 避免为只读事务分配事务ID ,不为只读事务分配回滚段,减少锁竞争等多种方式,优化了只读事务的开销,提高了数据库的整体性能

3.加速连接处理

  • 在MySQL 5.7之前,变量的初始化操作(THD、VIO)都是在连接接收线程里面完成的,现在将这些工作下发给工作线程,以减少连接接收线程的工作量,提高连接的处理速度。这个优化对那些频繁建立短连接的应用,将会非常有用

4.复制性能的改进 (支持多线程复制(Multi-Threaded Slaves, 简称MTS)

  • MySQL的默认配置是库级别的并行复制,为了充分发挥MySQL 5.7的并行复制的功能,我们需要将slave-parallel-type配置成LOGICAL_CLOCK

(6)严格性改变

1、默认启用 STRICT_TRANS_TABLES 模式。

2、对 ONLY_FULL_GROUP_BY 模式实现了更复杂的特性支持,并且也被默认启用。

3、其他被默认启用的sql mode还有 NO_ENGINE_SUBSTITUTION。

(7)默认参数的改变

1.默认binlog格式调整为ROW格式。
2.默认binlog错误后的操作调整为ABORT_SERVER。在先前的选项下(binlog_error_action=IGNORE_ERROR),如果一个错误发生,导致无法写入binlog,mysql-server会在错误日志中记录错误并强制关闭binlog功能。

3.这会使mysql-server在不记录binlog的模式下继续运行,导致从库无法继续获取到主库的binlog。
4.默认开启mysql崩溃时的binlog安全。
5.默认调低slave_net_timeout。
 

4、数据库中Btree 与Hash索引的区别?

(1)B-Tree 索引特征

B-Tree索引可以被用在像=,>,>=,<,<=和BETWEEN这些比较操作符上。而且还可以用于LIKE操作符,只要它的查询条件是一个不以通配符开头的常量。像下面的语句就可以使用索引:

SELECT * FROM tbl_name WHERE key_col LIKE 'Patrick%';
SELECT * FROM tbl_name WHERE key_col LIKE 'Pat%_ck%';

下面这两种情况不会使用索引:

SELECT * FROM tbl_name WHERE key_col LIKE '%Patrick%';
SELECT * FROM tbl_name WHERE key_col LIKE other_col;

(2)Hash 索引特征

1.它们只能用于对等比较,例如=和=>操作符(但是快很多)。它们不能被用于像<这样的范围查询条件。假如系统只需要使用像“键值对”的这样的存储结构,尽量使用hash类型索引。
2.优化器不能用hash索引来为ORDER BY操作符加速。(这类索引不能被用于搜索下一个次序的值)
3.mysql不能判断出两个值之间有多少条数据(这需要使用范围查询操作符来决定使用哪个索引)。假如你将一个MyISAM表转为一个依靠hash索引的MEMORY表,可能会影响一些语句(的性能)。
4.只有完整的键才能被用于搜索一行数据。(假如用B-tree索引,任何一个键的片段都可以用于查找。我觉得可能意味着带通配符LIKE操作符会不起作用)。

5、什么是数据库回表,如何避免回表查询?

官方解释:使用普通索引的情况下,并且包含了非索引字段的时候,会通过该普通索引获取到叶子节点的主键信息,拿到主键信息后再去聚合索引中找到对应的行信息,这个过程就叫做回表查询。

例子:

在了解例子前我们要先了解,InnoDB有两大类索引:1、聚集索引(clustered index)。2、普通索引(secondary index)。回表查询就发生在普通索引中。

设有表:t(id PK, name KEY, sex, flag);id是聚集索引,name是普通索引。

表中有四条记录:

1, shenjian, m, A

3, zhangsan, m, A

5, lisi, m, A

9, wangwu, f, B

Java面试题系列——JavaSE面试题(MySQL一)_第1张图片

 两个B+树索引分别如上图:

(1)id为PK,聚集索引,叶子节点存储行记录;

(2)name为KEY,普通索引,叶子节点存储PK值,即id;

如果你使用聚集索引mysql会使用左边进行查询,一次变找到了对应的信息。

但是如果你使用了普通索引情况就会稍复杂一些,mysql会先从右表中找到对应的聚集索引,再通过左表进行索引找到对应的信息。具体操作如下图所示:

Java面试题系列——JavaSE面试题(MySQL一)_第2张图片

6、InnoDB的行级锁什么情况下会使用?

  MySQL各存储引擎使用了三种类型(级别)的锁定机制:表级锁定,行级锁定和页级锁定。

  1.表级锁定(table-level)
      表级别的锁定是MySQL各存储引擎中最大颗粒度的锁定机制。该锁定机制最大的特点是实现逻辑非常简单,带来的系统负面影响最小。所以获取锁和释放锁的速度很快。由于表级锁一次会将整个表锁定,所以可以很好的避免困扰我们的死锁问题。当然,锁定颗粒度大所带来最大的负面影响就是出现锁定资源争用的概率也会最高,致使并大度大打折扣。 使用表级锁定的主要是MyISAM,MEMORY,CSV等一些非事务性存储引擎
  
2.行级锁定(row-level)
  行级锁定最大的特点就是锁定对象的颗粒度很小,也是目前各大数据库管理软件所实现的锁定颗粒度最小的。由于锁定颗粒度很小,所以发生锁定资源争用的概率也最小,能够给予应用程序尽可能大的并发处理能力而提高一些需要高并发应用系统的整体性能。
  虽然能够在并发处理能力上面有较大的优势,但是行级锁定也因此带来了不少弊端。由于锁定资源的颗粒度很小,所以每次获取锁和释放锁需要做的事情也更多,带来的消耗自然也就更大了。此外,行级锁定也最容易发生死锁。使用行级锁定的主要是InnoDB存储引擎。
  3.页级锁定(page-level)
  页级锁定是MySQL中比较独特的一种锁定级别,在其他数据库管理软件中也并不是太常见。页级锁定的特点是锁定颗粒度介于行级锁定与表级锁之间,所以获取锁定所需要的资源开销,以及所能提供的并发处理能力也同样是介于上面二者之间。另外,页级锁定和行级锁定一样,会发生死锁。

只有通过索引条件检索数据,InnoDB才使用行级锁,否则,InnoDB将使用表锁!

持续更新中,敬请期待!

参考文章:

数据库SQL调优的几种方式是什么-木庄网络博客

数据库SQL调优的几种方式_香瓜西蕉的博客-CSDN博客_sql调优

MySQL5.5、5.6、5.7、8.0之间的区别与特点_白雪滑落树梢的博客-CSDN博客_mysql56和57区别

Mysql中的Btree与Hash索引比较_慕课手记

如何避免回表查询?什么是索引覆盖? - danielzzz - 博客园

【mysql锁——innodb的行级锁】_阿波罗登月计划的博客-CSDN博客_innodb行级锁

你可能感兴趣的:(java)