互联网公司面试问题整理

1.git--- git reset 和revert区别

git revert  是生成一个新的提交来撤销某次提交,此次提交之前的commit都会被保留

git reset  是回到某次提交,提交及之前的commit都会被保留,但是此次之后的修改都会被退回到暂存区

2.java线程池里面的线程如何进行分配

CPU 密集型任务:比如像加解密,压缩、计算等一系列需要大量耗费 CPU 资源的任务,大部分场景下都是纯 CPU 计算。

IO 密集型任务:比如像 MySQL 数据库、文件的读写、网络通信等任务,这类任务不会特别消耗 CPU 资源,但是 IO 操作比较耗时,会占用比较多时间。在知道如何判断任务的类别后,让我们分两个场景进行讨论:

 

CPU 密集型任务,对于 CPU 密集型的计算场景,理论上线程的数量 = CPU 核数就是最合适的,不过通常把线程的数量设置为CPU 核数 +1,会实现最优的利用率。

对于 IO 密集型计算场景,最佳的线程数是与程序中 CPU 计算和 IO 操作的耗时比相关的,《Java并发编程实战》的作者 Brain Goetz 推荐的计算方法如下:

线程数 = CPU 核心数 * (1 + IO 耗时/ CPU 耗时)

还有一派的计算方式是《Java虚拟机并发编程》中提出的:

线程数 = CPU 核心数 / (1 - 阻塞系数)

其中计算密集型阻塞系数为 0,IO 密集型阻塞系数接近 1,一般认为在 0.8 ~ 0.9 之间。比如 8 核 CPU,按照公式就是 2 / ( 1 - 0.9 ) = 20 个线程数

3.讲一下 auth2的流程

OAuth2 核心概念
OAuth 2.0 主要有4类角色:

resource owner:资源所有者,指终端的“用户”(user)
resource server:资源服务器,即服务提供商存放受保护资源。访问这些资源,需要获得访问令牌(access token)。
client:客户端,代表向受保护资源进行资源请求的第三方应用程序。
authorization server: 授权服务器, 在验证资源所有者并获得授权成功后,将发放访问令牌给客户端。
 

授权的四种模式

  • 授权码模式(authorization code)
  • 简化模式(implicit)
  • 密码模式(resource owner password credentials)
  • 客户端模式(client credentials)

 

OAuth2 认证流程

微服务统一认证,OAuth2 的认证流程

 

4.项目中怎么进行codeView的

六个不要

不要刻意的去寻找代码bug

不要按照自己的编程风格去评论别人的代码

不要带着抨击和质疑别人能力的心态去进行代码评审

不要在不确定的问题上争来争去

不要听不进别人的意见参与者最好

不要自己都没想明白就提意见

三个原则

发现代码的正确性分享和学习业务逻辑和设计思路高效迅速的完成CodeReviewReview会议流程

1. 主持者分享业务逻辑和设计思路,技术经验。并且对代码功能进行介绍

2. 检查设计的合理性和业务逻辑的正确性

业务流程是否能够走通单一职责,入参是否合理数据库字段设计是否满足需求,是否满足三范式,实体类设计是否合理是否有异常处理机制是否影响后续业务的扩展关注数据结构:Map, HashMap, List, LinkedList是否有影响性能的代码:数据库批量操作,资源是否关闭

3. 检查代码的可读性和可维护性

必要的注释: 类,方法, 复杂代码段命名规范重复代码抽取成方法繁琐代码,能简单实现的地方是否需要优化4. 总结优缺点

设计思想、技术方法、业务知识编程规范、代码风格如何避坑

4. 总结优缺点

设计思想、技术方法、业务知识编程规范、代码风格如何避坑

5.如何保证缓存和数据库在高并发下面的强一致性

https://blog.csdn.net/jinjiniao1/article/details/95494358

 

 

6.RockMQ相关

RocketMQ架构的组成

消息模式--集群模型  广播模式

消费消息是否消费完了之后就删除---commiltLog

保证消息被消费---从生产者 消费者 broker三方面描述

rocketMQ的重试机制

rocketMQ的消息堆积解决方案

 

