ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

【面试】我以为面试官要问我八股文,可惜大意了没有闪之场景题

2021-07-18 14:01:36  阅读:150  来源: 互联网

标签:面试官 八股文 登录 布隆 面试 过滤器 hash 数据


前言:送给面试的小伙伴,也送给我自己。很多小伙伴可能会被一次面试打击后,就有了害怕的想法,不敢去面试,馄饨也有这样的情况,这还是在今年春招的时候,虽然自己在学校做过几个项目,但是对于理论方面或者知识点,我基本上是空白,这就让我有了一种畏惧心理,我到底该不该面试,我面试的话什么都不知道啊,怀着这种心态去复习,我会发现,我虽然复习了,但是我并不知道自己在真正面试时会多少。
在这里给小伙伴们两条建议:

  1. 不要畏惧面试,要敢于面试并去总结经验
  2. 不只是硬背,要加入自己的理解

在这里插入图片描述

文章目录


馄饨怀着激动的心打开了腾讯会议,在昨天又是把HashMap看了一遍,感觉又充满了动力。

面试官:先自我介绍一下吧!

我:面试官您好,我叫馄饨…………

用mybatis动态建表

面试官:看你的简历,你在学校也做过项目吧,那我考考你,用过mybatis吗?

我:用过。

面试官:那你给我说说怎么用mybatis动态建表

我:恩?用mybatis建表?没用过啊,不都是从数据库建表

面试官此时表情已经有点波动了,怀疑你是否真的用过mybatis

在有些应用场景中,我们会有需要动态创建和操作表的需求。比如因为单表数据存储量太大而采取分表存储的情况,又或者是按日期生成日志表存储系统日志等等。这个时候就需要我们动态的生成和操作数据库表了。而我们都知道,以往我们使用MyBatis是需要提前生成包括Model,Mapper和XML映射文件的,显然因为动态生成和操作表的需求一开始表都是不存在的,所以也就不能直接通过MyBatis连接数据库来生成我们的数据访问层代码并用来访问数据库了。

比如我们在打印日志的过程,按照时间定期在数据库中创建日志表。
创建过程,可以使用动态SQL,先查询表是否存在,然后创建表,加入数据
验证表是否存在

<select id="existTable" parameterType="String" resultType="Integer">  
        select count(*)  
        from information_schema.TABLES  
        where LCASE(table_name)=#{tableName} 
    </select>

删除表

<update id="dropTable">  
        DROP TABLE IF EXISTS ${tableName} 
    </update>  

创建表

 <update id="createNewTable" parameterType="String">  
        CREATE TABLE ${tableName} (
          id bigint(20) NOT NULL AUTO_INCREMENT,
          entityId bigint(20) NOT NULL,
          dx double NOT NULL,
          dy double NOT NULL,
          dz double NOT NULL,
          ntype varchar(32) NOT NULL,
          gnssTime bigint(20) NOT NULL,
          speed float DEFAULT NULL,
          direction float DEFAULT NULL,
          attributes varchar(255) DEFAULT NULL,
          PRIMARY KEY (id)) 
    </update> 

添加数据

