java常见面试题——适用于2-5年后端开发面试(一)

1、什么是索引回表,如何避免?

关于这个问题,这里以 MySQL InnoDB 来说,先简单介绍下 MySQL InnoDB 的 B+ 树索引。

B+ 树索引主要可以分为两种索引,聚集索引和非聚集索引。

聚集索引:也就是平常我们说的主键索引,在 B+ 树中叶子节点存的是整行数据。

非聚集索引:也叫二级索引

那我们应该如何避免回表,因为如果用到了回表,就需要二次查询的过程,效率肯定更慢,很简单,回表是因为要查询的字段在非聚集索引里没有,所以在满足需求的情况下,我们尽量使非聚集索引里有要查询的索引字段

所以在查询时,可以尽量用聚集索引来查(也就是用主键来查询),或者根据业务需求,建好的索引,满足索引查询字段。但是实际业务中,很难建立一个索引就能满足所有查询要求,所以,正常情况,回表也没事,只要能用到索引也能大大加快查询速度。


2、IP协议的首部结构

首部协议一共是20个字节(固定)

第一个4字节: 版本号;首部长度; 服务类型;总长度;

第二个4字节:标识;标志;片偏移;

第三个4字节:生存时间;协议;校验和;

第四个4字节:源ip地址;

第五个4字节:目的ip地址;


3、为什么要线程同步?并说一说线程同步的方式

在多线程并发场景下指令执行的先后顺序由内核决定,同一个线程内部指令按照先后顺序执行,但不同线程之间的指令执行先后顺序是不一定的。

如果执行结果依赖于不同线程执行的先后顺序,那么就会形成“竞争条件”,由于竞争条件下计算结果是非预期的,因此我们应该尽量避免竞争条件的形成。

最常解决竞争条件的方式是原子操作,其次便是线程同步。

线程同步主要包括四种方式:

1、互斥锁

互斥锁(又名互斥量)强调的是资源之间的访问互斥:每个线程在对共享资源操作前都会尝试先加锁,加锁成功才能操作,操作结束之后解锁。

2、读写锁

读写锁和互斥量类似,是另一种实现线程同步的方式,但是它将操作分为读、写两种方式,可以多个线程同时占用读模式,这样使得读写锁具有更高的并行性。相较于互斥锁而言读写锁有一定的性能提升,应对的是单写多读模型:

  • 写独占:写锁占用时,其他线程加读锁或者写锁时都会阻塞(并非失败)
  • 读共享:读锁占用时,其他线程加写锁时会阻塞,加读锁会成功

读写锁有两种策略:

  • 强读同步:读锁优先,只要写锁没有占用那么就可以加读锁
  • 强写同步:写锁优先,只能等到所有正在等待或者执行的写锁执行完成后才能加读锁

大部分读写锁的实现都采用的是“强写同步”策略,对尝试加锁的操作进行排队,如果前面已经有尝试加写被锁阻塞住的话,后续加读锁也都会被阻塞住(尽管当前时刻是读锁占用的状态)。这样做的目的主要是为了避免“写饥饿”,在“多读少写”的情况下防止数据修改延迟过高。

当然,具体采取哪种策略取决于业务。例如航班订票系统使用强写同步策略,图书馆查阅系统使用强读同步策略。非常适用于对数据结构读的次数远大于写的情况,因为读锁是共享的,这样可以提高并行性。

3、条件变量

并发有互斥和等待两大需求,前者是因为线程间存在共享数据依赖而后者是线程间存在依赖,条件变量正是为了解决等待需求。

条件变量本质上也是一个多线程间共享的全局变量,它的功能是阻塞线程,被阻塞的线程直到接收到“条件成立”的信号后才能继续执行。

需要注意的是:

  • 条件变量并不是锁(但它几乎总是和互斥量一起使用的),而是线程间的一种通讯机制
  • 条件变量本身也不包含条件,它被称为条件变量是因为它经常和条件语句(if/while)一起使用

4、信号量

信号量分为有名信号量和无名信号量,无名信号量用于线程同步,有名信号量一般用于进程之间管理。

信号量本质上是一个非负的整数计数器,用于控制公共资源的访问,也被称为PV原子操作:

  • P操作:即信号量sem减一,若sem小于等于0则P操作被阻塞,直到sem变量大于0为止
  • V操作:即信号量sem加一

信号量允许多个线程同时进入临界区,而互斥量只允许一个线程进入临界区。


4、索引设计原则和注意事项

一、索引设计原则

1、选择唯一性索引

    唯一性索引的值是唯一的,可以更快速的通过该索引来确定某条记录
    如学生的学号是唯一的,而如果使用姓名,可能存在同名现象,从而降低查询速度。

2、为常作为查询条件的字段建立索引(即where后面常用字段)

    如果某个字段经常用来做查询条件,那么该字段的查询速度会影响整个表的查询速度。因此,为这样的字段建⽴立索引,可以提⾼高整个表的查询速度

3、为经常需要排序、分组和联合操作的字段建立索引

    经常需要order by、group by、distinct和union等操作的字段,排序操作会浪费很多时间。如果为其建立索引,可以有效的避免排序操作

4、小表不建议索引(超过200w数据的表,建立索引)

    包含大量的列并且不需要搜索非空值的时候可以考虑不建索引

5、尽量使用数据量少的索引

    如果索引的值很长,那么查询的速度会受到影响
    例如:对一个char(100)类型的字段进行全文检索需要的时间肯定比对char(10)类型的字段需要的时间更多

6、限制索引的数目

    每个索引都需要占⽤用磁盘空间,索引越多,需要的磁盘空间就越大
    修改表时,对索引的重构和更新很麻烦
    越多的索引,会使更新表变得很浪费时间

