记:找工作的一周(java面试之旅)

前言

吐槽(这里可直接跳过):五天九面,面的都麻木了,虽然感觉面试挺累,所幸收获满满,也清楚知道了自身欠缺的一些问题。下面呢是关于一些面试中自己遇到的一些面试问题,略做小记,分享给大家。希望大家都能成功的面上自己心仪的公司,頑張って!

一、数据库

1.你所知道的SQL优化?

1、查询语句中不要使用 *
2、尽量减少子查询,使用关联查询(left join,right join,inner join)替代
3、减少使用IN或者NOT IN ,使用exists,not exists或者关联查询语句替代
4、or 的查询尽量用 union或者union all 代替(在确认没有重复数据或者不用剔除重复数据时,union all会更好)
5、合理的增加冗余的字段(减少表的联接查询)
6、增加中间表进行优化(这个主要是在统计报表的场景,后台开定时任务将数据先统计好,尽量不要在查询的时候去统计)
7、建表的时候能使用数字类型的字段就使用数字类型(type,status…),数字类型的字段作为条件查询比字符串的快
8、那些可以过滤掉最大数量记录的条件必须写在WHERE子句的最末尾

2.mysql与oracle区别

  • 连接字符串在Oracle中用|| ,SqlServer中用+,MySQL中用concat(‘a’,‘b’,‘c’)
  • mysql的group by 语句可以select 没有被分组的字段,如select id,name,age from A group by age 这样但是在orcale和sqlserver中是会报错的。
  • oracle很早就完全支持事务。mysql在innodb存储引擎的行级锁的情况下才支持事务。
  • oracle默认不自动提交,需要用户手动提交。mysql默认是自动提交。
  • mysql分页用limit,oracle分页用rownum

3.MySQL如何控制高并发下安全修改同一条数据

1.加锁,
加锁demo

2.进行手动commit

二、事务

1.什么是事务

是数据库管理系统执行过程中的一个逻辑单位,由一个有限的数据库操作序列构成。对数据库的操作要么统一执行成功,要么统一执行失败

2.spring中的事务传播

spring中的事务传播

3.@Transactional源码的理解

Spring中@Transactional的使用及事务源码分析

三、redis

1.五大数据类型

  • string(字符串)
  • hash(哈希)
  • list(列表)
  • set(集合)
  • zset(sorted set:有序集合)

2.redis是使用什么策略清理内存

1.过期删除策略

  • 定时删除
  • 惰性删除
  • 定期删除

2.内存淘汰策略

  • 设置Redis最大内存
  • 设置内存淘汰方式

Redis过期删除策略是采用惰性删除和定期删除这两种方式组合进行的,惰性删除能够保证过期的数据我们在获取时一定获取不到,而定期删除设置合适的频率,则可以保证无效的数据及时得到释放,而不会一直占用内存数据。

3.突然停电,redis中缓存数据是否会丢失,有没有什么处理

不会丢失,通常来说,当机器关闭的时候,内存中的数据都会清空,都不会存储,那么下次开机的时候,不会出现之前的数据。那么为了防止这种情况突然发生,我们通常都会对缓存做持久化操作。
Redis提供了2中不同形式的持久化类型:

1.rdb(redis database)
原理:将redis在内存的数据库中的数据定时dump到硬盘上,实现rdb持久化。
优缺点:
rdb是比较磁盘空间的,他只是保持更新整个redis缓存中所有的数据的一个完整性。
相当于在一定时间间隔内对redis缓存数据的一个持久化,
采取一种临时文件替换上一层保存的文件,比较节省空间的。
每次fork redis中全数据的时候,会因数据量的增加而增加性能的消耗。
周期进行备份,但是如果突然停止,会丢失最后一次快照的修改。临时文件会丢失。

2.aof(append of file)
原理:将redis的操作日志以追加的形式写入到文件中。
优缺点:
备份机制更加成熟稳健,对每一条操作都会有所记录,那么就会大大的降低丢失率。
这是个日志形式的备份,那么我们就可以直接读持久化文件。可以对其人为的操作。
粒度细致,并且只会增加数据,不会修改数据,那么这个时候会占用更多的磁盘空间。
那么粒度细,也会造成一定的性能压力,每次变更都会新增日志记录。
官方也做出了一些声明:存在个别bug,会造成不能恢复现象。

四、AOP(面向切面编程)

1.AOP是什么,怎么实现的

  • AOP面向切面编程,是对业务流程中某一类共性功能的横向抽取。比如事务处理,消息通知。
  • Spring AOP中的动态代理主要有两种方式,JDK动态代理和CGLIB动态代理。JDK动态代理通过反射来接收被代理的类,并且 要求被代理的类必须实现一个接口。JDK动态代理的核心是InvocationHandler接口和Proxy类。
  • 如果目标类没有实现接口,那么Spring AOP会选择使用CGLIB来动态代理目标类。CGLIB(Code Generation Library),是一个代码生成的类库,可以在运行时动态的生成某个类的子类,注意,CGLIB是通过继承的方式做的动态代理,因此如果某个类被标记为final,那么它是无法使用CGLIB做动态代理的。