<insert id="insert" parameterType="xx.xxx.xx.po.Trackpoint">
        insert into ${tableName}
        (entityId,dx,dy,dz,ntype,gnssTime,speed,direction,attributes)
        values
        (#{trackpoint.entityid},
        #{trackpoint.dx},
        #{trackpoint.dy},
        #{trackpoint.dz},
        #{trackpoint.ntype},
        #{trackpoint.gnsstime},
        #{trackpoint.speed},
        #{trackpoint.direction},
        #{trackpoint.attributes})
    </insert>

在mapper层配置

package xx.xxx.xx.mapper;

import org.apache.ibatis.annotations.Param;
import xx.xxx.xx.po.Trackpoint;

public interface OperateTableMapper {
    
    int existTable(String tableName);
    
    int dropTable(@Param("tableName")String tableName);
    
    int createNewTable(@Param("tableName")String tableName);
    
    int insert(@Param("tableName")String tableName,@Param("trackpoint")Trackpoint trackpoint);
}

以上参考于https://www.cnblogs.com/Joanna-Yan/p/9187538.html

怎样向有大量数据的表中添加索引

面试官:恩,看来你还是用过的,那你知道索引吧,怎样向有大量数据的表中添加索引,比如现在给你1000万数据量的表添加一个字段,如果直接添加字段那么很可能造成mysql崩溃,这种情况你们是怎么解决的?

我:恩?难道不是都考索引的知识点?我们项目最多数据就几十万条,这上来这么多数据,阿巴阿巴……我硬着头皮想了想,然后给出了自己的思路。

首先,我们肯定不能在原有的基础上去改表,那么有可能一个失误,使得整个数据都崩溃了。

  1. 那么我可以先创建一个测试表,然后在测试表,将原有的数据结构复制
  2. 在测试表中创建索引,然后将原表的数据复制到这个表中
  3. 修改原表名,将原表名赋值给测试表,测试表名赋值为原表名
  4. 将目标表删除

面试官:恩恩,看来你还是有些自己的想法,但是如果是线上,你数据在一直添加,你怎么保证数据一致性呢。

我:额,让我想一想……那可不可以在半夜的时候添加,这时候在网页上提示服务器正在维护,这时候就可以保证数据不会被修改了。

面试官:恩恩,这是一种方法,但这种弄是不是导致流量下降了,虽然晚上访问量少,但也不是没有。

我:确实是,我挠了挠头,想了几分钟,可以用事物吗,用日志记录在修改索引之后的数据,然后在索引修改完,执行日志操作。

面试官:恩恩,看来你还是很有想法的。

怎样将大量数据插入到数据库中

面试官:既然你知道能在大量数据的表中加索引,那么我考考你,怎样将大量的数据插入到数据库中呢,就好比我现在有几千万条数据。

我:恩?我们项目也没有那么多数据啊。

面试官:那你现在想想,有什么解决措施。

我:恩,那让我想一想。

馄饨百思苦想,想到了下面几个方案,仅供大家参考,如果有好的建议也可以私聊我或者下方评论。

  1. 使用for循环,但是这样会导致mysql频繁的进行操作,导致mysql数据库崩溃
  2. 一条sql插入多条数据
  3. 用svg进行插入
  4. 存储过程
  5. 分库分表(针对于一个表中不建议存储超过500万条数据)
  6. 将sql文件里面的主键、索引等全部删除掉,引擎改为MyIsAm
  7. 在事物中进行插入处理,所有事物都在执行后才进行提交操作
  8. 数据有序插入,无序插入会增大维护索引的成本,如果每次插入记录都在索引的最后面,索引的定位效率很高,并且对索引进行调整更小
  9. 通过一条sql+事物+排序对插入数据进行优化

其中6、7、8、9是参考于博客https://www.cnblogs.com/csnjava/p/13627959.html

业务登录

业务登录实现方式

面试官:知道登录吧,那你给我说说你们项目登录的实现是怎么样的

我:好的,这个我还是知道的。我们登录的时候要进行认证,来保护系统的隐私数据与资源。那么常用的认证方式有用户名密码登录、二维码登录、手机短信登录、指纹验证等。

为了避免用户每次登录都要进行验证,我们将验证存放在会话中。会话就是系统为了保持当前用户的状态提供的一种机制,常见的有Session方式、基于token方式

Session方式登录

先来说Session登录流程,用户在验证成功后,会在服务器端生成用户的相关信息并保存在Session(当前会话)中,发给用户也就是客户端的session_id存放到cookie中,这样用户客户端请求时带上session_id就可以验证服务器是否存在session数据,以此来完成用户的合法校验,当用户退出系统或Session过期销毁时,客户端的session也会无效。
在这里插入图片描述
我们用的是HttpSession操作。
在这里插入图片描述

面试官:恩恩,不错,但是是不是有些问题啊,如果我Session放在服务器的缓存中过于多,占用资源严重啊

我:恩恩,所以我们采用基于token的登录方式

面试官:那你说下token是什么吧。

我:好的。
token 是一个令牌,令牌是一种能够控制站点占有媒体的特殊帧,以区别数据帧及其他控制帧。token其实说的更通俗点可以叫暗号,在一些数据传输之前,要先进行暗号的核对,不同的暗号被授权不同的数据操作。

可以拿CSRF(跨站请求伪造)进行比对,CSRF是攻击者利用浏览器对于用户的信任,也就是利用用户以前认证过的浏览器,来进行一些恶意操作。如果是session,那么攻击者可以从cookie中获取session_id,然后在另一个浏览器进行操作,使得信息泄露。

所以,针对这一点,我们可以用token。

面试官:恩恩,那你说下token的执行流程吧。

我:好的。

1.客户端使用用户名跟密码请求登录
2.服务端收到请求,去验证用户名与密码
3.验证成功后,服务端会签发一个 Token,再把这个 Token 发送给客户端
4.客户端收到 Token 以后可以把它存储起来,比如放在 Cookie 里或者 Local Storage 里
5.客户端每次向服务端请求资源的时候需要带着服务端签发的 Token
6.服务端收到请求,然后去验证客户端请求里面带着的 Token,如果验证成功,就向客户端返回请求的数据。

面试官:行。那你说下你们项目中登录做了哪些处理吧?

我:好的。我们项目用到了SpringSecurity。

面试官:哦?那你简单给我介绍下SpringSecurity吧。

我:好的。SpringSecurity是spring全家桶中的一个,主要功能是授权和验证。

SpringSecurity实现验证登录

SpringSecurity 所解决的问题就是安全访问控制,就是对进入系统的请求进行拦截,检验每个请求是否能够访问它所期望的资源。SpringSecurity对Web资源的保护是靠Filter实现的。
在这里插入图片描述
FilterChainProxy是一个代理,真正起作用的是FilterChainProxy中SecurityFilterChain所包含的各个Filter,同时 这些Filter作为Bean被Spring管理,它们是Spring Security核心,各有各的职责,但他们并不直接处理用户的认 证,也不直接处理用户的授权,而是把它们交给了认证管理器(AuthenticationManager)和决策管理器 (AccessDecisionManager)进行处理,下图是FilterChainProxy相关类的UML图示。
在这里插入图片描述

认证流程:

在这里插入图片描述

  1. 用户提交用户名、密码被SecurityFilterChain中的 UsernamePasswordAuthenticationFilter 过滤器获取到, 封装为请求Authentication,通常情况下是UsernamePasswordAuthenticationToken这个实现类。
  2. 然后过滤器将Authentication提交至认证管理器(AuthenticationManager)进行认证
  3. 认证成功后, AuthenticationManager 身份管理器返回一个被填充满了信息的(包括上面提到的权限信息, 身份信息,细节信息,但密码通常会被移除) Authentication 实例。
    4.SecurityContextHolder 安全上下文容器将第3步填充了信息的 Authentication,通过 SecurityContextHolder.getContext().setAuthentication(…)方法,设置到其中。 可以看出AuthenticationManager接口(认证管理器)是认证相关的核心接口,也是发起认证的出发点,它 的实现类为ProviderManager。而Spring Security支持多种认证方式,因此ProviderManager维护着一个List<AuthenticationProvider> 列表,存放多种认证方式,最终实际的认证工作是由 AuthenticationProvider完成的。咱们知道web表单的对应的AuthenticationProvider实现类为 DaoAuthenticationProvider,它的内部又维护着一个UserDetailsService负责UserDetails的获取。最终 AuthenticationProvider将UserDetails填充至Authentication。

授权流程

Spring Security可以通过 http.authorizeRequests()对web请求进行授权保护。Spring Security使用标准Filter建立了对web请求的拦截,最终实现对资源的授权访问。
在这里插入图片描述

  1. 拦截请求,已认证用户访问受保护的web资源将被SecurityFilterChain中的 FilterSecurityInterceptor 的子 类拦截。
  2. 获取资源访问策略,FilterSecurityInterceptor会从 SecurityMetadataSource 的子类 DefaultFilterInvocationSecurityMetadataSource 获取要访问当前资源所需要的权限 Collection 。 SecurityMetadataSource其实就是读取访问策略的抽象,而读取的内容,其实就是我们配置的访问规则, 读 取访问策略如:
http.authorizeRequests() 
.antMatchers("/r/r1").hasAuthority("p1") 
.antMatchers("/r/r2").hasAuthority("p2")
  1. 最后,FilterSecurityInterceptor会调用 AccessDecisionManager 进行授权决策,若决策通过,则允许访问资 源,否则将禁止访问。

怎么对用户的登录做记录

面试官:恩恩,看来你确实用过点,那如果当前用户登录了,我怎么知道用户是在什么时候登录呢?有没有记录方式。

我:有的。我们用Spring的AOP,在用户登录前调用AOP,进行用户登录信息的记录。

面试官:那什么是AOP呢。

真的是说什么来什么啊,还好我有些映像。

我:AOP是一个面向切面编程技术, 利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重性,同时提高了开发的效率。就好比我当前接口只需要关心我当前的业务逻辑,不用去考虑其它非当前接口的业务逻辑。

大家可以参考这篇博客,通过一个小故事来对aop的概念进行加深,大家读后肯定有不少收获。https://blog.csdn.net/q982151756/article/details/80513340

怎么避免在登录时用户给的是一个不存在的账号密码导致查询性能下降

面试官:恩恩,看来你对情景问题有自己的想法,那我现在登录,但我给的用户名和密码是不存在的,那么缓存里面肯定没有,会直接向数据库访问,但是如果我数据库中用户名很多,肯定会使查询效率下降,这你怎么解决?

我:恩,这个redis中的布隆过滤器可以解决吧。

面试官:哦?看来你对redis还有研究,那你说下布隆过滤器是什么吧。

我(暗想):还好布隆过滤器看过一些,除了布隆过滤器,还有一个加强版的叫布谷鸟过滤器。

布隆过滤器

这个我是看bilibili上IT老哥的视频的,听着相比较容易些,感兴趣的小伙伴也可以去。

什么是布隆过滤器

布隆过滤器呢,是一个很长的二进制数组,来进行判断这个数据在不在这个数组里。
其中有两个关键点:

  1. 布隆过滤器用到了多个Hash函数
  2. 只有二进制数组都为1,才证明这个数据存在

在这里插入图片描述

存入过程

将要存入的值经过多个hash函数,获取每一个hash值,将值存入到指定的二进制数组中去。

查询过程

比如查找一个值,通过hash函数算出值存在二进制数组的下标位置,如果这查询到的二进制数组中下标的位置都为1,那么才会证明有这个数值,否则只要有一个数值不为1,那么就不存在。

删除操作

布隆过滤器很难做删除操作。如果现在来一个“你好”数据,又来一个“hello”数据,在进行hash函数解析,有可能通过哈希函数解析出的二进制数组值是相同的,那么就会出现“hello”的数组下标和“你好”的数组下标相同,布隆过滤器就会不清楚到底是删除“你好”还是删除“hello”。如果我现在将下标指向“hello”的1 改为0,那么下标指向“你好”的值也是从1变为0,相当于我删除了两个值。

优缺点

优点

  1. 因为布隆过滤器是由二进制数组组成,所以占的空间会非常小。
  2. 插入和查询速度是十分快的。因为他是计算hash值,在由hash值映射到数组的下标,在由数组去寻找,所以其时间复杂度为O(n),其中n为hash函数的个数。如果hash函数只有一个,那么其时间复杂度为O(1)。
  3. 保密性。因为存储的只有 0 和 1 ,所以人们根本不知道 0 和 1 是干什么的。

缺点

  1. 很难做删除操作。
  2. 误判。当前数据不在集合中,但是布隆过滤器会错误的判断其存在集合中。因为会计算哈希值,有可能会判为相同的hash值。比如上面提到的“你好”和“hello”,如果我现在集合中只有“你好”,没有“hello”,那有可能我在查询“hello”时,误判断“hello”是存在的,因为它们两的hash对应的数组下标是相同的,那么就会出现误判。

    但是这个误判是解决不了的,只能减少误判的概率。谷歌的Guava工具类已经做好了这个事情。这个误判率影响最后的结果,误判率越小,误判的个数越少,但是如果误判率设置的很小,其计算时间也会越耗时,影响性能。
    那么其减少误判率的原理就是通过多个hash函数,不同的hash函数计算出的下标值是不同的,不同的hash函数有不同的算法,hash函数多了,那么算出的hash值也就多了,所对应的二进制数据呢也就越多。正是因为哈希函数多,导致产生的hash值也多,那么布隆过滤器占用的空间也就越多。

布隆过滤器黑白名单

布隆过滤器白名单

在这里插入图片描述

布隆过滤器黑名单

在这里插入图片描述
场景
在这里插入图片描述
在这里插入图片描述

结尾

到此,馄饨已经将这次面试主要考察的点都总结完了,从中也会发现,我们很多知识点,其实都是跟场景去结合的,从场景的问题,引发出需要的技术,而不是为了用而用,我们在学习的过程中,也应该去多想为什么要有这个东西,有了会有什么样的作用,如果不用呢等等。

ok,希望我和在求职的小伙伴一样,积累经验与知识,储备自己的知识量,找到自己心仪的offer!

标签:面试官,八股文,登录,布隆,面试,过滤器,hash,数据
来源: https://blog.csdn.net/Black_Customer/article/details/118876487

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有