6.快速失败(fail—fast)

             fail-fast 机制:在用迭代器遍历一个集合对象时,如果遍历过程中对集合对象的内容进行了修改(增加、删除、修改),则会抛出Concurrent Modification Exception。

          原理:迭代器在遍历时直接访问集合中的内容,并且在遍历过程中使用一个 modCount 变量用来统计集合的修改次数。集合在被遍历期间如果内容发生变化,就会改变modCount的值。每当迭代器使用hashNext()/next()遍历下一个元素之前,都会检测modCount变量是否为expectedmodCount值,是的话就返回遍历;否则抛出异常,终止遍历。

      注意:这里异常的抛出条件是检测到 modCount!=expectedmodCount 这个条件。如果集合发生变化时修改modCount值刚好又设置为了expectedmodCount值,则异常不会抛出。因此,不能依赖于这个异常是否抛出而进行并发操作的编程,这个异常只建议用于检测并发修改的bug。

   场景:java.util包下的集合类都是快速失败的,不能在多线程下发生并发修改(迭代过程中被修改)。

解决办法:快速失败的出现是因为并发修改,因此,当需要在并发场景下使用集合时,换成并发包下采用安全失败的集合类即可。

二:安全失败(fail—safe)

      采用安全失败机制的集合容器,在遍历时不是直接在集合内容上访问的,而是先复制原有集合内容,在拷贝的集合上进行遍历。

      原理:由于迭代时是对原集合的拷贝进行遍历,所以在遍历过程中对原集合所作的修改并不能被迭代器检测到,所以不会触发Concurrent Modification Exception。

      缺点:基于拷贝内容的优点是避免了Concurrent Modification Exception,但同样地,迭代器并不能访问到修改后的内容,即:迭代器遍历的是开始遍历那一刻拿到的集合拷贝,在遍历期间原集合发生的修改迭代器是不知道的。

             场景:java.util.concurrent包下的容器都是安全失败,可以在多线程下并发使用,并发修改。

                fail-safe机制有两个问题

      (1)需要复制集合,产生大量的无效对象,开销大

      (2)无法保证读取的数据是目前原始数据结构中的数据。

7.MySQL索引失效的情况

1. where语句中包含or时,可能会导致索引失效

使用or并不是一定会使索引失效,你需要看or左右两边的查询列是否命中相同的索引。

假设USER表中的user_id列有索引,age列没有索引。

下面这条语句其实是命中索引的(据说是新版本的MySQL才可以,如果你使用的是老版本的MySQL,可以使用explain验证下)。

1

select * from `user` where user_id = 1 or user_id = 2;

但是这条语句是无法命中索引的。

1

select * from `user` where user_id = 1 or age = 20;

假设age列也有索引的话,依然是无法命中索引的。

1

select * from `user` where user_id = 1 or age = 20;

因此才有建议说,尽量避免使用or语句,可以根据情况尽量使用union all或者in来代替,这两个语句的执行效率也比or好些。

2. where语句中索引列使用了负向查询,可能会导致索引失效

负向查询包括:NOT、!=、<>、!<、!>、NOT IN、NOT LIKE等。

某“军规”中说,使用负向查询一定会索引失效,笔者查了些文章,有网友对这点进行了反驳并举证。

其实负向查询并不绝对会索引失效,这要看MySQL优化器的判断,全表扫描或者走索引哪个成本低了。

3. 索引字段可以为null,使用is null或is not null时,可能会导致索引失效

其实单个索引字段,使用is null或is not null时,是可以命中索引的,但网友在举证时说两个不同索引字段用or连接时,索引就失效了,笔者认为确实索引失效,但这个锅应该由or来背,属于第一种场景~~

假设USER表中的user_id列有索引且允许null,age列有索引且允许null。

1

select * from `user` where user_id is not null or age is not null;

不过某些“军规”和规范中都有强调,字段要设为not null并提供默认值,是有原因值得参考的。

  • null的列使索引/索引统计/值比较都更加复杂,对MySQL来说更难优化。
  • null 这种类型MySQL内部需要进行特殊处理,增加数据库处理记录的复杂性;同等条件下,表中有较多空字段的时候,数据库的处理性能会降低很多。
  • null值需要更多的存储空,无论是表还是索引中每行中的null的列都需要额外的空间来标识。
  • 对null 的处理时候,只能采用is null或is not null,而不能采用=、in、<、<>、!=、not in这些操作符号。如:where name!='shenjian',如果存在name为null值的记录,查询结果就不会包含name为null值的记录。

