牛客面经2 京东社招-002
自我介绍+项目拷打
(略)
项目中遇到的问题以及怎么解决的
(前文整理过,略)
系统用户量不大,某段时间内cpu飙升,怎么排查
- 使用top命令,查看cpu的占用情况,找到占用的pid进程id
- ps -mp + pid 查询这个进程下所有线程cpu占用的比例,找到占用最大的那个线程id-tid
- 使用printf “%x\n” + tid 将线程id转换为十六进制
- 用jstack pid | grep tid (找到pid这个进程下tid这个线程的运行状态)通过日志就能找到具体代码上的拿一行导致的cpu飙升,定位倒问题代码
有一张1亿数据的表,如何进行优化
如果是mysql数据表那就太大了,需要进行分表,根据唯一索引比如用户id进行分表,将每张表数据量控制在1000w以下,需要分128张表,再通过索引提升查询性能,也可以考虑将数据通过binlog的形式写入大数据表
如果是大数据表,那其实数据量不算大,可以通过常用的取数字段进行分区,比如通过时间dt进行分区,先对每个时间段内的数据进行计算,得到一个中间结果,然后对这些中间结果进行聚合分析,得到最终结果
spring的三级缓存你了解吗
spring的三级缓存主要用来存储不同状态的bean对象,用来解决spring的循环依赖问题。比如有一个对象A,A中有一个属性是对象B,对象B中也有一个属性是对象A,那么在初始化A的时候,就要去初始化B,初始化B的时候又去初始化A,但A此时还没有初始化完成,内存中找不到A,有要取创建A对象,这样就陷入了循环
spring种的三级缓存就解决了这个问题,spring
容器中将已创建但还未初始化的中间态对象存储到二级换存中,当对象初始化完成,将完成的对象存储到一级缓存中,三级缓存则存储一些接口类、工厂类和扩展点,在创建B对象的时候就可以使用三级缓存中的引用,找到二级缓存中未初始化完成的A,先完成B的创建,再完成A的初始化,实际上三级缓存的作用就是将还未初始化完成的对象存储并暴露出来使用。
spring的事务事怎么实现的呢
spring的事务主要使用aop的动态代理来实现,我们在方法上添加@Transcational注解来加事务。
在spring启动或者初始化这个bean对象的时候,会给这个对象创建一个动态代理对象,动态代理对象里有事务的拦截器transcationalIncepter,拦截器内部会检查代理对象上有没有Transcational注解,有则事务管理类中调用方法创建新事务。
大概的实现逻辑是使用事务的工具类,将事务的所有资源比如数据库连接都绑定到当前线程上,使当前事务的调用链上的所有请求都能从线程上获取同一个数据库连接,从而保证他们属于同一个事务。
这个时候会根据注解的属性,比如传播机制,取判断是开启一个新的事务还是融合到已有的外部事务。
获取并且开启事务后,通过反射调用原始对象的业务方法。
如果方法正常执行成功,代理对象会调用commit()提交事务;如果有异常,可以根据注解上配置的rollBackFor=Exception.class,针对某种异常进行回滚,或者对所有的exception进行事务的回滚。
spring什么场景事务会失效
spring的事务是通过aop代理对象来实现的,代理对象在调用方法前会先开启事务,方法执行后提交或者回滚事务。那如果调用方法没有通过代理对象,事务就会失效。
具体的场景比如在方法中使用this.save()调用添加了事务的对象,this直接调用的就是实际的对象本身,没有通过aop代理来实现,这时候事务就会失效。
或者不是public的方法,因为aop无法代理非public的方法。
如果直接在事务方法内部catch了异常没有抛出来,也会导致事务的异常回滚不生效。
怎么解决
如果要调用方法,将方法拆到另外一个service上,这样就能实现aop的代理对象;然后就是使用public修饰要加事务的方法;如果try/catch了异常,做完处理后再将异常手动抛出来。
spring事务的传播行为有哪些
spring事务的传播场景就是在一个事务方法嵌套另外一个事务方法时,会根据配置的传播行为判断是去创建一个新的事务还是失效还是使用已有的事务,一般直接在@Transcational注解上配置就可以
一般我们用默认的required,当外部没有事务的时候开启一个新事务,当外部有其他事务时,将事务融合到外部事务中,适用于增删改。
support 外部有事务则融入,无事务则不开启事务,一般用在查操作上,如果单查数据库,就不需要开启事务了。
我用到过的是 required_new,这种一般用作像日志打印这种,不管外部有没有事务,我都希望日志打印出来,就在内部方法的事务上配置上这种传播机制,其实就是内部的独立事务。