后端大厂面试总结大全六

1、@Transactional注解控制事务有哪些不生效的场景

  • 数据库引擎不支持事务
  • 数据源没有配置事务管理器
  • 没有在spring配置文件中开启事务管理器
  • 事务注解被覆盖导致事务失效
  • 配置错误的 @Transactional 注解
  • 事务超时时间设置过短
  • 使用了错误的事务传播机制(Propagation.NOT_SUPPORTED传播特性不支持事务)
  • rollbackFor属性配置错误(其实rollbackFor属性指定的异常必须是Throwable或者其子类。默认情况下,RuntimeException和Error两种异常都是会自动回滚的。但是因为以上的代码例子,指定了rollbackFor = Error.class,但是抛出的异常又是Exception,而Exception和Error没有任何什么继承关系,因此事务就不生效)
  • service类没有被spring管理
  • 方法的访问权限不是public的
  • 事务的方法被final、static关键字修饰了
  • 同一个类中方法调用,导致@Transactional失效
  • 异常被捕获了并处理了,没有重新抛出(事务中的异常已经被业务代码捕获并处理,而没有被正确地传播回事务管理器,事务将无法回滚。)
  • 手动抛了别的异常(失效的原因 :上面的代码例子中,手动抛了Exception异常,但是是不会回滚的,因为Spring默认只处理RuntimeException和Error,对于普通的Exception不会回滚,除非,用rollbackFor属性指定配置。
    解决方案:添加属性配置@Transactional(rollbackFor = Exception.class))
  • 事务多线程的调用(这是因为Spring事务是基于线程绑定的,每个线程都有自己的事务上下文 ,而多线程环境下可能会存在多个线程共享同一个事务上下文的情况,导致事务不生效。Spring事务管理器通过使用线程本地变量(ThreadLocal)来实现线程安全。)

举例最后一个的解决办法:
在这里插入图片描述

  • 第一种解决办法,新建一个类一个方法,在a中注入新类,再通过新类调用事务注解的方法。
  • 第二种解决方法,就是在该类中注入自己,通过在a方法中,通过自己类的对象调用b的事务注解方法。

2、MySQL的优化

  • 做MySQL优化,我们要善用EXPLAIN查看SQL执行计划
  • SQL语句中IN包含的值不应过多
  • SELECT语句务必指明字段名称
  • 当只需要一条数据的时候,使用limit 1
  • 如果限制条件中其他字段没有索引,尽量少用or
  • 尽量用union all代替union(使用union all前提是两张表没有重复数据)
  • 使用合理的分页方式以提高分页的效率(数据量太大,分页会越来越慢,建议使用id为条件来进行分页处理更快)
  • 分段查询(数据达到百万级,可以使用分段查询,循环展示数据,要的时候再加载数据)
  • 避免在where子句中对字段进行null值判断
  • 不建议使用%前缀模糊查询
  • 避免在where子句中对字段进行表达式操作
  • 对于联合索引来说,要遵守最左前缀法则(举列来说索引含有字段id、name、school,可以直接用id字段,也可以id、name这样的顺序,但是name;school都无法使用这个索引。所以在创建联合索引的时候一定要注意索引字段顺序,常用的查询字段放在最前面。)
  • 注意范围查询语句(对于联合索引来说,如果存在范围查询,比如between、>、<等条件时,会造成后面的索引字段失效。)
  • JOIN优化尽量使用inner join(等值关联查询),尽量使用小表来驱动大表。

19种优化建议

3、除了使用try…catch来抛异常还有其他的方法吗?

用 Assert(断言) 替换 throw exception

想必 Assert(断言) 大家都很熟悉,比如 Spring 家族的 org.springframework.util.Assert,在我们写测试用例的时候经常会用到,使用断言能让我们编码的时候有一种非一般丝滑的感觉,比如:

   @Test
    public void test1() {
        ...
        User user = userDao.selectById(userId);
        Assert.notNull(user, "用户不存在.");
        ...
    }

    @Test
    public void test2() {
        // 另一种写法
        User user = userDao.selectById(userId);
        if (user == null) {
            throw new IllegalArgumentException("用户不存在.");
        }
    }

也非常推荐大家使用断言来抛异常,会让代码更加的优雅。

4、Feign 第一次调用为什么会很慢?

首先要了解Feign是如何进行远程调用的,这里面包括,注册中心、负载均衡、FeignClient之间的关系,微服务通过不论是eureka、nacos也好注册到服务端,Feign是靠Ribbon做负载的,而Ribbon需要拿到注册中心的服务列表,将服务进行负载缓存到本地,然后FeignClient客户端在进行调用,大概就是这么一个过程。
在这里插入图片描述

我们知道ribbon是靠LoadBalancer做负载的 无非就是ILoadBalancer接口的方法,依次是添加新的服务、在负载均衡里选择一个服务、markServerDown服务下线、获取服务列表、获取存活的服务器、获取所有服务器(包括健康和不健康的)。

解决方法:

  • 开启Ribbon饥饿加载;在项目启动的时候,可以从日志看到,已经把Lxlxxx-system2服务进行加载,从而避免了第一次请求超时的情况;
  • 其实这种饥饿加载模式,类似于“客户端负载预热”的一个操作,项目启动的时候进行加载,防止服务之间调用可以因为数据量、业务逻辑处理复杂性导致接口超时,如果你的服务之间调用业务处理比较复杂、且慢,不妨可以试试这种解决方式。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

bst@微胖子

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

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

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

打赏作者

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

抵扣说明:

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

余额充值