4. 在索引列上使用内置函数,一定会导致索引失效

比如下面语句中索引列login_time上使用了函数,会索引失效:

1

select * from `user` where DATE_ADD(login_time, INTERVAL 1 DAY) = 7;

优化建议,尽量在应用程序中进行计算和转换。

其实还有网友提到的两种索引失效场景,应该都归于索引列使用了函数。

4.1 隐式类型转换导致的索引失效

比如下面语句中索引列user_id为varchar类型,不会命中索引:

1

select * from `user` where user_id = 12;

这是因为MySQL做了隐式类型转换,调用函数将user_id做了转换。

1

select * from `user` where CAST(user_id AS signed int) = 12;

4.2 隐式字符编码转换导致的索引失效

当两个表之间做关联查询时,如果两个表中关联的字段字符编码不一致的话,MySQL可能会调用CONVERT函数,将不同的字符编码进行隐式转换从而达到统一。作用到关联的字段时,就会导致索引失效。

比如下面这个语句,其中d.tradeid字符编码为utf8,而l.tradeid的字符编码为utf8mb4。因为utf8mb4是utf8的超集,所以MySQL在做转换时会用CONVERT将utf8转为utf8mb4。简单来看就是CONVERT作用到了d.tradeid上,因此索引失效。

1

select l.operator from tradelog l , trade_detail d where d.tradeid=l.tradeid and d.id=4;

这种情况一般有两种解决方案。

方案1: 将关联字段的字符编码统一。

方案2: 实在无法统一字符编码时,手动将CONVERT函数作用到关联时=的右侧,起到字符编码统一的目的,这里是强制将utf8mb4转为utf8,当然从超集向子集转换是有数据截断风险的。如下:

1

select d.* from tradelog l , trade_detail d where d.tradeid=CONVERT(l.tradeid USING utf8) and l.id=2;

5. 对索引列进行运算,一定会导致索引失效

运算如+,-,*,/等,如下:

1

select * from `user` where age - 1 = 10;

优化的话,要把运算放在值上,或者在应用程序中直接算好,比如:

1

select * from `user` where age = 10 - 1;

6. like通配符可能会导致索引失效

like查询以%开头时,会导致索引失效。解决办法有两种:

将%移到后面,如:

1

select * from `user` where `name` like '李%';

利用覆盖索引来命中索引。

1

select name from `user` where `name` like '%李%';

7. 联合索引中,where中索引列违背最左匹配原则,一定会导致索引失效

当创建一个联合索引的时候,如(k1,k2,k3),相当于创建了(k1)、(k1,k2)和(k1,k2,k3)三个索引,这就是最左匹配原则。

比如下面的语句就不会命中索引:

1

2

3

select * from t where k2=2;

select * from t where k3=3;

slect * from t where k2=2 and k3=3;

下面的语句只会命中索引(k1):

1

slect * from t where k1=1 and k3=3;

8. MySQL优化器的最终选择,不走索引

上面有提到,即使完全符合索引生效的场景,考虑到实际数据量等原因,最终是否使用索引还要看MySQL优化器的判断。当然你也可以在sql语句中写明强制走某个索引。

优化索引的一些建议

  • 禁止在更新十分频繁、区分度不高的属性上建立索引。
    • 更新会变更B+树,更新频繁的字段建立索引会大大降低数据库性能。
    • “性别”这种区分度不大的属性,建立索引是没有什么意义的,不能有效过滤数据,性能与全表扫描类似。
  • 建立组合索引,必须把区分度高的字段放在前面。

 

8.缓存穿透 缓存击穿 缓存雪蹦的区别

1、缓存穿透

       描述:

       缓存穿透是指缓存和数据库中都没有的数据,而用户不断发起请求,如发起为id为“-1”的数据或id为特别大不存在的数据。这时的用户很可能是攻击者,攻击会导致数据库压力过大。

      解决方案:

接口层增加校验,如用户鉴权校验,id做基础校验,id<=0的直接拦截;
从缓存取不到的数据,在数据库中也没有取到,这时也可以将key-value对写为key-null,缓存有效时间可以设置短点,如30秒(设置太长会导致正常情况也没法使用)。这样可以防止攻击用户反复用同一个id暴力攻击
 

2、缓存击穿

      描述:

      缓存击穿是指缓存中没有但数据库中有的数据(一般是缓存时间到期),这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,造成过大压力

      解决方案:

设置热点数据永远不过期。
加互斥锁,互斥锁参考代码如下:
         

 

          说明:

          1)缓存中有数据,直接走上述代码13行后就返回结果了

         2)缓存中没有数据,第1个进入的线程,获取锁并从数据库去取数据,没释放锁之前,其他并行进入的线程会等待100ms,再重新去缓存取数据。这样就防止都去数据库重复取数据,重复往缓存中更新数据情况出现。

          3)当然这是简化处理,理论上如果能根据key值加锁就更好了,就是线程A从数据库取key1的数据并不妨碍线程B取key2的数据,上面代码明显做不到这点。

 

3、缓存雪崩

      描述:

      缓存雪崩是指缓存中数据大批量到过期时间,而查询数据量巨大,引起数据库压力过大甚至down机。和缓存击穿不同的是,        缓存击穿指并发查同一条数据,缓存雪崩是不同数据都过期了,很多数据都查不到从而查数据库。

     解决方案:

缓存数据的过期时间设置随机,防止同一时间大量数据过期现象发生。
如果缓存数据库是分布式部署,将热点数据均匀分布在不同搞得缓存数据库中。
设置热点数据永远不过期。
 

8.CMS垃圾收集器的相关原理及优缺点

一.CMS垃圾回收器的使用与执行过程
想要使用CMS垃圾回收器非常简单,我们只需要在JVM启动参数上加上一条

-XX:+UseConcMarkSweepGC

即可让JVM使用CMS作为老年代的垃圾管理器。

接下来是我们的CMS执行过程,下面给大家放一张图

粉色代表用户线程,黄色代表CMS线程

在上图中我们可以看到在CMS回收器对垃圾回收的时候一种经过了5个步骤(用户线程除外),分别有初始标记,并发标记,重新标记,并发清理和并发重置。那么他们是什么意思又分别做了哪些事情呢?别着急,接下来我就给大家解释一下每个步骤之间所做的事情

1.初始标记(STW):
初始标记其实就是对被我们GC ROOT直接引用的对象做一个标记,而在这个过程中将会触发一次STW机制。
如下图所示:

在这个过程中,虽然会触发STW机制,但是时间会非常的短

2.并发标记
在进行并发标记的过程中,我们的用户线程和CMS线程会一起执行。CMS所做的一件事情就是把堆里的所有引用对象全部找到并做标记。
但是在这个过程中可能会发生对象状态被改变的问题。
比如我的一个对象的引用链已经断开,变成了垃圾对象,但是CMS已经对他做过标记判断为非垃圾对象了怎么办?(多标问题)
又比如本来一个对象在CMS标记的过程中把他标记成了垃圾对象但是后来我们有引用了,结果在我们用的时候垃圾对象已经被干掉了,那我们是不是在引用这个对象的时候就会找不到这个垃圾对象。这时候我们的第三步就产生了。(漏标问题)

3.重新标记(STW)
在这一步,CMS会触发STW机制,并修复并发标记状态已经改变的对象,但是这个过程会比较漫长。他利用三色标记和增量更新来解决我们的漏标问题,之后我会讲讲三色标记和增量更新并通过Hotspot源码来让大家对底层有一个更加深入的了解。

4.并发清理
那这一步就很好理解了,所谓的并发清理其实就是对没有被做标记的对象进行一个清理回收,在这个过程中同样不会产生STW。

5.并发重置
重置本次GC过程中的标记数据

9.针对CMS垃圾收集器浮动垃圾怎么回收

1.回收时间长,吞吐量不如Parallel
由于我们在执行CMS垃圾回收器的过程中有一部分资源让给了用户线程,那就会导致回收时间长,内存空间没有被及时释放掉也就会导致吞吐量不如Paralle