7、经常被用来过滤记录的字段

    primary key 字段,系统自动创建主键的索引
    unique key 字段,系统自动创建对应的索引
    foreign key 约束所定义的作为外键的字段
    在查询中用来连接表的字段

8、索引的创建必须考虑数据的操作方式

    内容很少变动,经常被查询,为它多创建几个索引无所谓
    经常性,例行性变动的表而言,则需要谨慎地创建确实必要的索引

9、尽量使用前缀索引

    如果索引字段的值很长,最好使用值的前缀来索引
    例如:text和blog类型的字段,进行全文检索会浪费时间。如果只检索字段的前面的若干个字符,这样可以提高检索速度

10、删除不再使用或者很少使用的索引

    表中数据被大量更新,或者数据的使用方式被改变后,原有的一些索引可能不再需要。数据库管理理员应当定期找出这些索引,将他们删除,从而减少索引对更新操作的影响

二、合理使用索引注意事项

    索引是针对数据库表中的某些“列”,因此在创建索引时,需要仔细考虑在哪些列上创建索引,在哪些列上不能创建索引。一般来说,在如下列上可以考虑创建索引:

1、“最左前缀匹配原则”,复合索引(联合索引、多列索引),mysql会从做从左向右匹配直到遇到范围查询(>、<、between、like)就“停止匹配”。比如a = 1 and b = 2 and c > 3 and d = 4 ,如果建立(a,b,c,d)顺序的复合索引,d是用不到索引的;如果建立(a,b,d,c)顺序的复合索引则都可以用到,a,b,d的顺序可以任意调整。

2、在经常需要搜索的列上,可以加快搜索的速度。

3、在作为主键的列上,强制该列的唯一性和组织表中数据的排列结构。

4、在经常用在连接的列上,这些列主要是一些外键,可以加快连表的速度。

5、在经常需要根据范围进行搜索的列上,因为索引已经排序,其指定的范围是连续的。

6、在经常需要排序的列上创建索引,因为索引已经排序,可以利用索引排序加快查询。

7、经常使用在where子句中的列上,加快条件的判断速度。

三、不合理使用索引注意事项

1、对于那些在查询中很少使用或者参考的列不应该创建索引。

2、对于那些取值很少的列,如性别等等。

3、对于那些定义为text、image和bit数据类型的列不应该增加索引。


5、B树和B+树有什么区别?为什么使用B+树而不用B树?

1、B树的节点既存储key也存储数据,B+树key只存储key,数据是存在叶子节点上的

2、B树的叶子节点之间是没有关系的,而B+树的叶子节点得相互之间是有一个引用链路的

3、当我们去查找数据的时候查找方式不一样,查找的时候B树可能还不需要查找叶子节点就把数据找到了,而B+树需要查找到叶子节点才能找到数据


6、Explain的作用?

使用Explain关键字可以模拟优化器执行SQL语句从而知道MySQL是 如何处理你的SQL语句的。分析你的查询语句或是结构的性能瓶颈 

不会看 Explain执行计划,劝你简历别写熟悉 SQL优化_程序员小富的博客-CSDN博客

Explain详解_AnEra的博客-CSDN博客_explain详解


7、为什么要分库分表?

分库分表的面试题1 - 百度文库

分库分表的面试题3 - 百度文库

分库分表面试题及答案_JAVA6b的博客-CSDN博客_分库分表面试题


8、处理高并发的六种方法

(1)    系统拆分,将一个系统拆分为多个子系统,用dubbo来搞。然后每个系统连一个数据库,这样本来就一个库,现在多个数据库,这样就可以抗高并发;

(2)    缓存,大部分的高并发场景,都是读多写少,读的时候走缓存,redis轻轻松松单机几万的并发;

(3)    MQ(消息队列),将请求灌入mq中,控制在mysql承载范围之内,排队后面系统慢慢写,mq单机抗几万并也是可以的;

(4)    分库分表,一个库拆分为多个库,多个库来抗更高的并发;一个表拆分为多个表,减少每个表的数据量,提高sql跑的性能;

(5)    读写分离,可以搞个主从架构读写分离,主库写入,从库读取。流量太多的时候,还可以加更多的从库。

(6)    Solrcloud,是solr提供的分布式搜索方案,可以解决海量数据的分布式全文检索,因为搭建了集群,因此具备高可用的特性,同时对数据进行主从备份,避免了单点故障问题。可以做到数据的快速恢复。并且可以动态的添加新的节点,在对数据进行平衡,可以做到负载均衡;


9、wait()与sleep()的区别?

1、wait()方法属于Object类,sleep()属于Thread类;

2、wait()方法释放cpu给其他线程,自己让出资源进入等待池等待;sleep占用cpu,不让出资源;

3、sleep()必须指定时间,wait()可以指定时间也可以不指定;sleep()时间到,线程处于临时阻塞或运行状态;

4、wait()方法会释放持有的锁,不然其他线程不能进入同步方法或同步块,从而不能调用notify(),notifyAll()方法来唤醒线程,产生死锁,所以释放锁,可以执行其他线程,也可以唤醒自己,只是设置停止自己的时间时不确定的;sleep方法不会释放持有的锁,设置sleep的时间是确定的会按时执行的;

5、wait()方法只能在同步方法或同步代码块中调用,否则会报illegalMonitorStateException异常,如果没有设定时间,使用notify()来唤醒;而sleep()能在任何地方调用;


10、如何创建线程池

创建线程池的2种方式

1、手动创建线程池

new ThreadPoolExecutor();

2、Executors工具类创建线程池

  • 3
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

91科技

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值