java面经
面经
第一次面试
笔试
- 对List排序(倒叙)?
- redis缓存中需要注意什么 ?
- 请设计一个用户管理和权限管理模块,要数据库表的设计,登录功能和权限校验功能的实现,中间会遇到什么问题 ?
- 单表查询,联表查询等SQL语句。
面试
1. 你在项目中开发了文件上传模块,解决了大文件的传输问题,是如何解决的
前端部分
1.1 切文件(前端)
1.2 判定切片是否完成上传完成(前端)
- 客户端记录切片的上传状态,只需要上传未成功的切片
1.3 断点、错误续传(前端)
- 客户端上传文件时,记录已上传的切片位置
- 下次上传时,根据记录的位置,继续上传
后端部分
1.1 收切片、存切片
- 将相关切片保存在目标文件夹
1.2 合并切片
- 服务端根据切片的顺序,将切片合并成完整文件
1.3 文件是否存在校验
- 服务端根据文件md5值、文件名,校验该文件是否已经上传(解决重复提交问题)。
2. 百万级数据导入导出场景问题、大数据处理策略及优化方案?
场景问题分析解决
百万级或者千万级数据量导入导出的场景面临的一些问题,拆开解决:
导入问题解决方案
内存溢出(分批导入)
问题:传统的Apache POI在读取Excel文件时会创建大量的Java对象
来表示文件中的每一个单元格和行,当数据量超级大,使用传统的POI方式来完成导入会内存溢出
,并且效率会非常低;
解决:EasyExcel通过流式读取和写入数据
,只在内存中处理当前的数据块,避免了一次性加载整个文件,从而有效降低了内存消耗。分批读取读取Excel中的百万级的数据,这一点EasyExcel只需要把它分批的参数3000调大即可。我是用的20w;
EasyExcel底层采用了什么技术解决的这个问题:
- 基于流的API:EasyExcel使用了
流式API
来处理Excel文件。它使用了InputStream和OutputStream来逐步读取和写入数据,而不是一次性将整个文件加载到内存中。这样可以处理数据块,逐步读取和写入文件。- 事件驱动模型:EasyExcel采用了
事件驱动
的方式,特别是在读取时,库会触发事件(如行读取事件),开发者可以在这些事件发生时处理数据。这样,只有当前正在处理的行会被加载到内存中,其他数据仍然保留在文件中。- 按需加载:在读取过程中,EasyExcel只会加载当前需要处理的数据,而不是整个文件。它会在读取数据时
动态地从磁盘加载数据块
,然后处理完这些数据块后将其从内存中清除。- 低级别的文件操作:EasyExcel使用了底层的文件操作技术,如
BufferedInputStream
,来高效地读取文件内容,减少内存占用和提高读取速度。
DB插入(分批插入)
问题:其次就是往DB里插入,怎么去插入这20w条数据,当然不能一条一条的循环,应该批量插入这20w条数据,同样也不能使用Mybatis的批量插入,因为效率也低。
解决:使用JDBC+事务的批量操作将数据插入到数据库。(分批读取+JDBC分批插入+手动事务控制)
1 | // EasyExcel的读取Excel数据的API |
导出问题解决方案
问题:如果一次性查询数据库百万条数据会很慢
解决:
- 首先在查询数据库层面,需要分批进行查询(我使用的是每次查询20w)
- 每查询一次结束,就使用EasyExcel工具将这些数据写入一次
- 当一个Sheet写满了100w条数据,开始将查询的数据写入到另一个Sheet中
- 如此循环直到数据全部导出到Excel完毕
3. 如何设计一个权限管理模块(这里用的是RBAC模式)
在RBAC 模型中,通过将权限分配给角色,当该角色被指定给用户时,用户就拥有了该角色所分配的权限。每个角色至少具备一个权限,每个用户至少扮演一个角色。
用户
n:<——>:n:角色
n:<——>n:权限
只要对角色设置权限,属于该角色的人员就自动拥有这些权限。
如果某一权限范围太大了,想修改怎么弄:通过删除该角色的权限缩小范围
RBAC1:角色继承的RBAC模型
系统中存在的角色太多了,因为只要有权限不一样的用户加入系统,就需要新建一个角色,当用户权限分得很细的时候,甚至比ACL还繁琐
他发现角色之间存在着类似组织架构一样的上下级关系,比如COO>运营经理>运营主管>运营组长>运营人员>运营实习生,并且上级拥有下级的所有权限,同时可以额外拥有其他权限,于是他在现有的角色基础上又抽象出一层角色等级。
这种在角色中引入上下级关系的RBAC模型就叫做角色继承的RBAC模型(RBAC1),通过给角色分级,高级别的角色可继承低级别角色的权限,一定程度上简化了权限管理工作。
RBAC2:角色限制的RBAC模型
这种针对角色进行限制的模型就叫做角色限制的RBAC模型(RBAC2),可以把限制具体地分为静态职责分离(SSD)和动态职责分离(DSD):
- SSD:
- 互斥角色:同一用户只能分配到一组互斥角色集合中至多一个角色,比如用户不能同时拥有会计和审计两个角色
- 基数约束:一个用户可拥有的角色数目受限;一个角色可被分配的用户数量受限;一个角色对应的权限数目受限
- 先决条件角色:用户想要成为上级角色,必须先成为下一级角色,比如游戏中的转职
- DSD:允许一个用户具有多个角色,但在运行时只能激活其中某些角色,比如BOSS直聘,一个用户既可以是招聘者也可以是应聘者,但同时只能选择一种身份进行操作
RBAC3:统一的RBAC模型
RBAC3=RBAC1+RBAC2,既引入了角色间的继承关系,又引入了角色限制关系。
完善的RBAC模型
现有的权限管理模型虽然已经对“角色”进行了层级优化,但并没有优化“用户”方,这就意味着每入职一个新员工,小王得单独为其设置权限,还是很麻烦,于是他利用抽象的思想将相同属性的用户进行归类。在公司里,最简单的相同属性就是“部门”了,如果给部门赋予了角色和权限,那么这个部门中的所有用户都有了部门权限,而不需要为每一个用户再单独指定角色。
用户可以分组,权限同样也可以分组。在权限特别多的情况下,可以把同一层级的权限合并为一个权限组,如二级菜单下面有十几个按钮,如果对按钮的操作没有角色限制,可以把这些按钮权限与二级菜单权限合并为一个权限组,然后再把权限组赋予角色就可以了。
“组”概念的引入完善了RBAC模型,在简化操作的同时更贴近了实际业务,便于理解。
4. 什么是笛卡尔积,及其在SQL中的应用?
在关系型数据库中,笛卡尔积用于生成两个或多个表之间的所有可能组合。
假设我们有两个表:students
和 courses
他们各有两个字段
使用以下 SQL 查询来生成 students
和 courses
表之间的笛卡尔积:
1 | select * from stu cross join cou; |
每个学生与每门课程的所有可能组合都被列出了
student_id | student_name | course_id | course_name |
---|---|---|---|
1 | Alice | 101 | Math |
1 | Alice | 102 | Science |
2 | Bob | 101 | Math |
2 | Bob | 102 | Science |
我们可以生成这三个表之间的笛卡尔积,查询如下:
1 | SELECT * |
结果集
employee_id | employee_name | department_id | department_name | project_id | project_name |
---|---|---|---|---|---|
1 | John | 10 | HR | 1001 | Alpha |
1 | John | 10 | HR | 1002 | Beta |
1 | John | 20 | IT | 1001 | Alpha |
1 | John | 20 | IT | 1002 | Beta |
2 | Jane | 10 | HR | 1001 | Alpha |
2 | Jane | 10 | HR | 1002 | Beta |
2 | Jane | 20 | IT | 1001 | Alpha |
2 | Jane | 20 | IT | 1002 | Beta |
这生成了每个员工、每个部门和每个项目的所有可能组合。
使用 WHERE子句限制结果集
虽然笛卡尔积会生成所有可能的组合,但在实际查询中,我们常常需要限制结果集。通过结合 WHERE
子句,我们可以筛选掉不需要的结果。
例如,我们只对 IT
部门的员工和项目感兴趣。可以使用如下查询:
1 | SELECT * |
结果集
employee_id | employee_name | department_id | department_name | project_id | project_name |
---|---|---|---|---|---|
1 | John | 20 | IT | 1001 | Alpha |
1 | John | 20 | IT | 1002 | Beta |
2 | Jane | 20 | IT | 1001 | Alpha |
2 | Jane | 20 | IT | 1002 | Beta |
这样,我们只保留了 IT 部门的所有组合。
笛卡尔积的性能考虑
笛卡尔积虽然功能强大,但在处理大数据集时可能会导致性能问题。生成的结果集的大小是输入表行数的乘积,因此在数据量大的情况下,结果集的大小会迅速增长,从而对数据库性能产生重大影响。这个时候可以加限制条件。
MySQL怎么连表查询
内连接
1
select a.name,b.name form a inner join b on a.id=b.id
左外连接
1
select a.name,b.name form a left join b on a.id=b.id
右外连接
1 | select a.name,b.name form a right join b on a.id=b.id |
- 全外连接
1 | select a.name,b.name form a left join b on a.id=b.id |
5. 使用联表查询比较慢如何进行优化?
使用正确的连接类型:根据查询需求,选择合适的连接类型,如 INNER JOIN、LEFT JOIN 或 RIGHT JOIN。INNER JOIN 通常性能较好,因为只返回匹配的记录。
使用连接操作时,应该尽量使用 INNER JOIN,因为它通常性能最好。
优化 WHERE 子句:减少查询结果集的大小,可以有效提高关联查询的性能。在 WHERE 子句中添加筛选条件,以尽可能地缩小结果集。
为关联字段创建索引:为关联操作中用到的字段创建索引,可以大大提高查询性能。确保在所有参与连接操作的表中的关联字段上都创建了合适的索引。
减少查询的字段:尽量只查询需要的字段,而不是使用
SELECT *
。返回更少的数据可以减轻数据库和网络负担,从而提高性能。考虑使用分布式查询:对于非常大的数据集,可以考虑将数据分布在多个服务器上,然后使用分布式查询(如 MySQL Cluster 或分片技术)来提高关联查询的性能。
尽量避免子查询:子查询可能导致性能下降。尽可能使用连接操作替换子查询,因为 MySQL 在执行连接操作时通常性能更好。
利用 EXPLAIN 分析查询:使用 EXPLAIN 命令分析查询计划,找出性能瓶颈,然后针对性地进行优化。EXPLAIN 可以帮助你识别需要添加索引的字段、连接顺序等问题。
分解复杂查询:将复杂的多表关联查询分解成多个简单查询,可以降低查询复杂度,提高性能。通过将查询结果保存到临时表或内存表,然后再执行其他查询操作,可以有效地降低查询的复杂度。
总结
:联表查询较慢可以从以下方面解决使用内连接
,优化where语句
,为查询字段创建索引
,减少查询字段(不要用select*)
,采用分布式
,避免子查询
等。