2.无法处理浮动垃圾
还记得刚刚给大家埋了两个坑吗,我们刚刚在重新标记里讲过,三色标记和增量更新可以解决漏标问题。那么多标实际上到最后都变成了浮动垃圾。浮动垃圾只有在执行下一次垃圾回收的时候才会被真正回收掉。

3.会产生大量的空间碎片
为什么这么说呢?因为我们的CMS垃圾回收器的算法采用的是标记清除算法,这个算法的弊端就是会产生空间随便,造成空间不连续。但是没关系CMS提供了两个参数,解决了这个问题

-XX:+UseCMSCompactAtFullCollection

这个参数可以开启我们的内存空间整理,使我们的空间整齐划一。通常情况下我们会配合另一个参数来使用

-XX:CMSFullGCsBeforeCompaction
 

10.Redis哨兵机制、集群模式、主从模式区别

https://www.cnblogs.com/zhonglongbo/p/13128955.html

11.SpringCloud 负载均衡策略(各种策略的实现方式)

https://www.cnblogs.com/xiufengchen/p/10430531.html

12.RocktMq的基本流程

https://www.cnblogs.com/ft-greate/p/12880852.html

13.搜索的底层原理,倒排索引,画图说明传统数据库和倒排索引的区别

14.JVM内存分配实例

https://www.cnblogs.com/zhang8692/p/11703513.html

线上多次GC收集之后统计

Java整个堆大小设置,Xmx 和 Xms设置为老年代存活对象的3-4倍,即FullGC之后的老年代内存占用的3-4倍
永久代 PermSize和MaxPermSize设置为老年代存活对象的1.2-1.5倍。
年轻代Xmn的设置为老年代存活对象的1-1.5倍。
老年代的内存大小设置为老年代存活对象的2-3倍。

 

15.索引下推 索引覆盖,聚集索引 非聚集索引区别

 

索引下推是数据库检索数据过程中为减少回表次数而做的优化。

首先介绍下什么是数据库回表,回表是一种数据库检索过程。通常发生在使用二级索引检索非主索引数据的过程中。举个例子:

 

 

假设有上面一张表(数据库是MYSQL,存储引擎是Innodb),上面的ID字段是主键索引,age是普通索引。

对比下面两条SQL语句:

select id from usertest where age = 10;

select name from usertest where age = 10;

第一条SQL语句不会产生回表:普通索引存储的值是主键的值。也就是说age索引里面存储的结构是下面的情况

age索引

根据age查询id的时候,索引中的值完全可以覆盖查询结果集字段时,不会产生回表操作。

由此也可以看出第二条SQL语句会产生回表是因为查询的结果集无法通过索引中的值直接获取。需要根据age查询到的id值再回到主键索引里面再次查询,这个过程叫做回表。

然后再看索引下推

还是上面的usertest表,只是索引变了,ID字段是仍主键索引,但是我们加上一个复合索引name_age(name,age)。

执行下面一条SQL语句:

select * from usertest where name like 'a%' and age = 10;

在Mysql5.6之前的执行流程是这样的:

1.根据最左前缀原则,执行name like 'a%'可以快速检索出id的值为1,5。

 

2.然后根据id的值进行回表操作,再次进行过滤age=10的数据。

查询id=1回表1次,id=5回表1次,这个过程总共回表了2次。

可能到这里都会有疑问,为什么不在索引里面直接过滤age=10的数据,因为复合索引里面也存了age的数据,这样明明可以减少回表1次。恭喜啦,Mysql5.6以后就这么做了,这就是索引下推。

这样可以看出,索引下推具体是在复合索引的查询中,针对特定的过滤条件而进行减少回表次数而做的优化(个人觉得本来就该这么设计的)。

 

解释一: 就是select的数据列只用从索引中就能够取得,不必从数据表中读取,换句话说查询列要被所使用的索引覆盖。

 

定义:数据行的物理顺序与列值(一般是主键的那一列)的逻辑顺序相同,一个表中只能拥有一个聚集索引。

定义:该索引中索引的逻辑顺序与磁盘上行的物理存储顺序不同,一个表中可以拥有多个非聚集索引。

16 互联网基础知识 

https://blog.csdn.net/liuqiyao_01/category_1651415.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值