2.举例项目上具体aop的使用

Spring AOP实现日志管理

  • 前置通知(可以作为切点,记录用户操作的开始时间等)
  • 后置通知(可以作为切点,记录在方法执行之后返回的数据等)
  /**
     * Spring AOP实现日志管理
     * 前置通知 (在方法执行之前返回)用于拦截Controller层记录用户的操作的开始时间
     * @param joinPoint 切点
     * @throws InterruptedException
     */
    @Before("controllerAspect()")
    public void doBefore(JoinPoint joinPoint) {

        // 线程绑定变量(该数据只有当前请求的线程可见)
        Date beginTime = new Date();
        THREAD_LOCAL_BEGIN_TIME.set(beginTime);
    }


    /**
     * Spring AOP实现日志管理
     * 后置通知(在方法执行之后并返回数据) 用于拦截Controller层无异常的操作
     * @param joinPoint 切点
     */
    @AfterReturning("controllerAspect()")
    public void after(JoinPoint joinPoint) {

        try {
            String username = "";
            String description = getControllerMethodInfo(joinPoint).get("description").toString();
            int type = (int) getControllerMethodInfo(joinPoint).get("type");
            Map<String, String[]> logParams = request.getParameterMap();
            Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
            if (authentication == null || !authentication.isAuthenticated() || authentication.getName() == null
                    || (authentication instanceof AnonymousAuthenticationToken)) {
                return;
            }
            username = authentication.getName();

            String device = "", isMobile = "PC端";
            UserAgent ua = UserAgentUtil.parse(request.getHeader("user-agent"));
            if (ua != null) {
                if (ua.isMobile()) {
                    isMobile = "移动端";
                }
                device = ua.getBrowser().toString() + " " + ua.getVersion() + " | " + ua.getPlatform().toString()
                        + " " + ua.getOs().toString() + " | " + isMobile;
            }

            if (esRecord) {
                EsLog esLog = new EsLog();

                // 请求用户
                esLog.setUsername(username);
                // 日志标题
                esLog.setName(description);
                // 日志类型
                esLog.setLogType(type);
                // 日志请求url
                esLog.setRequestUrl(request.getRequestURI());
                // 请求方式
                esLog.setRequestType(request.getMethod());
                // 请求参数
                esLog.setMapToParams(logParams);
                ipInfoUtil.getInfo(request, ObjectUtil.mapToStringAll(request.getParameterMap()));
                // 请求IP
                esLog.setIp(ipInfoUtil.getIpAddr(request));
                // IP地址
                esLog.setIpInfo(ipInfoUtil.getIpCity(request));
                // 设备信息
                esLog.setDevice(device);
                // 请求开始时间
                long beginTime = THREAD_LOCAL_BEGIN_TIME.get().getTime();
                long endTime = System.currentTimeMillis();
                // 请求耗时
                Long logElapsedTime = endTime - beginTime;
                esLog.setCostTime(logElapsedTime.intValue());

                // 调用线程保存至ES
                ThreadPoolUtil.getPool().execute(new SaveEsSystemLogThread(esLog, esLogService));
            } else {
                Log log = new Log();

                // 请求用户
                log.setUsername(username);
                // 日志标题
                log.setName(description);
                // 日志类型
                log.setLogType(type);
                // 日志请求url
                log.setRequestUrl(request.getRequestURI());
                // 请求方式
                log.setRequestType(request.getMethod());
                // 请求参数
                log.setMapToParams(logParams);
                ipInfoUtil.getInfo(request, ObjectUtil.mapToStringAll(request.getParameterMap()));
                // 请求IP
                log.setIp(ipInfoUtil.getIpAddr(request));
                // IP地址
                log.setIpInfo(ipInfoUtil.getIpCity(request));
                // 设备信息
                log.setDevice(device);
                // 请求开始时间
                long beginTime = THREAD_LOCAL_BEGIN_TIME.get().getTime();
                long endTime = System.currentTimeMillis();
                // 请求耗时
                Long logElapsedTime = endTime - beginTime;
                log.setCostTime(logElapsedTime.intValue());

                // 调用线程保存至ES
                ThreadPoolUtil.getPool().execute(new SaveSystemLogThread(log, logService));
            }
            THREAD_LOCAL_BEGIN_TIME.remove();
        } catch (Exception e) {
            log.error("AOP后置通知异常", e);
        }
    }

总结个人感受:

简单说说这次找工作给自己最深的体验:主要是心态,五天九面,前面五次全挂,音讯全无,确实挺受打击的。我爱面试,面也没用,没用也面。中毒惹~哈哈哈哈! 最后,祝大家都能找到心仪的工作!

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值